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

Merge branch 'master' into patch-1

This commit is contained in:
ninja-spotted 2025-08-09 00:45:04 +01:00 committed by GitHub
commit 91afe643be
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
571 changed files with 61853 additions and 83954 deletions

View File

@ -1,7 +1,7 @@
root = true
[{src, translations}/*]
end_of_line = crlf
[*]
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4

View File

@ -1,11 +0,0 @@
artwork/*
build/*
electron/*
gulp/*
node_modules/*
res/*
res_built/*
res_raw/*
tmp_standalone_files/*
tools/*
translations/*

View File

@ -1,26 +0,0 @@
env:
browser: true
es6: true
extends:
- "eslint:recommended"
- "plugin:@typescript-eslint/eslint-recommended"
- "prettier"
globals:
Atomics: readonly
SharedArrayBuffer: readonly
parser: "@typescript-eslint/parser"
parserOptions:
ecmaVersion: 6
sourceType: "module"
ecmaFeatures:
- modules: true
plugins:
- "@typescript-eslint"
- "prettier"
rules:
prettier/prettier: error
no-undef: off
no-unused-vars: off
no-unreachable: off
no-prototype-builtins: off
linebreak-style: off

View File

@ -10,42 +10,6 @@ on:
- master
jobs:
setup:
name: CI
runs-on: ubuntu-latest
steps:
- name: Install Dependencies
run: |
sudo apt-get update
sudo apt-get install ffmpeg
- name: Setup Node
uses: actions/setup-node@v2-beta
with:
node-version: 16.x
- name: Checkout repo
uses: actions/checkout@v2
- name: Install Yarn Dependencies
run: |
yarn
cd gulp/
yarn
cd ..
- name: Lint
run: |
yarn lint
- name: TSLint
run: |
cd gulp
yarn gulp translations.fullBuild
yarn gulp localConfig.findOrCreate
cd ..
yarn tslint
yaml-lint:
name: yaml-lint
runs-on: ubuntu-latest

19
.gitignore vendored
View File

@ -2,8 +2,6 @@
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
@ -27,15 +25,9 @@ node_modules/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
@ -46,8 +38,6 @@ res_built
gulp/runnable-texturepacker.jar
tmp_standalone_files
tmp_standalone_files_china
tmp_standalone_files_wegame
# Local config
config.local.js
@ -56,9 +46,12 @@ config.local.js
# Editor artifacts
*.*.swp
*.*.swo
app.vdf
steamtmp
build_output
built_vdfs
tmp
src/js/built-temp
translations/tmp
gulp/additional_build_files
electron/dist

4
.gitpod.Dockerfile vendored
View File

@ -1,4 +0,0 @@
FROM gitpod/workspace-full
RUN sudo apt-get update \
&& sudo apt install ffmpeg -yq

View File

@ -1,10 +0,0 @@
image:
file: .gitpod.Dockerfile
tasks:
- init: yarn && gp sync-done boot
- before: cd gulp
init: gp sync-await boot && yarn
command: yarn gulp
ports:
- port: 3005
onOpen: open-preview

7
.prettierignore Normal file
View File

@ -0,0 +1,7 @@
*.gif
*.jpg
*.mp3
*.png
*.wav
*.webm
*.woff2

9
.prettierrc Normal file
View File

@ -0,0 +1,9 @@
{
"arrowParens": "avoid",
"bracketSpacing": true,
"printWidth": 110,
"quoteProps": "consistent",
"semi": true,
"singleQuote": false,
"trailingComma": "es5"
}

View File

@ -1,11 +0,0 @@
# .prettierrc or .prettierrc.yaml
trailingComma: "es5"
tabWidth: 4
semi: true
singleQuote: false
printWidth: 110
useTabs: false
quoteProps: "consistent"
bracketSpacing: true
arrowParens: avoid
endOfLine: auto

View File

@ -1,166 +0,0 @@
# validate config at https://config.travis-ci.com/explore
os: linux
dist: xenial
language: node_js
node_js:
- "12"
cache: yarn
# platform specific configuration
jobs:
# jobs which have to succeed
include:
# OS: MAC
## -> build darwin
# - name: "Standalone MacOS on MacOS"
# os: osx
# osx_image: xcode11.3
# before_install:
# - HOMEBREW_NO_AUTO_UPDATE=1 brew install git-lfs
# - HOMEBREW_NO_AUTO_UPDATE=1 brew install ffmpeg
# script:
# - cd gulp
# - yarn gulp build.standalone-prod || travis_terminate 1
# - yarn gulp standalone.prepare
# - yarn gulp standalone.package.prod.darwin64
# - cd ..
## -> build win
# - name: "Standalone Windows on MacOS"
# os: osx
# osx_image: xcode11.3
# before_install:
# - HOMEBREW_NO_AUTO_UPDATE=1 brew install git-lfs
# - HOMEBREW_NO_AUTO_UPDATE=1 brew install ffmpeg
# - HOMEBREW_NO_AUTO_UPDATE=1 brew cask install wine-stable
# # prevent Wine popup dialogs about installing additional packages
# - export WINEDLLOVERRIDES="mscoree,mshtml="
# - export WINEDEBUG="-all"
# script:
# - cd gulp
# - yarn gulp build.standalone-prod || travis_terminate 1
# - yarn gulp standalone.prepare
# - yarn gulp standalone.package.prod.win64
# - yarn gulp standalone.package.prod.win32
# - cd ..
## -> build linux
# - name: "Standalone Linux on MacOS"
# os: osx
# osx_image: xcode11.3
# before_install:
# - HOMEBREW_NO_AUTO_UPDATE=1 brew install git-lfs
# - HOMEBREW_NO_AUTO_UPDATE=1 brew install ffmpeg
# script:
# - cd gulp
# - yarn gulp build.standalone-prod || travis_terminate 1
# - yarn gulp standalone.prepare
# - yarn gulp standalone.package.prod.linux64
# - yarn gulp standalone.package.prod.linux32
# - cd ..
# OS: LINUX
## -> build darwin
## not possible
## -> build win
# - name: "Standalone Windows on Linux"
# os: linux
# addons:
# apt:
# packages:
# - libavformat-dev
# - libavfilter-dev
# - libavdevice-dev
# - ffmpeg
# - wine
# script:
# - cd gulp
# - yarn gulp build.standalone-prod || travis_terminate 1
# - yarn gulp standalone.prepare
# - yarn gulp standalone.package.prod.win64
# - yarn gulp standalone.package.prod.win32
# - cd ..
## -> build linux
# - name: "Standalone Linux on Linux"
# os: linux
# addons:
# apt:
# packages:
# - libavformat-dev
# - libavfilter-dev
# - libavdevice-dev
# - ffmpeg
# script:
# - cd gulp
# - yarn gulp build.standalone-prod || travis_terminate 1
# - yarn gulp standalone.prepare
# - yarn gulp standalone.package.prod.linux64
# - yarn gulp standalone.package.prod.linux32
# - cd ..
# OS: WINDOWS
## -> build darwin
## not possible
## -> build linux
# - name: "Standalone Linux on Windows"
# os: windows
# env: YARN_GPG=no
# before_install:
# - choco install git-lfs -y -f || echo "0" # choco fails but git-lfs is still installed
# - choco install ffmpeg --version=4.2.3
# - export PATH=/C/ProgramData/chocolatey/lib/ffmpeg/tools/ffmpeg/bin:$PATH
# - wget https://github.com/moiamond/docker-ffmpeg-base-windowsservercore/raw/master/System32/avicap32.dll -P /C/Windows/System32/
# - wget https://github.com/moiamond/docker-ffmpeg-base-windowsservercore/raw/master/System32/msvfw32.dll -P /C/Windows/System32/
# script:
# - cd gulp
# - yarn gulp build.standalone-prod || travis_terminate 1
# - yarn gulp standalone.prepare
# - yarn gulp standalone.package.prod.linux64
# - yarn gulp standalone.package.prod.linux32
# - cd ..
## -> build win
- name: "Standalone Windows on Windows"
os: windows
env: YARN_GPG=no
before_install:
- choco install git-lfs -y -f || echo "0" # choco fails but git-lfs is still installed
- choco install ffmpeg --version=4.2.3
- choco install wget
- export PATH=/C/ProgramData/chocolatey/lib/ffmpeg/tools/ffmpeg/bin:$PATH
- wget https://github.com/moiamond/docker-ffmpeg-base-windowsservercore/raw/master/System32/avicap32.dll -P /C/Windows/System32/
- wget https://github.com/moiamond/docker-ffmpeg-base-windowsservercore/raw/master/System32/msvfw32.dll -P /C/Windows/System32/
script:
- cd gulp
- yarn gulp build.standalone-prod || travis_terminate 1
- yarn gulp standalone.prepare
- yarn gulp standalone.package.prod.win64
- yarn gulp standalone.package.prod.win32
- cd ..
# mark build as finished even if "allow_failures" are still running
fast_finish: true
# optional jobs which may fail
#allow_failures:
# - name: ""
# shared
install:
- git lfs install
- git lfs pull
- yarn
# electron dependencies
- cd electron
- yarn
- cd ..
# gulp dependendencies
- cd gulp
- yarn
- cd ..

View File

@ -1,9 +1,3 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
// List of extensions which should be recommended for users of this workspace.
"recommendations": ["esbenp.prettier-vscode"],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": []
"recommendations": ["esbenp.prettier-vscode", "EditorConfig.EditorConfig", "dbaeumer.vscode-eslint"]
}

12
.vscode/settings.json vendored
View File

@ -1,5 +1,13 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"files.trimTrailingWhitespace": true,
"editor.formatOnSave": true
}
"editor.formatOnSave": true,
"files.exclude": {
"**/.git": true,
"**/.DS_Store": true,
"**/Thumbs.db": true,
"node_modules/": true,
"build/": true,
"build_output/": true
}
}

View File

@ -1,33 +0,0 @@
Contributor license agreement (CLA)
1. Preamble
Thank you for your interest in shapez by tobspr IT Solutions (the "Company"). In order to clarify the intellectual property license granted with Contributions from any person or entity, the Company must have a Contributor License Agreement ("CLA") on file that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of the Company and its users; it does not change your rights to use your own Contributions for any other purpose.
2. General
You accept and agree to the following terms and conditions for Your present and future Contributions submitted to the Company. In return, the Company shall not use Your Contributions in a way that is contrary to the public benefit or inconsistent with its bylaws in effect at the time of the Contribution. Except for the license granted herein to the Company and recipients of software distributed by the Company, You reserve all right, title, and interest in and to Your Contributions.
You represent that you have the full authority to enter into this agreement.
3. Definitions
"You" (or "Your") "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with the Company. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
"Contribution" "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to the Company for inclusion in, or documentation of, any of the products owned or managed by the Company (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Company or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Company for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution."
4. Grant of Copyright License
Subject to the terms and conditions of this Agreement, You hereby grant to the Company and to recipients of software distributed by the Company a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works.
You agree that your changes/additions are incorporated into the source code under a GPL-3 license.
You agree that the Company is free to use its code without a GPL-3 license as closed source in any context, including for commercial purposes, without any license whatsoever
5. Grant of Patent License
Subject to the terms and conditions of this Agreement, You hereby grant to the Company and to recipients of software distributed by the Company a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.
6. Liability / Obligations
You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to the Company, or that your employer has executed a separate Corporate CLA with the Company.
You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions.
If you make changes or additions to the code, you assume full liability for this and assure that the changes/additions do not infringe the rights of third parties (e.g. copyrights).
You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
Should You wish to submit work that is not Your original creation, You may submit it to the Company separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]".
You agree to notify the Company of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect.
7. Final provisions
The law of the Federal Republic of Germany applies to this agreement.
The contract remains binding in its remaining parts even if individual points are legally ineffective. In place of the ineffective points, the statutory provisions, if any, apply. Insofar as this would represent unreasonable hardship for one of the contracting parties, the contract as a whole will become ineffective.

View File

@ -1,5 +0,0 @@
dengr1065
- Contributed various fixes to the dark mode
thelockj
- Contributed the design and initial implementation for the storage building

View File

@ -7,7 +7,7 @@ WORKDIR /shapez.io
RUN apt-get update && apt-get install -y --no-install-recommends \
ffmpeg default-jre \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
&& rm -rf /var/lib/apt/lists/*
COPY package.json yarn.lock ./
RUN yarn
@ -20,7 +20,6 @@ WORKDIR /shapez.io
COPY res ./res
COPY src/html ./src/html
COPY src/css ./src/css
COPY version ./version
COPY sync-translations.js ./
COPY translations ./translations
COPY src/js ./src/js

135
README.md
View File

@ -1,95 +1,84 @@
## NEW: Shapez 2!
# shapez Community Edition <img src="./electron/favicon.png" alt="shapez Logo" align="right" height="40">
We are currently working on a successor to shapez, with 3D Graphics, Exploration, Layers, Mass transport, New Shape Mechanics, Research and a lot more! Be sure to check it out:
**shapez Community Edition** (abbreviated as **CE**) is a community-maintained version of [shapez](https://store.steampowered.com/app/1318690/shapez/)!
<a href="https://tobspr.io/shapez-2" title="shapez 2">
<img src="https://i.imgur.com/6T7UP3p.png" alt="shapez 2 Announcement">
</a>
CE was created as the tobspr Games team moved away from shapez to work full-time on the upcoming [Shapez 2](https://store.steampowered.com/app/2162800/shapez_2/).
CE aims to:
<br>
- Continue the development of shapez as guided by the community.
- Allow contributors to continue submitting new features and improvements to the game.
- Provide an experimental and forgiving environment for faster development.
# shapez
> [!IMPORTANT]
> CE is different from the official game published on Steam and other platforms.
> CE was forked off of the official shapez, which has [its own repository](https://github.com/tobspr-games/shapez.io).
> No plans exist to merge the two versions of shapez.
<a href="https://get.shapez.io/ghi" title="shapez on Steam">
<img src="https://i.imgur.com/ihW2bUE.png" alt="shapez Logo">
</a>
<hr>
This is the source code for shapez, an open source base building game inspired by Factorio.
Your goal is to produce shapes by cutting, rotating, merging and painting parts of shapes.
- [Play on Steam](https://get.shapez.io/ghr)
- [Online Demo](https://shapez.io)
- [Official Discord](https://discord.com/invite/HN7EVzV) <- _Highly recommended to join!_
- [Trello Board & Roadmap](https://trello.com/b/ISQncpJP/shapezio)
## Reporting issues, suggestions, feedback, bugs
1. Ask in `#bugs` / `#feedback` / `#questions` on the [Official Discord](https://discord.com/invite/HN7EVzV) if you are not entirely sure if it's a bug
2. Check out the trello board: https://trello.com/b/ISQncpJP/shapezio
3. See if it's already there - If so, vote for it, done. I will see it. (You have to be signed in on trello)
4. If not, check if it's already reported here: https://github.com/tobspr-games/shapez.io/issues
5. If not, file a new issue here: https://github.com/tobspr-games/shapez.io/issues/new
6. I will then have a look (This can take days or weeks) and convert it to trello, and comment with the link. You can then vote there ;)
## Building
- Make sure `ffmpeg` is on your path
- Install Node.js 16 and Yarn
- Install Java (required for texture packer)
- Run `yarn` in the root folder
- `cd` into `gulp` folder
- Run `yarn` and then `yarn gulp` - it should now open in your browser
**Notice**: This will produce a debug build with several debugging flags enabled. If you want to disable them, modify [`src/js/core/config.js`](src/js/core/config.js).
## Creating Mods
Mods can be found [here](https://shapez.mod.io). The documentation for creating mods can be found [here](mod_examples/), including a bunch of sample mods.
## Build Online with one-click setup
You can use [Gitpod](https://www.gitpod.io/) (an Online Open Source VS Code-like IDE which is free for Open Source) for working on issues and making PRs to this project. With a single click it will start a workspace and automatically:
- clone the `shapez.io` repo.
- install all of the dependencies.
- start `gulp` in `gulp/` directory.
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/tobspr-games/shapez.io)
## Helping translate
Please checkout the [Translations readme](translations/).
As of now, CE must be built from source and supports only a standalone build,
with no plans for re-supporting a web version.
In the future, builds of CE may provided for owners of the full version of shapez.
## Contributing
I will only accept pull requests which add a benefit to a large portion of the player base. If the feature is useful but only to a fraction of players, or is controversial, I recommend making a mod instead.
We communicate on the [official shapez Discord server](https://discord.com/invite/HN7EVzV).
For historical reasons, we have communicated in a private channel,
but we are moving to the public `#contributing` channel.
If you would like to contribute to CE, feel free to share your ideas, plans, etc. there.
If you want to add a new feature or in generally contribute I recommend to get in touch on Discord in advance, which largely increases the chance of the PR to get merged:
In our current workflow, we (the "collaborators" of the repository) create internal branches and corresponding pull requests to work on a feature, refactor, etc.
We discuss changes in the Discord, and when 2 collaborators (including the PR creator) approve of a change, it can be merged.
See our existing [pull requests](https://github.com/tobspr-games/shapez-community-edition/pulls?q=) for examples.
<a href="https://discord.com/invite/HN7EVzV" target="_blank">
<img src="https://i.imgur.com/SoawBhW.png" alt="discord logo" width="100">
</a>
If you are not a collaborator and want to submit a change,
you can fork our repo and make a pull request.
Note that because of plans to overhaul many parts of the game,
unless you are improving translations, you should probably communicate with us on Discord!
> [!TIP]
> Be aware that [pull requests to the official shapez repository](https://github.com/tobspr-games/shapez.io/pulls) are unlikely to get merged in the near future. Instead, submit them to CE!
> In fact, because the game is licensed under the [GNU GPL v3.0](https://www.gnu.org/licenses/gpl-3.0.html),
> existing pull requests can be resubmitted to CE even if you aren't the author! **This is not legal advice.**
### Code
The game is based on a custom engine which itself is based on the YORG.io 3 game engine (Actually it shares almost the same core).
The game uses a custom engine originally based on the YORG.io 3 game engine.
The code within the engine is relatively clean with some code for the actual game on top being hacky.
This project is based on ES5 (If I would develop it again, I would definitely use TypeScript). Some ES2015 features are used but most of them are too slow, especially when polyfilled. For example, `Array.prototype.forEach` is only used within non-critical loops since its slower than a plain for loop.
We are in the process of migrating to TypeScript and JSX/TSX.
New changes should be implemented in TypeScript if possible,
but because we are planning on overhauling many parts of the game,
there is no need to convert existing code to TypeScript.
### Assets
This project is fine with using cutting-edge and bleeding-edge features
and does not intend to provide compatibility for older clients.
You can find most assets <a href="//github.com/tobspr-games/shapez.io-artwork" target="_blank">here</a>.
## Building
All assets will be automatically rebuilt into the atlas once changed (Thanks to dengr1065!)
### Prerequisites
<img src="https://i.imgur.com/W25Fkl0.png" alt="shapez Screenshot">
- [Node.js](https://nodejs.org)
- [ffmpeg](https://www.ffmpeg.org/download.html) for audio transcoding
- [Java](https://www.oracle.com/java/technologies/downloads/) (or [OpenJDK](https://openjdk.org/)) to run the texture packer
<br>
### Development
## Check out our other games!
- Run `npm i` in the root folder and in `electron/`.
- Run `npm run gulp` in the root folder to build and serve files.
If a new browser tab opens, ignore it.
- Open a new terminal and run `npm start` in `electron/` to open an Electron window.
- Use `npm start -- --dev` to run in development mode.
- Tip: If you open the Electron window too early, you can reload it when focused on DevTools.
<a href="https://tobspr.io" title="tobspr Games">
<img src="https://i.imgur.com/uA2wcUy.png" alt="tobspr Games">
</a>
### Release
- Run `npm i` in the root folder and in `electron/`.
- In the root folder, run `npm run package-$PLATFORM-$ARCH` where:
- `$PLATFORM` is `win32`, `linux` or `darwin` depending on your system.
- `$ARCH` is the target system architecture (`x64` or `arm64`)
- The build will be found under `build_output/standalone` as `shapez-...`.
## Credits
Thanks to [tobspr](https://tobspr.io) for creating this project!
[<img src="https://i.imgur.com/uA2wcUy.png" alt="tobspr Games">](https://tobspr.io)

1
electron/.gitignore vendored
View File

@ -1 +0,0 @@
mods/*.js

View File

@ -1,13 +0,0 @@
{
"folders": [
{
"path": "."
}
],
"settings": {
"files.exclude": {
"**/node_modules": true,
"**/typedefs_gen": true
}
}
}

View File

@ -1,389 +0,0 @@
/* eslint-disable quotes,no-undef */
const { app, BrowserWindow, Menu, MenuItem, ipcMain, shell, dialog, session } = require("electron");
const path = require("path");
const url = require("url");
const fs = require("fs");
const steam = require("./steam");
const asyncLock = require("async-lock");
const windowStateKeeper = require("electron-window-state");
// Disable hardware key handling, i.e. being able to pause/resume the game music
// with hardware keys
app.commandLine.appendSwitch("disable-features", "HardwareMediaKeyHandling");
const isDev = app.commandLine.hasSwitch("dev");
const isLocal = app.commandLine.hasSwitch("local");
const safeMode = app.commandLine.hasSwitch("safe-mode");
const externalMod = app.commandLine.getSwitchValue("load-mod");
const roamingFolder =
process.env.APPDATA ||
(process.platform == "darwin"
? process.env.HOME + "/Library/Preferences"
: process.env.HOME + "/.local/share");
let storePath = path.join(roamingFolder, "shapez.io", "saves");
let modsPath = path.join(roamingFolder, "shapez.io", "mods");
if (!fs.existsSync(storePath)) {
// No try-catch by design
fs.mkdirSync(storePath, { recursive: true });
}
if (!fs.existsSync(modsPath)) {
fs.mkdirSync(modsPath, { recursive: true });
}
/** @type {BrowserWindow} */
let win = null;
let menu = null;
function createWindow() {
let faviconExtension = ".png";
if (process.platform === "win32") {
faviconExtension = ".ico";
}
const mainWindowState = windowStateKeeper({
defaultWidth: 1000,
defaultHeight: 800,
});
win = new BrowserWindow({
x: mainWindowState.x,
y: mainWindowState.y,
width: mainWindowState.width,
height: mainWindowState.height,
show: false,
backgroundColor: "#222428",
useContentSize: false,
minWidth: 800,
minHeight: 600,
title: "shapez",
transparent: false,
icon: path.join(__dirname, "favicon" + faviconExtension),
// fullscreen: true,
autoHideMenuBar: !isDev,
webPreferences: {
nodeIntegration: false,
nodeIntegrationInWorker: false,
nodeIntegrationInSubFrames: false,
contextIsolation: true,
enableRemoteModule: false,
disableBlinkFeatures: "Auxclick",
webSecurity: true,
sandbox: true,
preload: path.join(__dirname, "preload.js"),
experimentalFeatures: false,
},
allowRunningInsecureContent: false,
});
mainWindowState.manage(win);
if (isLocal) {
win.loadURL("http://localhost:3005");
} else {
win.loadURL(
url.format({
pathname: path.join(__dirname, "index.html"),
protocol: "file:",
slashes: true,
})
);
}
win.webContents.session.clearCache();
win.webContents.session.clearStorageData();
////// SECURITY
// Disable permission requests
win.webContents.session.setPermissionRequestHandler((webContents, permission, callback) => {
callback(false);
});
session.fromPartition("default").setPermissionRequestHandler((webContents, permission, callback) => {
callback(false);
});
app.on("web-contents-created", (event, contents) => {
// Disable vewbiew
contents.on("will-attach-webview", (event, webPreferences, params) => {
event.preventDefault();
});
// Disable navigation
contents.on("will-navigate", (event, navigationUrl) => {
event.preventDefault();
});
});
win.webContents.on("will-redirect", (contentsEvent, navigationUrl) => {
// Log and prevent the app from redirecting to a new page
console.error(
`The application tried to redirect to the following address: '${navigationUrl}'. This attempt was blocked.`
);
contentsEvent.preventDefault();
});
// Filter loading any module via remote;
// you shouldn't be using remote at all, though
// https://electronjs.org/docs/tutorial/security#16-filter-the-remote-module
app.on("remote-require", (event, webContents, moduleName) => {
event.preventDefault();
});
// built-ins are modules such as "app"
app.on("remote-get-builtin", (event, webContents, moduleName) => {
event.preventDefault();
});
app.on("remote-get-global", (event, webContents, globalName) => {
event.preventDefault();
});
app.on("remote-get-current-window", (event, webContents) => {
event.preventDefault();
});
app.on("remote-get-current-web-contents", (event, webContents) => {
event.preventDefault();
});
//// END SECURITY
win.webContents.on("new-window", (event, pth) => {
event.preventDefault();
if (pth.startsWith("https://") || pth.startsWith("steam://")) {
shell.openExternal(pth);
}
});
win.on("closed", () => {
console.log("Window closed");
win = null;
});
if (isDev) {
menu = new Menu();
win.webContents.toggleDevTools();
const mainItem = new MenuItem({
label: "Toggle Dev Tools",
click: () => win.webContents.toggleDevTools(),
accelerator: "F12",
});
menu.append(mainItem);
const reloadItem = new MenuItem({
label: "Reload",
click: () => win.reload(),
accelerator: "F5",
});
menu.append(reloadItem);
const fullscreenItem = new MenuItem({
label: "Fullscreen",
click: () => win.setFullScreen(!win.isFullScreen()),
accelerator: "F11",
});
menu.append(fullscreenItem);
const mainMenu = new Menu();
mainMenu.append(
new MenuItem({
label: "shapez.io",
submenu: menu,
})
);
Menu.setApplicationMenu(mainMenu);
} else {
Menu.setApplicationMenu(null);
}
win.once("ready-to-show", () => {
win.show();
win.focus();
});
}
if (!app.requestSingleInstanceLock()) {
app.exit(0);
} else {
app.on("second-instance", () => {
// Someone tried to run a second instance, we should focus
if (win) {
if (win.isMinimized()) {
win.restore();
}
win.focus();
}
});
}
app.on("ready", createWindow);
app.on("window-all-closed", () => {
console.log("All windows closed");
app.quit();
});
ipcMain.on("set-fullscreen", (event, flag) => {
win.setFullScreen(flag);
});
ipcMain.on("exit-app", () => {
win.close();
app.quit();
});
let renameCounter = 1;
const fileLock = new asyncLock({
timeout: 30000,
maxPending: 1000,
});
function niceFileName(filename) {
return filename.replace(storePath, "@");
}
async function writeFileSafe(filename, contents) {
++renameCounter;
const prefix = "[ " + renameCounter + ":" + niceFileName(filename) + " ] ";
const transactionId = String(new Date().getTime()) + "." + renameCounter;
if (fileLock.isBusy()) {
console.warn(prefix, "Concurrent write process on", filename);
}
fileLock.acquire(filename, async () => {
console.log(prefix, "Starting write on", niceFileName(filename), "in transaction", transactionId);
if (!fs.existsSync(filename)) {
// this one is easy
console.log(prefix, "Writing file instantly because it does not exist:", niceFileName(filename));
await fs.promises.writeFile(filename, contents, "utf8");
return;
}
// first, write a temporary file (.tmp-XXX)
const tempName = filename + ".tmp-" + transactionId;
console.log(prefix, "Writing temporary file", niceFileName(tempName));
await fs.promises.writeFile(tempName, contents, "utf8");
// now, rename the original file to (.backup-XXX)
const oldTemporaryName = filename + ".backup-" + transactionId;
console.log(
prefix,
"Renaming old file",
niceFileName(filename),
"to",
niceFileName(oldTemporaryName)
);
await fs.promises.rename(filename, oldTemporaryName);
// now, rename the temporary file (.tmp-XXX) to the target
console.log(
prefix,
"Renaming the temporary file",
niceFileName(tempName),
"to the original",
niceFileName(filename)
);
await fs.promises.rename(tempName, filename);
// we are done now, try to create a backup, but don't fail if the backup fails
try {
// check if there is an old backup file
const backupFileName = filename + ".backup";
if (fs.existsSync(backupFileName)) {
console.log(prefix, "Deleting old backup file", niceFileName(backupFileName));
// delete the old backup
await fs.promises.unlink(backupFileName);
}
// rename the old file to the new backup file
console.log(prefix, "Moving", niceFileName(oldTemporaryName), "to the backup file location");
await fs.promises.rename(oldTemporaryName, backupFileName);
} catch (ex) {
console.error(prefix, "Failed to switch backup files:", ex);
}
});
}
ipcMain.handle("fs-job", async (event, job) => {
const filenameSafe = job.filename.replace(/[^a-z\.\-_0-9]/gi, "_");
const fname = path.join(storePath, filenameSafe);
switch (job.type) {
case "read": {
if (!fs.existsSync(fname)) {
// Special FILE_NOT_FOUND error code
return { error: "file_not_found" };
}
return await fs.promises.readFile(fname, "utf8");
}
case "write": {
await writeFileSafe(fname, job.contents);
return job.contents;
}
case "delete": {
await fs.promises.unlink(fname);
return;
}
default:
throw new Error("Unknown fs job: " + job.type);
}
});
ipcMain.handle("open-mods-folder", async () => {
shell.openPath(modsPath);
});
console.log("Loading mods ...");
function loadMods() {
if (safeMode) {
console.log("Safe Mode enabled for mods, skipping mod search");
}
console.log("Loading mods from", modsPath);
let modFiles = safeMode
? []
: fs
.readdirSync(modsPath)
.filter(filename => filename.endsWith(".js"))
.map(filename => path.join(modsPath, filename));
if (externalMod) {
console.log("Adding external mod source:", externalMod);
const externalModPaths = externalMod.split(",");
modFiles = modFiles.concat(externalModPaths);
}
return modFiles.map(filename => fs.readFileSync(filename, "utf8"));
}
let mods = [];
try {
mods = loadMods();
console.log("Loaded", mods.length, "mods");
} catch (ex) {
console.error("Failed to load mods");
dialog.showErrorBox("Failed to load mods:", ex);
}
ipcMain.handle("get-mods", async () => {
return mods;
});
steam.init(isDev);
// Only allow achievements and puzzle DLC if no mods are loaded
if (mods.length === 0) {
steam.listen();
}

View File

@ -1,6 +0,0 @@
Here you can place mods. Every mod should be a single file ending with ".js".
--- WARNING ---
Mods can potentially access to your filesystem.
Please only install mods from trusted sources and developers.
--- WARNING ---

935
electron/package-lock.json generated Normal file
View File

@ -0,0 +1,935 @@
{
"name": "electron",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "electron",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"chokidar": "^4.0.3",
"semver": "^7.7.1",
"zod": "^3.24.2"
},
"devDependencies": {
"@types/semver": "^7.7.0",
"electron": "^37.2.3",
"typescript": "^5.8.2"
}
},
"node_modules/@electron/get": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz",
"integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"debug": "^4.1.1",
"env-paths": "^2.2.0",
"fs-extra": "^8.1.0",
"got": "^11.8.5",
"progress": "^2.0.3",
"semver": "^6.2.0",
"sumchecker": "^3.0.1"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"global-agent": "^3.0.0"
}
},
"node_modules/@electron/get/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/@sindresorhus/is": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
"integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sindresorhus/is?sponsor=1"
}
},
"node_modules/@szmarczak/http-timer": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
"integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
"dev": true,
"license": "MIT",
"dependencies": {
"defer-to-connect": "^2.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@types/cacheable-request": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
"integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/http-cache-semantics": "*",
"@types/keyv": "^3.1.4",
"@types/node": "*",
"@types/responselike": "^1.0.0"
}
},
"node_modules/@types/http-cache-semantics": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
"integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/keyv": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
"integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/node": {
"version": "22.16.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.4.tgz",
"integrity": "sha512-PYRhNtZdm2wH/NT2k/oAJ6/f2VD2N2Dag0lGlx2vWgMSJXGNmlce5MiTQzoWAiIJtso30mjnfQCOKVH+kAQC/g==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/@types/responselike": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz",
"integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/semver": {
"version": "7.7.0",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz",
"integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/yauzl": {
"version": "2.10.3",
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
"integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/boolean": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz",
"integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==",
"deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/buffer-crc32": {
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
"integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/cacheable-lookup": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
"integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10.6.0"
}
},
"node_modules/cacheable-request": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz",
"integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==",
"dev": true,
"license": "MIT",
"dependencies": {
"clone-response": "^1.0.2",
"get-stream": "^5.1.0",
"http-cache-semantics": "^4.0.0",
"keyv": "^4.0.0",
"lowercase-keys": "^2.0.0",
"normalize-url": "^6.0.1",
"responselike": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
},
"engines": {
"node": ">= 14.16.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/clone-response": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz",
"integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==",
"dev": true,
"license": "MIT",
"dependencies": {
"mimic-response": "^1.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/debug": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/decompress-response": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"mimic-response": "^3.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/decompress-response/node_modules/mimic-response": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/defer-to-connect": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
"integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/define-data-property": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"gopd": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/define-properties": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
"integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"define-data-property": "^1.0.1",
"has-property-descriptors": "^1.0.0",
"object-keys": "^1.1.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/detect-node": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
"integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/electron": {
"version": "37.2.3",
"resolved": "https://registry.npmjs.org/electron/-/electron-37.2.3.tgz",
"integrity": "sha512-JRKKn8cRDXDfkC+oWISbYs+c+L6RA776JM0NiB9bn2yV8H/LnBUlVPzKKfsXgrUIokN4YcbCw694vfAdEJwtGw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@electron/get": "^2.0.0",
"@types/node": "^22.7.7",
"extract-zip": "^2.0.1"
},
"bin": {
"electron": "cli.js"
},
"engines": {
"node": ">= 12.20.55"
}
},
"node_modules/end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"once": "^1.4.0"
}
},
"node_modules/env-paths": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
"integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es6-error": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
"integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/extract-zip": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
"integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"debug": "^4.1.1",
"get-stream": "^5.1.0",
"yauzl": "^2.10.0"
},
"bin": {
"extract-zip": "cli.js"
},
"engines": {
"node": ">= 10.17.0"
},
"optionalDependencies": {
"@types/yauzl": "^2.9.1"
}
},
"node_modules/fd-slicer": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
"integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
"dev": true,
"license": "MIT",
"dependencies": {
"pend": "~1.2.0"
}
},
"node_modules/fs-extra": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
},
"engines": {
"node": ">=6 <7 || >=8"
}
},
"node_modules/get-stream": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
"dev": true,
"license": "MIT",
"dependencies": {
"pump": "^3.0.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/global-agent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz",
"integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==",
"dev": true,
"license": "BSD-3-Clause",
"optional": true,
"dependencies": {
"boolean": "^3.0.1",
"es6-error": "^4.1.1",
"matcher": "^3.0.0",
"roarr": "^2.15.3",
"semver": "^7.3.2",
"serialize-error": "^7.0.1"
},
"engines": {
"node": ">=10.0"
}
},
"node_modules/globalthis": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
"integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"define-properties": "^1.2.1",
"gopd": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/got": {
"version": "11.8.6",
"resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz",
"integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@sindresorhus/is": "^4.0.0",
"@szmarczak/http-timer": "^4.0.5",
"@types/cacheable-request": "^6.0.1",
"@types/responselike": "^1.0.0",
"cacheable-lookup": "^5.0.3",
"cacheable-request": "^7.0.2",
"decompress-response": "^6.0.0",
"http2-wrapper": "^1.0.0-beta.5.2",
"lowercase-keys": "^2.0.0",
"p-cancelable": "^2.0.0",
"responselike": "^2.0.0"
},
"engines": {
"node": ">=10.19.0"
},
"funding": {
"url": "https://github.com/sindresorhus/got?sponsor=1"
}
},
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true,
"license": "ISC"
},
"node_modules/has-property-descriptors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"es-define-property": "^1.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/http-cache-semantics": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
"integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==",
"dev": true,
"license": "BSD-2-Clause"
},
"node_modules/http2-wrapper": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
"integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
"dev": true,
"license": "MIT",
"dependencies": {
"quick-lru": "^5.1.1",
"resolve-alpn": "^1.0.0"
},
"engines": {
"node": ">=10.19.0"
}
},
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
"dev": true,
"license": "MIT"
},
"node_modules/json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
"dev": true,
"license": "ISC",
"optional": true
},
"node_modules/jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
"integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
"dev": true,
"license": "MIT",
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
"dev": true,
"license": "MIT",
"dependencies": {
"json-buffer": "3.0.1"
}
},
"node_modules/lowercase-keys": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
"integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/matcher": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
"integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"escape-string-regexp": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/mimic-response": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
"integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true,
"license": "MIT"
},
"node_modules/normalize-url": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
"integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/object-keys": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">= 0.4"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true,
"license": "ISC",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/p-cancelable": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
"integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/pend": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
"dev": true,
"license": "MIT"
},
"node_modules/progress": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/pump": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz",
"integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==",
"dev": true,
"license": "MIT",
"dependencies": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"node_modules/quick-lru": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
"integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/readdirp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
"license": "MIT",
"engines": {
"node": ">= 14.18.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/resolve-alpn": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
"integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==",
"dev": true,
"license": "MIT"
},
"node_modules/responselike": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz",
"integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==",
"dev": true,
"license": "MIT",
"dependencies": {
"lowercase-keys": "^2.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/roarr": {
"version": "2.15.4",
"resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz",
"integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==",
"dev": true,
"license": "BSD-3-Clause",
"optional": true,
"dependencies": {
"boolean": "^3.0.1",
"detect-node": "^2.0.4",
"globalthis": "^1.0.1",
"json-stringify-safe": "^5.0.1",
"semver-compare": "^1.0.0",
"sprintf-js": "^1.1.2"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/semver": {
"version": "7.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/semver-compare": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
"integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/serialize-error": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz",
"integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"type-fest": "^0.13.1"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/sprintf-js": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
"dev": true,
"license": "BSD-3-Clause",
"optional": true
},
"node_modules/sumchecker": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz",
"integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"debug": "^4.1.0"
},
"engines": {
"node": ">= 8.0"
}
},
"node_modules/type-fest": {
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
"integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
"dev": true,
"license": "(MIT OR CC0-1.0)",
"optional": true,
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/typescript": {
"version": "5.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"license": "MIT"
},
"node_modules/universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true,
"license": "ISC"
},
"node_modules/yauzl": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
"integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
"dev": true,
"license": "MIT",
"dependencies": {
"buffer-crc32": "~0.2.3",
"fd-slicer": "~1.1.0"
}
},
"node_modules/zod": {
"version": "3.24.2",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz",
"integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
}
}
}

View File

@ -1,21 +1,21 @@
{
"name": "electron",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"private": true,
"scripts": {
"startDev": "electron --disable-direct-composition --in-process-gpu . --dev --local",
"startDevGpu": "electron --enable-gpu-rasterization --enable-accelerated-2d-canvas --num-raster-threads=8 --enable-zero-copy . --dev --local",
"start": "electron --disable-direct-composition --in-process-gpu ."
},
"devDependencies": {},
"optionalDependencies": {
"shapez.io-private-artifacts": "github:tobspr/shapez.io-private-artifacts#abi-v99"
},
"dependencies": {
"async-lock": "^1.2.8",
"electron": "16.2.8",
"electron-window-state": "^5.0.3"
}
}
{
"name": "electron",
"version": "1.0.0",
"license": "MIT",
"type": "module",
"main": "dist/index.js",
"private": true,
"scripts": {
"start": "tsc && electron ."
},
"dependencies": {
"chokidar": "^4.0.3",
"semver": "^7.7.1",
"zod": "^3.24.2"
},
"devDependencies": {
"@types/semver": "^7.7.0",
"electron": "^37.2.3",
"typescript": "^5.8.2"
}
}

22
electron/src/config.ts Normal file
View File

@ -0,0 +1,22 @@
import { app } from "electron";
import path from "node:path";
const disabledFeatures = ["HardwareMediaKeyHandling"];
app.commandLine.appendSwitch("disable-features", disabledFeatures.join(","));
export const defaultWindowTitle = "shapez CE";
app.setName("shapez-ce");
// This variable should be used to avoid situations where the app name
// wasn't set yet.
export const userData = app.getPath("userData");
export const executableDir = path.dirname(app.getPath("exe"));
export const pageUrl = app.isPackaged
? new URL("../index.html", import.meta.url).href
: "http://localhost:3005/";
export const switches = {
dev: app.commandLine.hasSwitch("dev"),
safeMode: app.commandLine.hasSwitch("safe-mode"),
};

132
electron/src/fsjob.ts Normal file
View File

@ -0,0 +1,132 @@
import { BrowserWindow, dialog, FileFilter } from "electron";
import fs from "fs/promises";
import path from "path";
import { userData } from "./config.js";
interface GenericFsJob {
id: string;
}
export type InitializeFsJob = GenericFsJob & { type: "initialize" };
type ListFsJob = GenericFsJob & { type: "list"; filename: string };
type ReadFsJob = GenericFsJob & { type: "read"; filename: string };
type WriteFsJob = GenericFsJob & { type: "write"; filename: string; contents: Uint8Array };
type DeleteFsJob = GenericFsJob & { type: "delete"; filename: string };
type OpenExternalFsJob = GenericFsJob & { type: "open-external"; extension: string };
type SaveExternalFsJob = GenericFsJob & { type: "save-external"; filename: string; contents: Uint8Array };
export type FsJob =
| InitializeFsJob
| ListFsJob
| ReadFsJob
| WriteFsJob
| DeleteFsJob
| OpenExternalFsJob
| SaveExternalFsJob;
type FsJobResult = Uint8Array | string[] | void;
export class FsJobHandler {
readonly rootDir: string;
private initialized = false;
constructor(subDir: string) {
this.rootDir = path.join(userData, subDir);
}
async initialize(): Promise<void> {
if (this.initialized) {
return;
}
// Create the directory so that users know where to put files
await fs.mkdir(this.rootDir, { recursive: true });
this.initialized = true;
}
handleJob(job: FsJob): Promise<FsJobResult> {
switch (job.type) {
case "initialize":
return this.initialize();
case "open-external":
return this.openExternal(job.extension);
case "save-external":
return this.saveExternal(job.filename, job.contents);
}
const filename = this.safeFileName(job.filename);
switch (job.type) {
case "list":
return this.list(filename);
case "read":
return fs.readFile(filename);
case "write":
return this.write(filename, job.contents);
case "delete":
return fs.unlink(filename);
}
// @ts-expect-error this method can actually receive garbage
throw new Error(`Unknown FS job type: ${job.type}`);
}
private async openExternal(extension: string): Promise<Uint8Array | undefined> {
const filters = this.getFileDialogFilters(extension === "*" ? undefined : extension);
const window = BrowserWindow.getAllWindows()[0]!;
const result = await dialog.showOpenDialog(window, { filters, properties: ["openFile"] });
if (result.canceled) {
return undefined;
}
return await fs.readFile(result.filePaths[0]);
}
private async saveExternal(filename: string, contents: Uint8Array): Promise<void> {
// Try to guess extension
const ext = filename.indexOf(".") < 1 ? filename.split(".").at(-1)! : undefined;
const filters = this.getFileDialogFilters(ext);
const window = BrowserWindow.getAllWindows()[0]!;
const result = await dialog.showSaveDialog(window, { defaultPath: filename, filters });
if (result.canceled) {
return;
}
return await fs.writeFile(result.filePath, contents);
}
private getFileDialogFilters(extension?: string): FileFilter[] {
const filters: FileFilter[] = [{ name: "All files", extensions: ["*"] }];
if (extension !== undefined) {
filters.unshift({
name: `${extension.toUpperCase()} files`,
extensions: [extension],
});
}
return filters;
}
private list(subdir: string): Promise<string[]> {
// Bare-bones implementation
return fs.readdir(subdir);
}
private async write(file: string, contents: Uint8Array): Promise<void> {
// The target directory might not exist, ensure it does
const parentDir = path.dirname(file);
await fs.mkdir(parentDir, { recursive: true });
await fs.writeFile(file, contents);
}
private safeFileName(name: string) {
// TODO: Rather than restricting file names, attempt to resolve everything
// relative to the data directory (i.e. normalize the file path, then join)
const relative = name.replace(/[^a-z.0-9_-]/gi, "_");
return path.join(this.rootDir, relative);
}
}

95
electron/src/index.ts Normal file
View File

@ -0,0 +1,95 @@
import { BrowserWindow, app, shell } from "electron";
import path from "path";
import { defaultWindowTitle, pageUrl, switches } from "./config.js";
import { IpcHandler } from "./ipc.js";
import { ModLoader } from "./mods/loader.js";
import { ModProtocolHandler } from "./mods/protocol_handler.js";
let win: BrowserWindow | null = null;
if (!app.requestSingleInstanceLock()) {
app.quit();
} else {
app.on("second-instance", () => {
if (win?.isMinimized()) {
win.restore();
}
win?.focus();
});
}
const modLoader = new ModLoader();
const modProtocol = new ModProtocolHandler(modLoader);
const ipc = new IpcHandler(modLoader);
function createWindow() {
// The protocol can only be handled after "ready" event
modProtocol.install();
const window = new BrowserWindow({
minWidth: 800,
minHeight: 600,
useContentSize: true,
autoHideMenuBar: !switches.dev,
show: false,
title: defaultWindowTitle,
webPreferences: {
preload: path.join(import.meta.dirname, "../preload.cjs"),
},
});
win = window;
if (!switches.dev) {
window.removeMenu();
}
window.on("ready-to-show", () => {
window.show();
});
ipc.install(window);
window.loadURL(pageUrl);
modLoader.on("forcereload", () => {
// TODO: Find a better way to manage cache when force
// reloading (use a non-persistent session?)
window.webContents.session.clearData({ dataTypes: ["cache"] }).then(() => window.reload());
});
// Redirect any kind of main frame navigation to external applications
window.webContents.on("will-navigate", (ev, url) => {
if (url === window.webContents.getURL()) {
// Avoid handling reloads externally
return;
}
ev.preventDefault();
openExternalUrl(url);
});
// Also redirect window.open
window.webContents.setWindowOpenHandler(({ url }) => {
openExternalUrl(url);
return { action: "deny" };
});
}
function openExternalUrl(urlString: string) {
try {
const url = new URL(urlString);
// TODO: Let the user explicitly allow other protocols
if (["http:", "https:"].includes(url.protocol)) {
shell.openExternal(urlString);
}
} catch {
// Ignore invalid URLs
}
}
app.on("ready", createWindow);
app.on("window-all-closed", () => {
app.quit();
});

41
electron/src/ipc.ts Normal file
View File

@ -0,0 +1,41 @@
import { BrowserWindow, IpcMainInvokeEvent, ipcMain } from "electron";
import { FsJob, FsJobHandler } from "./fsjob.js";
import { ModLoader } from "./mods/loader.js";
export class IpcHandler {
private readonly savesHandler = new FsJobHandler("saves");
private readonly modLoader: ModLoader;
constructor(modLoader: ModLoader) {
this.modLoader = modLoader;
}
install(window: BrowserWindow) {
ipcMain.handle("fs-job", this.handleFsJob.bind(this));
ipcMain.handle("get-mods", this.getMods.bind(this));
ipcMain.handle("set-fullscreen", this.setFullscreen.bind(this, window));
// Not implemented
// ipcMain.handle("open-mods-folder", ...)
}
private handleFsJob(_event: IpcMainInvokeEvent, job: FsJob) {
if (job.id !== "saves") {
throw new Error("Storages other than saves/ are not implemented yet");
}
return this.savesHandler.handleJob(job);
}
private async getMods() {
// TODO: Split mod reloads into a different IPC request
await this.modLoader.loadMods();
return this.modLoader.getAllMods();
}
private setFullscreen(window: BrowserWindow, _event: IpcMainInvokeEvent, flag: boolean) {
if (window.isFullScreen() != flag) {
window.setFullScreen(flag);
}
}
}

166
electron/src/mods/loader.ts Normal file
View File

@ -0,0 +1,166 @@
import EventEmitter from "node:events";
import fs from "node:fs/promises";
import path from "node:path";
import { DevelopmentModLocator, DistroModLocator, ModLocator, UserModLocator } from "./locator.js";
import { IpcModMetadata, ModMetadata } from "./metadata.js";
type ModSource = "user" | "distro" | "dev";
interface ModLocation {
source: ModSource;
file: string;
}
interface DisabledMod {
source: ModSource;
id: string;
}
interface IpcMod extends ModLocation {
disabled: boolean;
metadata: IpcModMetadata;
}
const METADATA_FILE = "mod.json";
class Mod {
readonly source: ModSource;
readonly file: string;
readonly metadata: ModMetadata;
disabled = false;
constructor(source: ModSource, file: string, metadata: ModMetadata) {
this.source = source;
this.file = file;
this.metadata = metadata;
}
toJSON(): IpcMod {
return {
source: this.source,
file: this.file,
disabled: this.disabled,
metadata: {
...this.metadata,
version: this.metadata.version.format(),
},
};
}
}
export class ModLoader extends EventEmitter {
private mods: Mod[] = [];
private readonly locators = new Map<ModSource, ModLocator>();
constructor() {
super();
this.locators.set("user", new UserModLocator());
this.locators.set("distro", new DistroModLocator());
const devLocator = new DevelopmentModLocator();
this.locators.set("dev", devLocator);
// If requested, restart automatically when dev mods are modified
devLocator.fsWatcher?.on("all", this.delayedForceReload());
}
/**
* Resets modloader state and reloads all mods, then triggers page reload.
*/
async forceReload() {
await this.loadMods();
this.emit("forcereload");
}
async loadMods(): Promise<void> {
const mods: Mod[] = [];
this.mods = mods;
const locations = await this.locateAllMods();
for (const location of locations) {
const metadata = await this.resolveMetadata(location);
if (metadata === null) {
continue;
}
// TODO: Only check this after applying disabled state
if (this.isModPresent(metadata.id)) {
console.warn(`Ignoring duplicate mod ${location.source}::${location.file}`);
continue;
}
mods.push(new Mod(location.source, location.file, metadata));
}
// Check for mods that should be disabled
for (const { source, id } of await this.collectDisabledMods()) {
const target = mods.find(m => m.source === source && m.metadata.id === id);
if (target !== undefined) {
target.disabled = true;
}
}
}
getAllMods(): IpcMod[] {
return this.mods.map(mod => mod.toJSON());
}
isModPresent(id: string): boolean {
return this.mods.some(mod => mod.metadata.id === id);
}
getModById(id: string): Mod | undefined {
return this.mods.find(mod => mod.metadata.id === id);
}
private delayedForceReload() {
// Debounce the force reload manually as chokidar won't aggregate events the way we want
// NOTE: The delay chosen here (250ms) is quite arbitrary!
let timeout: NodeJS.Timeout | undefined = undefined;
return () => {
clearTimeout(timeout);
timeout = setTimeout(() => this.forceReload(), 250);
};
}
private async locateAllMods(): Promise<ModLocation[]> {
// Sort locators by priority, lowest number is highest priority
const locators = [...this.locators.entries()].sort(([, a], [, b]) => a.priority - b.priority);
const result: ModLocation[] = [];
for (const [source, locator] of locators) {
for (const file of await locator.locateMods()) {
result.push({ source, file });
}
}
return result;
}
private async resolveMetadata(mod: ModLocation): Promise<ModMetadata | null> {
// TODO: This function might call validation routines
const filePath = path.join(mod.file, METADATA_FILE);
try {
const contents = await fs.readFile(filePath, "utf-8");
return ModMetadata.parse(JSON.parse(contents));
} catch (err) {
// TODO: Collect mod errors, show to the user once all mods are loaded
console.error("Failed to read mod metadata", err);
return null;
}
}
private async collectDisabledMods(): Promise<DisabledMod[]> {
const result: DisabledMod[] = [];
for (const [source, locator] of this.locators.entries()) {
for (const id of await locator.getDisabledMods()) {
result.push({ source, id });
}
}
return result;
}
}

View File

@ -0,0 +1,213 @@
import chokidar, { FSWatcher } from "chokidar";
import { app } from "electron";
import fs from "node:fs/promises";
import path from "node:path";
import { executableDir, switches, userData } from "../config.js";
export const MOD_FILE_SUFFIX = ".asar";
const DISABLED_MODS_FILE = "disabled-mods.json";
const USER_MODS_DIR = path.join(userData, "mods");
const DISTRO_MODS_DIR = path.join(executableDir, "mods");
const DEV_SWITCH = "load-mod";
const DEV_WATCH_SWITCH = "watch";
const DEV_USER_MOD_PREFIX = "@/";
export interface ModLocator {
readonly priority: number;
/**
* Asynchronously look for mod candidates.
*
* @returns absolute file paths of located mods
*/
locateMods(): Promise<string[]>;
/**
* Mark or unmark the specified mod as disabled.
*
* @param id ID of the mod to disable or enable
* @param flag whether to disable the mod
*/
setModDisabled(id: string, flag: boolean): Promise<void>;
/**
* Retrieve the list of mod IDs that should not be loaded.
*
* @returns IDs of the disabled mods
*/
getDisabledMods(): Promise<string[]>;
}
abstract class DirectoryModLocator implements ModLocator {
abstract readonly priority: number;
protected readonly directory: string;
private readonly disabledModsFile: string;
private disabledMods: Set<string> | null = null;
constructor(directory: string) {
this.directory = directory;
this.disabledModsFile = path.join(directory, DISABLED_MODS_FILE);
}
async locateMods(): Promise<string[]> {
if (switches.safeMode) {
return [];
}
try {
const dir = await fs.readdir(this.directory, { withFileTypes: true });
return dir
.filter(entry => entry.name.endsWith(MOD_FILE_SUFFIX))
.map(entry => path.join(entry.path, entry.name));
} catch (err) {
if ((err as NodeJS.ErrnoException).code === "ENOENT") {
// The directory does not exist
return [];
}
// Propagate all other errors
throw err;
}
}
setModDisabled(id: string, flag: boolean): Promise<void> {
// Note: it is assumed that calling this before accessing
// getDisabledMods will overwrite the file.
this.disabledMods ??= new Set();
if (flag) {
this.disabledMods.add(id);
} else {
this.disabledMods.delete(id);
}
return this.writeDisabledModsFile();
}
async getDisabledMods(): Promise<string[]> {
if (this.disabledMods === null) {
await this.readDisabledModsFile();
}
return [...this.disabledMods!];
}
private async readDisabledModsFile(): Promise<void> {
// TODO: Validate internal structure (once something is added for
// mod metadata file validation)
try {
const contents = await fs.readFile(this.disabledModsFile, "utf-8");
this.disabledMods = new Set(JSON.parse(contents));
} catch (err) {
// Ensure we don't fail twice
this.disabledMods ??= new Set();
if ((err as NodeJS.ErrnoException).code == "ENOENT") {
// Ignore error entirely if the file is missing
return;
}
if (err instanceof SyntaxError) {
// Malformed JSON, replace the file
return this.writeDisabledModsFile();
}
console.warn(`Reading ${this.disabledModsFile} failed:`, err);
}
}
private async writeDisabledModsFile(): Promise<void> {
try {
const contents = JSON.stringify([...(this.disabledMods ?? new Set())]);
await fs.writeFile(this.disabledModsFile, contents, "utf-8");
} catch (err: unknown) {
// Nothing we can do
console.warn(`Writing ${this.disabledModsFile} failed:`, err);
}
}
}
export class UserModLocator extends DirectoryModLocator {
readonly priority = 1;
constructor() {
super(USER_MODS_DIR);
}
async locateMods(): Promise<string[]> {
// Ensure the directory exists
await fs.mkdir(this.directory, { recursive: true });
return super.locateMods();
}
}
export class DistroModLocator extends DirectoryModLocator {
readonly priority = 2;
constructor() {
super(DISTRO_MODS_DIR);
}
}
export class DevelopmentModLocator implements ModLocator {
readonly priority = 0;
readonly fsWatcher: FSWatcher | null = null;
private readonly modFiles: string[] = [];
private readonly disabledMods = new Set<string>();
constructor() {
const switchValue = app.commandLine.getSwitchValue(DEV_SWITCH);
if (switchValue === "") {
// Empty string = switch not passed
return;
}
const resolved = switchValue.split(",").map(f => this.resolveFile(f));
this.modFiles.push(...resolved);
const watchMode = app.commandLine.hasSwitch(DEV_WATCH_SWITCH);
if (!watchMode || this.modFiles.length === 0) {
// Skip setting up chokidar
return;
}
this.fsWatcher = chokidar.watch(this.modFiles, {
persistent: false,
ignoreInitial: true,
});
}
locateMods(): Promise<string[]> {
return Promise.resolve(this.modFiles);
}
setModDisabled(id: string, flag: boolean): Promise<void> {
if (flag) {
this.disabledMods.add(id);
} else {
this.disabledMods.delete(id);
}
return Promise.resolve();
}
getDisabledMods(): Promise<string[]> {
return Promise.resolve([...this.disabledMods]);
}
private resolveFile(file: string) {
// Allow using @/*.asar to reference user mods directory
if (file.startsWith(DEV_USER_MOD_PREFIX)) {
file = file.slice(DEV_USER_MOD_PREFIX.length);
return path.join(USER_MODS_DIR, file);
}
// Resolve mods relative to CWD, useful for development
return path.resolve(file);
}
}

View File

@ -0,0 +1,38 @@
import SemVer from "semver/classes/semver.js";
import { z } from "zod";
const semver = z.string().transform((str, ctx) => {
try {
return new SemVer(str);
} catch {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Not a valid SemVer version string",
});
return z.NEVER;
}
});
// TBD: dependencies, icons, readme
export const ModMetadata = z.object({
format: z.literal(1),
id: z.string().regex(/^[a-z0-9][a-z0-9_-]{0,48}[a-z0-9]$/g),
entry: z.string().nonempty(),
name: z.string().nonempty(),
description: z.ostring(),
authors: z
.object({
name: z.string().nonempty(),
website: z.string().url().optional(),
})
.array(),
version: semver,
savegameResident: z.boolean().default(true),
website: z.string().url().optional(),
source: z.string().url().optional(),
});
export type ModMetadata = z.infer<typeof ModMetadata>;
export type IpcModMetadata = Omit<ModMetadata, "version"> & {
version: string;
};

View File

@ -0,0 +1,98 @@
import { net, protocol } from "electron";
import { lstat, readdir } from "node:fs/promises";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { ModLoader } from "./loader.js";
export const MOD_SCHEME = "mod";
export class ModProtocolHandler {
private modLoader: ModLoader;
constructor(modLoader: ModLoader) {
this.modLoader = modLoader;
protocol.registerSchemesAsPrivileged([
{
scheme: MOD_SCHEME,
privileges: {
allowServiceWorkers: true,
bypassCSP: true,
secure: true,
standard: true,
stream: true,
supportFetchAPI: true,
},
},
]);
}
install() {
protocol.handle(MOD_SCHEME, this.handler.bind(this));
}
private async handler(request: GlobalRequest): Promise<GlobalResponse> {
const fileUrl = this.getFileUrlForRequest(request);
if (fileUrl === undefined) {
return Response.error();
}
try {
return await net.fetch(fileUrl.toString());
} catch (err) {
// Check if this is a directory request
const directoryIndex = await this.getDirectoryIndex(fileUrl);
if (directoryIndex !== null) {
return directoryIndex;
}
console.error("Failed to fetch:", err);
return Response.error();
}
}
private async getDirectoryIndex(fileUrl: URL): Promise<GlobalResponse | null> {
if (!fileUrl.pathname.endsWith("/")) {
return null;
}
// Remove the trailing slash
fileUrl.pathname = fileUrl.pathname.slice(0, -1);
try {
const stats = await lstat(fileUrl);
if (!stats.isDirectory()) {
return null;
}
const dir = await readdir(fileUrl, { withFileTypes: true });
const result = dir.map(entry => entry.name + (entry.isDirectory() ? "/" : ""));
return Response.json(result);
} catch (err) {
console.error("Failed to get directory index:", err);
return null;
}
}
private getFileUrlForRequest(request: GlobalRequest): URL | undefined {
// mod://mod-id/path/to/file
const modUrl = new URL(request.url);
const mod = this.modLoader.getModById(modUrl.hostname);
if (mod === undefined) {
return undefined;
}
const bundle = mod.file;
const filePath = path.join(bundle, modUrl.pathname);
// Check if the path escapes the bundle as per Electron example
// NOTE: this means file names cannot start with ..
const relative = path.relative(bundle, filePath);
if (relative.startsWith("..") || path.isAbsolute(relative)) {
return undefined;
}
return pathToFileURL(filePath);
}
}

View File

@ -1,112 +0,0 @@
const fs = require("fs");
const path = require("path");
const { ipcMain } = require("electron");
let greenworks = null;
let appId = null;
let initialized = false;
try {
greenworks = require("shapez.io-private-artifacts/steam/greenworks");
appId = parseInt(fs.readFileSync(path.join(__dirname, "steam_appid.txt"), "utf8"));
} catch (err) {
// greenworks is not installed
console.warn("Failed to load steam api:", err);
}
console.log("App ID:", appId);
function init(isDev) {
if (!greenworks) {
return;
}
if (!isDev) {
if (greenworks.restartAppIfNecessary(appId)) {
console.log("Restarting ...");
process.exit(0);
}
}
if (!greenworks.init()) {
console.log("Failed to initialize greenworks");
process.exit(1);
}
initialized = true;
}
function listen() {
ipcMain.handle("steam:is-initialized", isInitialized);
if (!initialized) {
console.warn("Steam not initialized, won't be able to listen");
return;
}
if (!greenworks) {
console.warn("Greenworks not loaded, won't be able to listen");
return;
}
console.log("Adding listeners");
ipcMain.handle("steam:get-achievement-names", getAchievementNames);
ipcMain.handle("steam:activate-achievement", activateAchievement);
function bufferToHex(buffer) {
return Array.from(new Uint8Array(buffer))
.map(b => b.toString(16).padStart(2, "0"))
.join("");
}
ipcMain.handle("steam:get-ticket", (event, arg) => {
console.log("Requested steam ticket ...");
return new Promise((resolve, reject) => {
greenworks.getAuthSessionTicket(
success => {
const ticketHex = bufferToHex(success.ticket);
resolve(ticketHex);
},
error => {
console.error("Failed to get steam ticket:", error);
reject(error);
}
);
});
});
ipcMain.handle("steam:check-app-ownership", (event, appId) => {
return Promise.resolve(greenworks.isDLCInstalled(appId));
});
}
function isInitialized(event) {
return Promise.resolve(initialized);
}
function getAchievementNames(event) {
return new Promise((resolve, reject) => {
try {
const achievements = greenworks.getAchievementNames();
resolve(achievements);
} catch (err) {
reject(err);
}
});
}
function activateAchievement(event, id) {
return new Promise((resolve, reject) => {
greenworks.activateAchievement(
id,
() => resolve(),
err => reject(err)
);
});
}
module.exports = {
init,
listen,
};

View File

@ -1 +0,0 @@
1318690

9
electron/tsconfig.json Normal file
View File

@ -0,0 +1,9 @@
{
"extends": "../tsconfig.json",
"include": ["./src/**/*"],
"compilerOptions": {
"noEmit": false,
"outDir": "./dist",
"strict": true
}
}

View File

@ -1,584 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@electron/get@^1.13.0":
version "1.13.1"
resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.13.1.tgz#42a0aa62fd1189638bd966e23effaebb16108368"
integrity sha512-U5vkXDZ9DwXtkPqlB45tfYnnYBN8PePp1z/XDCupnSpdrxT8/ThCv9WCwPLf9oqiSGZTkH6dx2jDUPuoXpjkcA==
dependencies:
debug "^4.1.1"
env-paths "^2.2.0"
fs-extra "^8.1.0"
got "^9.6.0"
progress "^2.0.3"
semver "^6.2.0"
sumchecker "^3.0.1"
optionalDependencies:
global-agent "^3.0.0"
global-tunnel-ng "^2.7.1"
"@sindresorhus/is@^0.14.0":
version "0.14.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==
"@szmarczak/http-timer@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421"
integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==
dependencies:
defer-to-connect "^1.0.1"
"@types/node@^14.6.2":
version "14.18.20"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.20.tgz#268f028b36eaf51181c3300252f605488c4f0650"
integrity sha512-Q8KKwm9YqEmUBRsqJ2GWJDtXltBDxTdC4m5vTdXBolu2PeQh8LX+f6BTwU+OuXPu37fLxoN6gidqBmnky36FXA==
async-lock@^1.2.8:
version "1.2.8"
resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.2.8.tgz#7b02bdfa2de603c0713acecd11184cf97bbc7c4c"
integrity sha512-G+26B2jc0Gw0EG/WN2M6IczuGepBsfR1+DtqLnyFSH4p2C668qkOCtEkGNVEaaNAVlYwEMazy1+/jnLxltBkIQ==
boolean@^3.0.1:
version "3.0.2"
resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.0.2.tgz#df1baa18b6a2b0e70840475e1d93ec8fe75b2570"
integrity sha512-RwywHlpCRc3/Wh81MiCKun4ydaIFyW5Ea6JbL6sRCVx5q5irDw7pMXBUFYF/jArQ6YrG36q0kpovc9P/Kd3I4g==
buffer-crc32@~0.2.3:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
buffer-from@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
cacheable-request@^6.0.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912"
integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==
dependencies:
clone-response "^1.0.2"
get-stream "^5.1.0"
http-cache-semantics "^4.0.0"
keyv "^3.0.0"
lowercase-keys "^2.0.0"
normalize-url "^4.1.0"
responselike "^1.0.2"
clone-response@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b"
integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=
dependencies:
mimic-response "^1.0.0"
concat-stream@^1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
dependencies:
buffer-from "^1.0.0"
inherits "^2.0.3"
readable-stream "^2.2.2"
typedarray "^0.0.6"
config-chain@^1.1.11:
version "1.1.12"
resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa"
integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==
dependencies:
ini "^1.3.4"
proto-list "~1.2.1"
core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
debug@^4.1.0, debug@^4.1.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
dependencies:
ms "2.1.2"
decompress-response@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3"
integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=
dependencies:
mimic-response "^1.0.0"
defer-to-connect@^1.0.1:
version "1.1.3"
resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591"
integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==
define-properties@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
dependencies:
object-keys "^1.0.12"
detect-node@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c"
integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==
duplexer3@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=
electron-window-state@^5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/electron-window-state/-/electron-window-state-5.0.3.tgz#4f36d09e3f953d87aff103bf010f460056050aa8"
integrity sha512-1mNTwCfkolXl3kMf50yW3vE2lZj0y92P/HYWFBrb+v2S/pCka5mdwN3cagKm458A7NjndSwijynXgcLWRodsVg==
dependencies:
jsonfile "^4.0.0"
mkdirp "^0.5.1"
electron@16.2.8:
version "16.2.8"
resolved "https://registry.yarnpkg.com/electron/-/electron-16.2.8.tgz#b7f2bd1184701e54a1bc902839d5a3ec95bb8982"
integrity sha512-KSOytY6SPLsh3iCziztqa/WgJyfDOKzCvNqku9gGIqSdT8CqtV66dTU1SOrKZQjRFLxHaF8LbyxUL1vwe4taqw==
dependencies:
"@electron/get" "^1.13.0"
"@types/node" "^14.6.2"
extract-zip "^1.0.3"
encodeurl@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
end-of-stream@^1.1.0:
version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
dependencies:
once "^1.4.0"
env-paths@^2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
es6-error@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
escape-string-regexp@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
extract-zip@^1.0.3:
version "1.7.0"
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927"
integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==
dependencies:
concat-stream "^1.6.2"
debug "^2.6.9"
mkdirp "^0.5.4"
yauzl "^2.10.0"
fd-slicer@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=
dependencies:
pend "~1.2.0"
fs-extra@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^4.0.0"
universalify "^0.1.0"
get-stream@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
dependencies:
pump "^3.0.0"
get-stream@^5.1.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
dependencies:
pump "^3.0.0"
global-agent@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-3.0.0.tgz#ae7cd31bd3583b93c5a16437a1afe27cc33a1ab6"
integrity sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==
dependencies:
boolean "^3.0.1"
es6-error "^4.1.1"
matcher "^3.0.0"
roarr "^2.15.3"
semver "^7.3.2"
serialize-error "^7.0.1"
global-tunnel-ng@^2.7.1:
version "2.7.1"
resolved "https://registry.yarnpkg.com/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz#d03b5102dfde3a69914f5ee7d86761ca35d57d8f"
integrity sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==
dependencies:
encodeurl "^1.0.2"
lodash "^4.17.10"
npm-conf "^1.1.3"
tunnel "^0.0.6"
globalthis@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.2.tgz#2a235d34f4d8036219f7e34929b5de9e18166b8b"
integrity sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==
dependencies:
define-properties "^1.1.3"
got@^9.6.0:
version "9.6.0"
resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85"
integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==
dependencies:
"@sindresorhus/is" "^0.14.0"
"@szmarczak/http-timer" "^1.1.2"
cacheable-request "^6.0.0"
decompress-response "^3.3.0"
duplexer3 "^0.1.4"
get-stream "^4.1.0"
lowercase-keys "^1.0.1"
mimic-response "^1.0.1"
p-cancelable "^1.0.0"
to-readable-stream "^1.0.0"
url-parse-lax "^3.0.0"
graceful-fs@^4.1.6, graceful-fs@^4.2.0:
version "4.2.6"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
http-cache-semantics@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
inherits@^2.0.3, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ini@^1.3.4:
version "1.3.8"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
json-buffer@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=
json-stringify-safe@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
jsonfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
optionalDependencies:
graceful-fs "^4.1.6"
keyv@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9"
integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==
dependencies:
json-buffer "3.0.0"
lodash@^4.17.10:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==
lowercase-keys@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
dependencies:
yallist "^4.0.0"
matcher@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca"
integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==
dependencies:
escape-string-regexp "^4.0.0"
mimic-response@^1.0.0, mimic-response@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
mkdirp@^0.5.1, mkdirp@^0.5.4:
version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
dependencies:
minimist "^1.2.5"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
normalize-url@^4.1.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129"
integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==
npm-conf@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9"
integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==
dependencies:
config-chain "^1.1.11"
pify "^3.0.0"
object-keys@^1.0.12:
version "1.1.1"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
dependencies:
wrappy "1"
p-cancelable@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc"
integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==
pend@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
pify@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
prepend-http@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
progress@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
proto-list@~1.2.1:
version "1.2.4"
resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=
pump@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
dependencies:
end-of-stream "^1.1.0"
once "^1.3.1"
readable-stream@^2.2.2:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
responselike@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7"
integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=
dependencies:
lowercase-keys "^1.0.0"
roarr@^2.15.3:
version "2.15.4"
resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd"
integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==
dependencies:
boolean "^3.0.1"
detect-node "^2.0.4"
globalthis "^1.0.1"
json-stringify-safe "^5.0.1"
semver-compare "^1.0.0"
sprintf-js "^1.1.2"
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
semver-compare@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w=
semver@^6.2.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@^7.3.2:
version "7.3.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97"
integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==
dependencies:
lru-cache "^6.0.0"
serialize-error@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18"
integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==
dependencies:
type-fest "^0.13.1"
"shapez.io-private-artifacts@github:tobspr/shapez.io-private-artifacts#abi-v99":
version "0.1.0"
resolved "git+ssh://git@github.com/tobspr/shapez.io-private-artifacts.git#3293b20be26060fd36e9f00ded9ab5d0bdf57338"
sprintf-js@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673"
integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"
sumchecker@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42"
integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==
dependencies:
debug "^4.1.0"
to-readable-stream@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771"
integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==
tunnel@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
type-fest@^0.13.1:
version "0.13.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934"
integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==
typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
url-parse-lax@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c"
integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=
dependencies:
prepend-http "^2.0.0"
util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
yallist@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
yauzl@^2.10.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=
dependencies:
buffer-crc32 "~0.2.3"
fd-slicer "~1.1.0"

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

@ -1,381 +0,0 @@
/* eslint-disable quotes,no-undef */
const { app, BrowserWindow, Menu, MenuItem, ipcMain, shell, dialog, session } = require("electron");
const path = require("path");
const url = require("url");
const fs = require("fs");
const asyncLock = require("async-lock");
const windowStateKeeper = require("electron-window-state");
// Disable hardware key handling, i.e. being able to pause/resume the game music
// with hardware keys
app.commandLine.appendSwitch("disable-features", "HardwareMediaKeyHandling");
const isDev = app.commandLine.hasSwitch("dev");
const isLocal = app.commandLine.hasSwitch("local");
const safeMode = app.commandLine.hasSwitch("safe-mode");
const externalMod = app.commandLine.getSwitchValue("load-mod");
const roamingFolder =
process.env.APPDATA ||
(process.platform == "darwin"
? process.env.HOME + "/Library/Preferences"
: process.env.HOME + "/.local/share");
let storePath = path.join(roamingFolder, "shapez.io", "saves");
let modsPath = path.join(roamingFolder, "shapez.io", "mods");
if (!fs.existsSync(storePath)) {
// No try-catch by design
fs.mkdirSync(storePath, { recursive: true });
}
if (!fs.existsSync(modsPath)) {
fs.mkdirSync(modsPath, { recursive: true });
}
/** @type {BrowserWindow} */
let win = null;
let menu = null;
function createWindow() {
let faviconExtension = ".png";
if (process.platform === "win32") {
faviconExtension = ".ico";
}
const mainWindowState = windowStateKeeper({
defaultWidth: 1000,
defaultHeight: 800,
});
win = new BrowserWindow({
x: mainWindowState.x,
y: mainWindowState.y,
width: mainWindowState.width,
height: mainWindowState.height,
show: false,
backgroundColor: "#222428",
useContentSize: false,
minWidth: 800,
minHeight: 600,
title: "shapez",
transparent: false,
icon: path.join(__dirname, "favicon" + faviconExtension),
// fullscreen: true,
autoHideMenuBar: !isDev,
webPreferences: {
nodeIntegration: false,
nodeIntegrationInWorker: false,
nodeIntegrationInSubFrames: false,
contextIsolation: true,
enableRemoteModule: false,
disableBlinkFeatures: "Auxclick",
webSecurity: true,
sandbox: true,
preload: path.join(__dirname, "preload.js"),
experimentalFeatures: false,
},
allowRunningInsecureContent: false,
});
mainWindowState.manage(win);
if (isLocal) {
win.loadURL("http://localhost:3005");
} else {
win.loadURL(
url.format({
pathname: path.join(__dirname, "index.html"),
protocol: "file:",
slashes: true,
})
);
}
win.webContents.session.clearCache();
win.webContents.session.clearStorageData();
////// SECURITY
// Disable permission requests
win.webContents.session.setPermissionRequestHandler((webContents, permission, callback) => {
callback(false);
});
session.fromPartition("default").setPermissionRequestHandler((webContents, permission, callback) => {
callback(false);
});
app.on("web-contents-created", (event, contents) => {
// Disable vewbiew
contents.on("will-attach-webview", (event, webPreferences, params) => {
event.preventDefault();
});
// Disable navigation
contents.on("will-navigate", (event, navigationUrl) => {
event.preventDefault();
});
});
win.webContents.on("will-redirect", (contentsEvent, navigationUrl) => {
// Log and prevent the app from redirecting to a new page
console.error(
`The application tried to redirect to the following address: '${navigationUrl}'. This attempt was blocked.`
);
contentsEvent.preventDefault();
});
// Filter loading any module via remote;
// you shouldn't be using remote at all, though
// https://electronjs.org/docs/tutorial/security#16-filter-the-remote-module
app.on("remote-require", (event, webContents, moduleName) => {
event.preventDefault();
});
// built-ins are modules such as "app"
app.on("remote-get-builtin", (event, webContents, moduleName) => {
event.preventDefault();
});
app.on("remote-get-global", (event, webContents, globalName) => {
event.preventDefault();
});
app.on("remote-get-current-window", (event, webContents) => {
event.preventDefault();
});
app.on("remote-get-current-web-contents", (event, webContents) => {
event.preventDefault();
});
//// END SECURITY
win.webContents.on("new-window", (event, pth) => {
event.preventDefault();
if (pth.startsWith("https://")) {
shell.openExternal(pth);
}
});
win.on("closed", () => {
console.log("Window closed");
win = null;
});
if (isDev) {
menu = new Menu();
win.webContents.toggleDevTools();
const mainItem = new MenuItem({
label: "Toggle Dev Tools",
click: () => win.webContents.toggleDevTools(),
accelerator: "F12",
});
menu.append(mainItem);
const reloadItem = new MenuItem({
label: "Reload",
click: () => win.reload(),
accelerator: "F5",
});
menu.append(reloadItem);
const fullscreenItem = new MenuItem({
label: "Fullscreen",
click: () => win.setFullScreen(!win.isFullScreen()),
accelerator: "F11",
});
menu.append(fullscreenItem);
const mainMenu = new Menu();
mainMenu.append(
new MenuItem({
label: "shapez.io",
submenu: menu,
})
);
Menu.setApplicationMenu(mainMenu);
} else {
Menu.setApplicationMenu(null);
}
win.once("ready-to-show", () => {
win.show();
win.focus();
});
}
if (!app.requestSingleInstanceLock()) {
app.exit(0);
} else {
app.on("second-instance", () => {
// Someone tried to run a second instance, we should focus
if (win) {
if (win.isMinimized()) {
win.restore();
}
win.focus();
}
});
}
app.on("ready", createWindow);
app.on("window-all-closed", () => {
console.log("All windows closed");
app.quit();
});
ipcMain.on("set-fullscreen", (event, flag) => {
win.setFullScreen(flag);
});
ipcMain.on("exit-app", () => {
win.close();
app.quit();
});
let renameCounter = 1;
const fileLock = new asyncLock({
timeout: 30000,
maxPending: 1000,
});
function niceFileName(filename) {
return filename.replace(storePath, "@");
}
async function writeFileSafe(filename, contents) {
++renameCounter;
const prefix = "[ " + renameCounter + ":" + niceFileName(filename) + " ] ";
const transactionId = String(new Date().getTime()) + "." + renameCounter;
if (fileLock.isBusy()) {
console.warn(prefix, "Concurrent write process on", filename);
}
fileLock.acquire(filename, async () => {
console.log(prefix, "Starting write on", niceFileName(filename), "in transaction", transactionId);
if (!fs.existsSync(filename)) {
// this one is easy
console.log(prefix, "Writing file instantly because it does not exist:", niceFileName(filename));
await fs.promises.writeFile(filename, contents, "utf8");
return;
}
// first, write a temporary file (.tmp-XXX)
const tempName = filename + ".tmp-" + transactionId;
console.log(prefix, "Writing temporary file", niceFileName(tempName));
await fs.promises.writeFile(tempName, contents, "utf8");
// now, rename the original file to (.backup-XXX)
const oldTemporaryName = filename + ".backup-" + transactionId;
console.log(
prefix,
"Renaming old file",
niceFileName(filename),
"to",
niceFileName(oldTemporaryName)
);
await fs.promises.rename(filename, oldTemporaryName);
// now, rename the temporary file (.tmp-XXX) to the target
console.log(
prefix,
"Renaming the temporary file",
niceFileName(tempName),
"to the original",
niceFileName(filename)
);
await fs.promises.rename(tempName, filename);
// we are done now, try to create a backup, but don't fail if the backup fails
try {
// check if there is an old backup file
const backupFileName = filename + ".backup";
if (fs.existsSync(backupFileName)) {
console.log(prefix, "Deleting old backup file", niceFileName(backupFileName));
// delete the old backup
await fs.promises.unlink(backupFileName);
}
// rename the old file to the new backup file
console.log(prefix, "Moving", niceFileName(oldTemporaryName), "to the backup file location");
await fs.promises.rename(oldTemporaryName, backupFileName);
} catch (ex) {
console.error(prefix, "Failed to switch backup files:", ex);
}
});
}
ipcMain.handle("fs-job", async (event, job) => {
const filenameSafe = job.filename.replace(/[^a-z\.\-_0-9]/gi, "_");
const fname = path.join(storePath, filenameSafe);
switch (job.type) {
case "read": {
if (!fs.existsSync(fname)) {
// Special FILE_NOT_FOUND error code
return { error: "file_not_found" };
}
return await fs.promises.readFile(fname, "utf8");
}
case "write": {
await writeFileSafe(fname, job.contents);
return job.contents;
}
case "delete": {
await fs.promises.unlink(fname);
return;
}
default:
throw new Error("Unknown fs job: " + job.type);
}
});
ipcMain.handle("open-mods-folder", async () => {
shell.openPath(modsPath);
});
console.log("Loading mods ...");
function loadMods() {
if (safeMode) {
console.log("Safe Mode enabled for mods, skipping mod search");
}
console.log("Loading mods from", modsPath);
let modFiles = safeMode
? []
: fs
.readdirSync(modsPath)
.filter(filename => filename.endsWith(".js"))
.map(filename => path.join(modsPath, filename));
if (externalMod) {
console.log("Adding external mod source:", externalMod);
const externalModPaths = externalMod.split(",");
modFiles = modFiles.concat(externalModPaths);
}
return modFiles.map(filename => fs.readFileSync(filename, "utf8"));
}
let mods = [];
try {
mods = loadMods();
console.log("Loaded", mods.length, "mods");
} catch (ex) {
console.error("Failed to load mods");
dialog.showErrorBox("Failed to load mods:", ex);
}
ipcMain.handle("get-mods", async () => {
return mods;
});

View File

@ -1,17 +0,0 @@
{
"name": "electron",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"private": true,
"scripts": {
"startDev": "electron --disable-direct-composition --in-process-gpu . --dev --local",
"startDevGpu": "electron --enable-gpu-rasterization --enable-accelerated-2d-canvas --num-raster-threads=8 --enable-zero-copy . --dev --local",
"start": "electron --disable-direct-composition --in-process-gpu ."
},
"dependencies": {
"async-lock": "^1.2.8",
"electron": "16.2.8",
"electron-window-state": "^5.0.3"
}
}

View File

@ -1,7 +0,0 @@
const { contextBridge, ipcRenderer } = require("electron");
contextBridge.exposeInMainWorld("ipcRenderer", {
invoke: ipcRenderer.invoke.bind(ipcRenderer),
on: ipcRenderer.on.bind(ipcRenderer),
send: ipcRenderer.send.bind(ipcRenderer),
});

View File

@ -1,580 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@electron/get@^1.13.0":
version "1.13.1"
resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.13.1.tgz#42a0aa62fd1189638bd966e23effaebb16108368"
integrity sha512-U5vkXDZ9DwXtkPqlB45tfYnnYBN8PePp1z/XDCupnSpdrxT8/ThCv9WCwPLf9oqiSGZTkH6dx2jDUPuoXpjkcA==
dependencies:
debug "^4.1.1"
env-paths "^2.2.0"
fs-extra "^8.1.0"
got "^9.6.0"
progress "^2.0.3"
semver "^6.2.0"
sumchecker "^3.0.1"
optionalDependencies:
global-agent "^3.0.0"
global-tunnel-ng "^2.7.1"
"@sindresorhus/is@^0.14.0":
version "0.14.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==
"@szmarczak/http-timer@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421"
integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==
dependencies:
defer-to-connect "^1.0.1"
"@types/node@^14.6.2":
version "14.18.20"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.20.tgz#268f028b36eaf51181c3300252f605488c4f0650"
integrity sha512-Q8KKwm9YqEmUBRsqJ2GWJDtXltBDxTdC4m5vTdXBolu2PeQh8LX+f6BTwU+OuXPu37fLxoN6gidqBmnky36FXA==
async-lock@^1.2.8:
version "1.2.8"
resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.2.8.tgz#7b02bdfa2de603c0713acecd11184cf97bbc7c4c"
integrity sha512-G+26B2jc0Gw0EG/WN2M6IczuGepBsfR1+DtqLnyFSH4p2C668qkOCtEkGNVEaaNAVlYwEMazy1+/jnLxltBkIQ==
boolean@^3.0.1:
version "3.0.2"
resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.0.2.tgz#df1baa18b6a2b0e70840475e1d93ec8fe75b2570"
integrity sha512-RwywHlpCRc3/Wh81MiCKun4ydaIFyW5Ea6JbL6sRCVx5q5irDw7pMXBUFYF/jArQ6YrG36q0kpovc9P/Kd3I4g==
buffer-crc32@~0.2.3:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
buffer-from@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
cacheable-request@^6.0.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912"
integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==
dependencies:
clone-response "^1.0.2"
get-stream "^5.1.0"
http-cache-semantics "^4.0.0"
keyv "^3.0.0"
lowercase-keys "^2.0.0"
normalize-url "^4.1.0"
responselike "^1.0.2"
clone-response@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b"
integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=
dependencies:
mimic-response "^1.0.0"
concat-stream@^1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
dependencies:
buffer-from "^1.0.0"
inherits "^2.0.3"
readable-stream "^2.2.2"
typedarray "^0.0.6"
config-chain@^1.1.11:
version "1.1.12"
resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa"
integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==
dependencies:
ini "^1.3.4"
proto-list "~1.2.1"
core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
debug@^4.1.0, debug@^4.1.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
dependencies:
ms "2.1.2"
decompress-response@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3"
integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=
dependencies:
mimic-response "^1.0.0"
defer-to-connect@^1.0.1:
version "1.1.3"
resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591"
integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==
define-properties@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
dependencies:
object-keys "^1.0.12"
detect-node@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c"
integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==
duplexer3@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=
electron-window-state@^5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/electron-window-state/-/electron-window-state-5.0.3.tgz#4f36d09e3f953d87aff103bf010f460056050aa8"
integrity sha512-1mNTwCfkolXl3kMf50yW3vE2lZj0y92P/HYWFBrb+v2S/pCka5mdwN3cagKm458A7NjndSwijynXgcLWRodsVg==
dependencies:
jsonfile "^4.0.0"
mkdirp "^0.5.1"
electron@16.2.8:
version "16.2.8"
resolved "https://registry.yarnpkg.com/electron/-/electron-16.2.8.tgz#b7f2bd1184701e54a1bc902839d5a3ec95bb8982"
integrity sha512-KSOytY6SPLsh3iCziztqa/WgJyfDOKzCvNqku9gGIqSdT8CqtV66dTU1SOrKZQjRFLxHaF8LbyxUL1vwe4taqw==
dependencies:
"@electron/get" "^1.13.0"
"@types/node" "^14.6.2"
extract-zip "^1.0.3"
encodeurl@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
end-of-stream@^1.1.0:
version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
dependencies:
once "^1.4.0"
env-paths@^2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
es6-error@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
escape-string-regexp@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
extract-zip@^1.0.3:
version "1.7.0"
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927"
integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==
dependencies:
concat-stream "^1.6.2"
debug "^2.6.9"
mkdirp "^0.5.4"
yauzl "^2.10.0"
fd-slicer@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=
dependencies:
pend "~1.2.0"
fs-extra@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^4.0.0"
universalify "^0.1.0"
get-stream@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
dependencies:
pump "^3.0.0"
get-stream@^5.1.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
dependencies:
pump "^3.0.0"
global-agent@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-3.0.0.tgz#ae7cd31bd3583b93c5a16437a1afe27cc33a1ab6"
integrity sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==
dependencies:
boolean "^3.0.1"
es6-error "^4.1.1"
matcher "^3.0.0"
roarr "^2.15.3"
semver "^7.3.2"
serialize-error "^7.0.1"
global-tunnel-ng@^2.7.1:
version "2.7.1"
resolved "https://registry.yarnpkg.com/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz#d03b5102dfde3a69914f5ee7d86761ca35d57d8f"
integrity sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==
dependencies:
encodeurl "^1.0.2"
lodash "^4.17.10"
npm-conf "^1.1.3"
tunnel "^0.0.6"
globalthis@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.2.tgz#2a235d34f4d8036219f7e34929b5de9e18166b8b"
integrity sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==
dependencies:
define-properties "^1.1.3"
got@^9.6.0:
version "9.6.0"
resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85"
integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==
dependencies:
"@sindresorhus/is" "^0.14.0"
"@szmarczak/http-timer" "^1.1.2"
cacheable-request "^6.0.0"
decompress-response "^3.3.0"
duplexer3 "^0.1.4"
get-stream "^4.1.0"
lowercase-keys "^1.0.1"
mimic-response "^1.0.1"
p-cancelable "^1.0.0"
to-readable-stream "^1.0.0"
url-parse-lax "^3.0.0"
graceful-fs@^4.1.6, graceful-fs@^4.2.0:
version "4.2.6"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
http-cache-semantics@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
inherits@^2.0.3, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ini@^1.3.4:
version "1.3.8"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
json-buffer@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=
json-stringify-safe@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
jsonfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
optionalDependencies:
graceful-fs "^4.1.6"
keyv@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9"
integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==
dependencies:
json-buffer "3.0.0"
lodash@^4.17.10:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==
lowercase-keys@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
dependencies:
yallist "^4.0.0"
matcher@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca"
integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==
dependencies:
escape-string-regexp "^4.0.0"
mimic-response@^1.0.0, mimic-response@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
mkdirp@^0.5.1, mkdirp@^0.5.4:
version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
dependencies:
minimist "^1.2.5"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
normalize-url@^4.1.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129"
integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==
npm-conf@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9"
integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==
dependencies:
config-chain "^1.1.11"
pify "^3.0.0"
object-keys@^1.0.12:
version "1.1.1"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
dependencies:
wrappy "1"
p-cancelable@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc"
integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==
pend@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
pify@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
prepend-http@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
progress@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
proto-list@~1.2.1:
version "1.2.4"
resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=
pump@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
dependencies:
end-of-stream "^1.1.0"
once "^1.3.1"
readable-stream@^2.2.2:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
responselike@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7"
integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=
dependencies:
lowercase-keys "^1.0.0"
roarr@^2.15.3:
version "2.15.4"
resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd"
integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==
dependencies:
boolean "^3.0.1"
detect-node "^2.0.4"
globalthis "^1.0.1"
json-stringify-safe "^5.0.1"
semver-compare "^1.0.0"
sprintf-js "^1.1.2"
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
semver-compare@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w=
semver@^6.2.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@^7.3.2:
version "7.3.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97"
integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==
dependencies:
lru-cache "^6.0.0"
serialize-error@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18"
integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==
dependencies:
type-fest "^0.13.1"
sprintf-js@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673"
integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"
sumchecker@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42"
integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==
dependencies:
debug "^4.1.0"
to-readable-stream@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771"
integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==
tunnel@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
type-fest@^0.13.1:
version "0.13.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934"
integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==
typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
url-parse-lax@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c"
integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=
dependencies:
prepend-http "^2.0.0"
util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
yallist@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
yauzl@^2.10.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=
dependencies:
buffer-crc32 "~0.2.3"
fd-slicer "~1.1.0"

View File

@ -1 +0,0 @@
wegame_sdk

View File

@ -1 +0,0 @@
To build, place the lib64 folder from the wegame sdk for electron 13 in `wegame_sdk` and run the `wegame.main.standalone` gulp task.

View File

@ -1,13 +0,0 @@
{
"folders": [
{
"path": "."
}
],
"settings": {
"files.exclude": {
"**/node_modules": true,
"**/typedefs_gen": true
}
}
}

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

@ -1,253 +0,0 @@
/* eslint-disable quotes,no-undef */
const { app, BrowserWindow, Menu, MenuItem, ipcMain, shell } = require("electron");
app.commandLine.appendSwitch("in-process-gpu");
const path = require("path");
const url = require("url");
const fs = require("fs");
const wegame = require("./wegame");
const asyncLock = require("async-lock");
const isDev = process.argv.indexOf("--dev") >= 0;
const isLocal = process.argv.indexOf("--local") >= 0;
const roamingFolder =
process.env.APPDATA ||
(process.platform == "darwin"
? process.env.HOME + "/Library/Preferences"
: process.env.HOME + "/.local/share");
let storePath = path.join(roamingFolder, "shapez.io", "saves");
if (!fs.existsSync(storePath)) {
// No try-catch by design
fs.mkdirSync(storePath, { recursive: true });
}
/** @type {BrowserWindow} */
let win = null;
let menu = null;
function createWindow() {
let faviconExtension = ".png";
if (process.platform === "win32") {
faviconExtension = ".ico";
}
win = new BrowserWindow({
width: 1280,
height: 800,
show: false,
backgroundColor: "#222428",
useContentSize: true,
minWidth: 800,
minHeight: 600,
title: "图形工厂",
transparent: false,
icon: path.join(__dirname, "favicon" + faviconExtension),
// fullscreen: true,
autoHideMenuBar: true,
webPreferences: {
nodeIntegration: false,
webSecurity: true,
sandbox: true,
contextIsolation: true,
preload: path.join(__dirname, "preload.js"),
},
allowRunningInsecureContent: false,
});
if (isLocal) {
win.loadURL("http://localhost:3005");
} else {
win.loadURL(
url.format({
pathname: path.join(__dirname, "index.html"),
protocol: "file:",
slashes: true,
})
);
}
win.webContents.session.clearCache(() => null);
win.webContents.session.clearStorageData();
win.webContents.on("new-window", (event, pth) => {
event.preventDefault();
shell.openExternal(pth);
});
win.on("closed", () => {
console.log("Window closed");
win = null;
});
if (isDev) {
menu = new Menu();
const mainItem = new MenuItem({
label: "Toggle Dev Tools",
click: () => win.webContents.toggleDevTools(),
accelerator: "F12",
});
menu.append(mainItem);
const reloadItem = new MenuItem({
label: "Restart",
click: () => win.reload(),
accelerator: "F5",
});
menu.append(reloadItem);
const fullscreenItem = new MenuItem({
label: "Fullscreen",
click: () => win.setFullScreen(!win.isFullScreen()),
accelerator: "F11",
});
menu.append(fullscreenItem);
Menu.setApplicationMenu(menu);
} else {
Menu.setApplicationMenu(null);
}
win.once("ready-to-show", () => {
win.show();
win.focus();
});
}
if (!app.requestSingleInstanceLock()) {
app.exit(0);
} else {
app.on("second-instance", (event, commandLine, workingDirectory) => {
// Someone tried to run a second instance, we should focus
if (win) {
if (win.isMinimized()) {
win.restore();
}
win.focus();
}
});
}
app.on("ready", createWindow);
app.on("window-all-closed", () => {
console.log("All windows closed");
app.quit();
});
ipcMain.on("set-fullscreen", (event, flag) => {
win.setFullScreen(flag);
});
ipcMain.on("exit-app", (event, flag) => {
win.close();
app.quit();
});
let renameCounter = 1;
const fileLock = new asyncLock({
timeout: 30000,
maxPending: 1000,
});
function niceFileName(filename) {
return filename.replace(storePath, "@");
}
async function writeFileSafe(filename, contents) {
++renameCounter;
const prefix = "[ " + renameCounter + ":" + niceFileName(filename) + " ] ";
const transactionId = String(new Date().getTime()) + "." + renameCounter;
if (fileLock.isBusy()) {
console.warn(prefix, "Concurrent write process on", filename);
}
fileLock.acquire(filename, async () => {
console.log(prefix, "Starting write on", niceFileName(filename), "in transaction", transactionId);
if (!fs.existsSync(filename)) {
// this one is easy
console.log(prefix, "Writing file instantly because it does not exist:", niceFileName(filename));
await fs.promises.writeFile(filename, contents, "utf8");
return;
}
// first, write a temporary file (.tmp-XXX)
const tempName = filename + ".tmp-" + transactionId;
console.log(prefix, "Writing temporary file", niceFileName(tempName));
await fs.promises.writeFile(tempName, contents, "utf8");
// now, rename the original file to (.backup-XXX)
const oldTemporaryName = filename + ".backup-" + transactionId;
console.log(
prefix,
"Renaming old file",
niceFileName(filename),
"to",
niceFileName(oldTemporaryName)
);
await fs.promises.rename(filename, oldTemporaryName);
// now, rename the temporary file (.tmp-XXX) to the target
console.log(
prefix,
"Renaming the temporary file",
niceFileName(tempName),
"to the original",
niceFileName(filename)
);
await fs.promises.rename(tempName, filename);
// we are done now, try to create a backup, but don't fail if the backup fails
try {
// check if there is an old backup file
const backupFileName = filename + ".backup";
if (fs.existsSync(backupFileName)) {
console.log(prefix, "Deleting old backup file", niceFileName(backupFileName));
// delete the old backup
await fs.promises.unlink(backupFileName);
}
// rename the old file to the new backup file
console.log(prefix, "Moving", niceFileName(oldTemporaryName), "to the backup file location");
await fs.promises.rename(oldTemporaryName, backupFileName);
} catch (ex) {
console.error(prefix, "Failed to switch backup files:", ex);
}
});
}
ipcMain.handle("fs-job", async (event, job) => {
const filenameSafe = job.filename.replace(/[^a-z\.\-_0-9]/i, "");
const fname = path.join(storePath, filenameSafe);
switch (job.type) {
case "read": {
if (!fs.existsSync(fname)) {
// Special FILE_NOT_FOUND error code
return { error: "file_not_found" };
}
return await fs.promises.readFile(fname, "utf8");
}
case "write": {
await writeFileSafe(fname, job.contents);
return job.contents;
}
case "delete": {
await fs.promises.unlink(fname);
return;
}
default:
throw new Error("Unknown fs job: " + job.type);
}
});
wegame.init(isDev);
wegame.listen();

View File

@ -1,18 +0,0 @@
{
"name": "electron",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"private": true,
"scripts": {
"startDev": "electron --disable-direct-composition --in-process-gpu . --dev --local",
"startDevGpu": "electron --enable-gpu-rasterization --enable-accelerated-2d-canvas --num-raster-threads=8 --enable-zero-copy . --dev --local",
"start": "electron --disable-direct-composition --in-process-gpu ."
},
"devDependencies": {
"electron": "^13.1.6"
},
"dependencies": {
"async-lock": "^1.2.8"
}
}

View File

@ -1,63 +0,0 @@
const railsdk = require("./wegame_sdk/railsdk.js");
const { dialog, app, remote, ipcMain } = require("electron");
function init(isDev) {
console.log("Step 1: wegame: init");
try {
console.log("Step 2: Calling need restart app");
const need_restart = railsdk.RailNeedRestartAppForCheckingEnvironment(
2001639,
[`--rail_render_pid=${process.pid}`] //,"--rail_debug_mode",
);
console.log("Step 3: Needs restart =", need_restart);
if (need_restart) {
console.error("Step 4: Need restart");
dialog.showErrorBox("加载RailSDK失败", "请先运行WeGame开发者版本");
return;
}
} catch (err) {
console.error("Rail SDK error:", err);
dialog.showErrorBox("加载RailSDK失败", err);
return;
}
console.log("Step 5: starting rail sdk");
if (railsdk.RailInitialize() === false) {
console.error("RailInitialize() = false");
dialog.showErrorBox("RailInitialize调用失败", "请先运行WeGame开发者版本");
return;
}
console.log("Initialize RailSDK success!");
railsdk.RailRegisterEvent(railsdk.RailEventID.kRailEventSystemStateChanged, event => {
console.log(event);
if (event.result === railsdk.RailResult.kSuccess) {
if (
event.state === railsdk.RailSystemState.kSystemStatePlatformOffline ||
event.state === railsdk.RailSystemState.kSystemStatePlatformExit ||
event.state === railsdk.RailSystemState.kSystemStateGameExitByAntiAddiction
) {
app.exit();
}
}
});
}
function listen() {
console.log("wegame: listen");
ipcMain.handle("profanity-check", async (event, data) => {
if (data.length === 0) {
return "";
}
const result = railsdk.RailUtils.DirtyWordsFilter(data, true);
if (result.check_result.dirty_type !== 0 /** kRailDirtyWordsTypeNormalAllowWords */) {
return result.check_result.replace_string;
}
return data;
});
}
module.exports = { init, listen };

View File

@ -1,578 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@electron/get@^1.0.1":
version "1.12.4"
resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.12.4.tgz#a5971113fc1bf8fa12a8789dc20152a7359f06ab"
integrity sha512-6nr9DbJPUR9Xujw6zD3y+rS95TyItEVM0NVjt1EehY2vUWfIgPiIPVHxCvaTS0xr2B+DRxovYVKbuOWqC35kjg==
dependencies:
debug "^4.1.1"
env-paths "^2.2.0"
fs-extra "^8.1.0"
got "^9.6.0"
progress "^2.0.3"
semver "^6.2.0"
sumchecker "^3.0.1"
optionalDependencies:
global-agent "^2.0.2"
global-tunnel-ng "^2.7.1"
"@sindresorhus/is@^0.14.0":
version "0.14.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==
"@szmarczak/http-timer@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421"
integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==
dependencies:
defer-to-connect "^1.0.1"
"@types/node@^14.6.2":
version "14.17.4"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.4.tgz#218712242446fc868d0e007af29a4408c7765bc0"
integrity sha512-8kQ3+wKGRNN0ghtEn7EGps/B8CzuBz1nXZEIGGLP2GnwbqYn4dbTs7k+VKLTq1HvZLRCIDtN3Snx1Ege8B7L5A==
async-lock@^1.2.8:
version "1.2.8"
resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.2.8.tgz#7b02bdfa2de603c0713acecd11184cf97bbc7c4c"
integrity sha512-G+26B2jc0Gw0EG/WN2M6IczuGepBsfR1+DtqLnyFSH4p2C668qkOCtEkGNVEaaNAVlYwEMazy1+/jnLxltBkIQ==
boolean@^3.0.1:
version "3.1.2"
resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.1.2.tgz#e30f210a26b02458482a8cc353ab06f262a780c2"
integrity sha512-YN6UmV0FfLlBVvRvNPx3pz5W/mUoYB24J4WSXOKP/OOJpi+Oq6WYqPaNTHzjI0QzwWtnvEd5CGYyQPgp1jFxnw==
buffer-crc32@~0.2.3:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
buffer-from@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
cacheable-request@^6.0.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912"
integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==
dependencies:
clone-response "^1.0.2"
get-stream "^5.1.0"
http-cache-semantics "^4.0.0"
keyv "^3.0.0"
lowercase-keys "^2.0.0"
normalize-url "^4.1.0"
responselike "^1.0.2"
clone-response@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b"
integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=
dependencies:
mimic-response "^1.0.0"
concat-stream@^1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
dependencies:
buffer-from "^1.0.0"
inherits "^2.0.3"
readable-stream "^2.2.2"
typedarray "^0.0.6"
config-chain@^1.1.11:
version "1.1.13"
resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4"
integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==
dependencies:
ini "^1.3.4"
proto-list "~1.2.1"
core-js@^3.6.5:
version "3.15.2"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.15.2.tgz#740660d2ff55ef34ce664d7e2455119c5bdd3d61"
integrity sha512-tKs41J7NJVuaya8DxIOCnl8QuPHx5/ZVbFo1oKgVl1qHFBBrDctzQGtuLjPpRdNTWmKPH6oEvgN/MUID+l485Q==
core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
debug@^4.1.0, debug@^4.1.1:
version "4.3.2"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
dependencies:
ms "2.1.2"
decompress-response@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3"
integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=
dependencies:
mimic-response "^1.0.0"
defer-to-connect@^1.0.1:
version "1.1.3"
resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591"
integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==
define-properties@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
dependencies:
object-keys "^1.0.12"
detect-node@^2.0.4:
version "2.1.0"
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==
duplexer3@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=
electron@^13.1.6:
version "13.1.6"
resolved "https://registry.yarnpkg.com/electron/-/electron-13.1.6.tgz#6ecaf969255d62ce82cc0b5c948bf26e7dfb489b"
integrity sha512-XiB55/JTaQpDFQrD9pulYnOGwaWeMyRIub5ispvoE2bWBvM5zVMLptwMLb0m3KTMrfSkzhedZvOu7fwYvR7L7Q==
dependencies:
"@electron/get" "^1.0.1"
"@types/node" "^14.6.2"
extract-zip "^1.0.3"
encodeurl@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
end-of-stream@^1.1.0:
version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
dependencies:
once "^1.4.0"
env-paths@^2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
es6-error@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
escape-string-regexp@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
extract-zip@^1.0.3:
version "1.7.0"
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927"
integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==
dependencies:
concat-stream "^1.6.2"
debug "^2.6.9"
mkdirp "^0.5.4"
yauzl "^2.10.0"
fd-slicer@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=
dependencies:
pend "~1.2.0"
fs-extra@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^4.0.0"
universalify "^0.1.0"
get-stream@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
dependencies:
pump "^3.0.0"
get-stream@^5.1.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
dependencies:
pump "^3.0.0"
global-agent@^2.0.2:
version "2.2.0"
resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-2.2.0.tgz#566331b0646e6bf79429a16877685c4a1fbf76dc"
integrity sha512-+20KpaW6DDLqhG7JDiJpD1JvNvb8ts+TNl7BPOYcURqCrXqnN1Vf+XVOrkKJAFPqfX+oEhsdzOj1hLWkBTdNJg==
dependencies:
boolean "^3.0.1"
core-js "^3.6.5"
es6-error "^4.1.1"
matcher "^3.0.0"
roarr "^2.15.3"
semver "^7.3.2"
serialize-error "^7.0.1"
global-tunnel-ng@^2.7.1:
version "2.7.1"
resolved "https://registry.yarnpkg.com/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz#d03b5102dfde3a69914f5ee7d86761ca35d57d8f"
integrity sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==
dependencies:
encodeurl "^1.0.2"
lodash "^4.17.10"
npm-conf "^1.1.3"
tunnel "^0.0.6"
globalthis@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.2.tgz#2a235d34f4d8036219f7e34929b5de9e18166b8b"
integrity sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==
dependencies:
define-properties "^1.1.3"
got@^9.6.0:
version "9.6.0"
resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85"
integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==
dependencies:
"@sindresorhus/is" "^0.14.0"
"@szmarczak/http-timer" "^1.1.2"
cacheable-request "^6.0.0"
decompress-response "^3.3.0"
duplexer3 "^0.1.4"
get-stream "^4.1.0"
lowercase-keys "^1.0.1"
mimic-response "^1.0.1"
p-cancelable "^1.0.0"
to-readable-stream "^1.0.0"
url-parse-lax "^3.0.0"
graceful-fs@^4.1.6, graceful-fs@^4.2.0:
version "4.2.6"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
http-cache-semantics@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
inherits@^2.0.3, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ini@^1.3.4:
version "1.3.8"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
json-buffer@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=
json-stringify-safe@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
jsonfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
optionalDependencies:
graceful-fs "^4.1.6"
keyv@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9"
integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==
dependencies:
json-buffer "3.0.0"
lodash@^4.17.10:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==
lowercase-keys@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
dependencies:
yallist "^4.0.0"
matcher@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca"
integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==
dependencies:
escape-string-regexp "^4.0.0"
mimic-response@^1.0.0, mimic-response@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
mkdirp@^0.5.4:
version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
dependencies:
minimist "^1.2.5"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
normalize-url@^4.1.0:
version "4.5.1"
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a"
integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==
npm-conf@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9"
integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==
dependencies:
config-chain "^1.1.11"
pify "^3.0.0"
object-keys@^1.0.12:
version "1.1.1"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
dependencies:
wrappy "1"
p-cancelable@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc"
integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==
pend@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
pify@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
prepend-http@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
progress@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
proto-list@~1.2.1:
version "1.2.4"
resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=
pump@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
dependencies:
end-of-stream "^1.1.0"
once "^1.3.1"
readable-stream@^2.2.2:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
responselike@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7"
integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=
dependencies:
lowercase-keys "^1.0.0"
roarr@^2.15.3:
version "2.15.4"
resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd"
integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==
dependencies:
boolean "^3.0.1"
detect-node "^2.0.4"
globalthis "^1.0.1"
json-stringify-safe "^5.0.1"
semver-compare "^1.0.0"
sprintf-js "^1.1.2"
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
semver-compare@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w=
semver@^6.2.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@^7.3.2:
version "7.3.5"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
dependencies:
lru-cache "^6.0.0"
serialize-error@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18"
integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==
dependencies:
type-fest "^0.13.1"
sprintf-js@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673"
integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"
sumchecker@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42"
integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==
dependencies:
debug "^4.1.0"
to-readable-stream@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771"
integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==
tunnel@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
type-fest@^0.13.1:
version "0.13.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934"
integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==
typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
url-parse-lax@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c"
integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=
dependencies:
prepend-http "^2.0.0"
util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
yallist@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
yauzl@^2.10.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=
dependencies:
buffer-crc32 "~0.2.3"
fd-slicer "~1.1.0"

61
eslint.config.js Normal file
View File

@ -0,0 +1,61 @@
import eslint from "@eslint/js";
import globals from "globals";
import tseslint from "typescript-eslint";
const baseConfig = tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
// Enable type-aware linting
{
languageOptions: {
parserOptions: {
project: true,
tsconfigRootDir: import.meta.dirname,
},
},
},
// Disable type-aware linting for JS files as it causes issues
{
files: ["*.js"],
...tseslint.configs.disableTypeChecked,
}
);
const nodeConfig = tseslint.config(...baseConfig, {
languageOptions: {
sourceType: "module",
globals: {
...globals.node,
},
},
});
const runtimeConfig = tseslint.config(...baseConfig, {
languageOptions: {
sourceType: "module",
globals: {
...globals.browser,
},
},
rules: {
// Mostly caused by JSDoc imports, disable for now
"@typescript-eslint/no-unused-vars": "off",
// FIXME: enforce when we're ready to
"prefer-const": "warn",
},
});
// I don't know what the ESLint devs were thinking about. This is just horrible
export default [
{
ignores: ["build/*"],
},
...nodeConfig.map(config => ({
...config,
files: ["*.{ts,js}", "{gulp,electron}/**/*.{ts,js}"],
})),
...runtimeConfig.map(config => ({
...config,
files: ["src/**/*.{ts,js,tsx,jsx}"],
})),
];

View File

@ -1,3 +0,0 @@
{
"presets": ["es2015"]
}

1
gulp/.gitignore vendored
View File

@ -1 +0,0 @@
additional_build_files

View File

@ -1,13 +1,13 @@
const { join, resolve } = require("path");
const { readFileSync, readdirSync, writeFileSync } = require("fs");
import { join, resolve } from "path/posix";
import { readFileSync, readdirSync, writeFileSync } from "fs";
const suffixToScale = {
lq: "0.25",
mq: "0.5",
hq: "0.75"
hq: "0.75",
};
function convert(srcDir) {
export default function convert(srcDir) {
const full = resolve(srcDir);
const srcFiles = readdirSync(full)
.filter(n => n.endsWith(".atlas"))
@ -65,7 +65,7 @@ function convert(srcDir) {
x: xy[0],
y: xy[1],
w: size[0],
h: size[1]
h: size[1],
},
// Whether image was rotated
@ -75,21 +75,21 @@ function convert(srcDir) {
// How is the image trimmed
spriteSourceSize: {
x: offset[0],
y: (orig[1] - size[1]) - offset[1],
y: orig[1] - size[1] - offset[1],
w: size[0],
h: size[1]
h: size[1],
},
sourceSize: {
w: orig[0],
h: orig[1]
}
}
h: orig[1],
},
};
}
// Simple object that will hold other metadata
current = {
name: line
name: line,
};
} else {
// Read and set current image metadata
@ -108,20 +108,14 @@ function convert(srcDir) {
format: srcMeta.format,
size: {
w: atlasSize[0],
h: atlasSize[1]
h: atlasSize[1],
},
scale: atlasScale.toString()
}
scale: atlasScale.toString(),
},
});
writeFileSync(atlas.replace(".atlas", ".json"), result, {
encoding: "utf-8"
encoding: "utf-8",
});
}
}
if (require.main == module) {
convert(process.argv[2]);
}
module.exports = { convert };

View File

@ -1,47 +0,0 @@
module.exports = function (api) {
api.cache(true);
const presets = [
[
"@babel/preset-env",
{
targets: "> 5%",
useBuiltIns: "usage",
corejs: 3,
loose: true,
spec: false,
modules: "auto",
},
],
];
const plugins = [
"closure-elimination",
// var is faster than let and const!
[
"@babel/plugin-transform-block-scoping",
{
throwIfClosureRequired: false,
},
],
[
"@babel/plugin-transform-classes",
{
loose: true,
},
],
];
return {
presets,
plugins,
highlightCode: true,
sourceType: "module",
sourceMaps: false,
parserOpts: {},
only: ["../src/js"],
generatorOpts: {
retainLines: false,
compact: true,
minified: true,
comments: true,
},
};
};

View File

@ -1,55 +0,0 @@
module.exports = function (api) {
api.cache(true);
const presets = [
[
"@babel/preset-env",
{
// targets: ">0.01%",
targets: {
edge: 10,
firefox: 37,
chrome: 24,
safari: 10,
ie: 10,
},
useBuiltIns: "usage",
corejs: 3,
loose: true,
spec: false,
modules: "auto",
},
],
];
const plugins = [
"@babel/plugin-transform-arrow-functions",
"closure-elimination",
// var is faster than let and const!
// [
// "@babel/plugin-transform-block-scoping",
// {
// throwIfClosureRequired: true,
// },
// ],
[
"@babel/plugin-transform-classes",
{
loose: true,
},
],
];
return {
presets,
plugins,
highlightCode: true,
sourceType: "unambiguous",
sourceMaps: false,
parserOpts: {},
exclude: /(core-js|babel-core|babel-runtime)/,
generatorOpts: {
retainLines: false,
compact: true,
minified: true,
comments: true,
},
};
};

View File

@ -1,74 +1,10 @@
/**
* @type {Record<string, {
* standalone: boolean,
* environment?: 'dev' | 'staging' | 'prod',
* electronBaseDir?: string,
* steamAppId?: number,
* executableName?: string,
* buildArgs: {
* chineseVersion?: boolean,
* wegameVersion?: boolean,
* steamDemo?: boolean,
* gogVersion?: boolean
* }}>}
* standalone: boolean
* }>}
*/
const BUILD_VARIANTS = {
"web-localhost": {
standalone: false,
environment: "dev",
buildArgs: {},
},
"web-shapezio-beta": {
standalone: false,
environment: "staging",
buildArgs: {},
},
"web-shapezio": {
standalone: false,
environment: "prod",
buildArgs: {},
},
"standalone-steam": {
export const BUILD_VARIANTS = {
standalone: {
standalone: true,
executableName: "shapez",
steamAppId: 1318690,
buildArgs: {},
},
"standalone-steam-china": {
standalone: true,
steamAppId: 1318690,
buildArgs: {
chineseVersion: true,
},
},
"standalone-steam-demo": {
standalone: true,
steamAppId: 1930750,
buildArgs: {
steamDemo: true,
},
},
"standalone-steam-china-demo": {
standalone: true,
steamAppId: 1930750,
buildArgs: {
steamDemo: true,
chineseVersion: true,
},
},
"standalone-wegame": {
standalone: true,
electronBaseDir: "electron_wegame",
buildArgs: {
wegameVersion: true,
},
},
"standalone-gog": {
standalone: true,
electronBaseDir: "electron_gog",
buildArgs: {
gogVersion: true,
},
},
};
module.exports = { BUILD_VARIANTS };

View File

@ -1,47 +1,22 @@
const glob = require("glob");
const execSync = require("child_process").execSync;
const trim = require("trim");
const fs = require("fs");
const path = require("path");
import { execSync } from "child_process";
import fs from "fs";
module.exports = {
getRevision: function (useLast = false) {
const commitHash = execSync("git rev-parse --short " + (useLast ? "HEAD^1" : "HEAD")).toString(
"ascii"
);
return commitHash.replace(/^\s+|\s+$/g, "");
},
export function getRevision(useLast = false) {
const commitHash = execSync("git rev-parse --short " + (useLast ? "HEAD^1" : "HEAD")).toString("ascii");
return commitHash.trim();
}
getAllResourceImages() {
return glob
.sync("res/**/*.@(png|svg|jpg)", { cwd: ".." })
.map(f => f.replace(/^res\//gi, ""))
.filter(f => {
if (f.indexOf("ui") >= 0) {
// We drop all ui images except for the noinline ones
return f.indexOf("noinline") >= 0;
}
return true;
});
},
getTag() {
try {
return execSync("git describe --tag --exact-match").toString("ascii");
} catch (e) {
throw new Error("Current git HEAD is not a version tag");
export function getAllResourceImages() {
return fs.globSync("./**/*.@(png|svg|jpg)", { cwd: "../res" }).filter(f => {
if (f.indexOf("ui") >= 0) {
// We drop all ui images except for the noinline ones
return f.indexOf("noinline") >= 0;
}
},
return true;
});
}
getVersion() {
return trim(fs.readFileSync(path.join(__dirname, "..", "version")).toString());
},
/**
* @param {string} url
* @param {string} commitHash
*/
cachebust(url, commitHash) {
return "/v/" + commitHash + "/" + url;
},
};
export function getVersion() {
// Use the version number specified in package.json
return JSON.parse(fs.readFileSync("../package.json", "utf-8")).version;
}

23
gulp/config.js Normal file
View File

@ -0,0 +1,23 @@
import BrowserSync from "browser-sync";
import path from "path/posix";
export const baseDir = path.resolve("..");
export const buildFolder = path.join(baseDir, "build");
export const buildOutputFolder = path.join(baseDir, "build_output");
export const generatedCodeFolder = path.join(baseDir, "src/js/built-temp");
// Globs for atlas resources
export const rawImageResourcesGlobs = ["../res_raw/atlas.json", "../res_raw/**/*.png"];
// Globs for non-ui resources
export const nonImageResourcesGlobs = ["../res/**/*.woff2", "../res/*.ico", "../res/**/*.webm"];
// Globs for ui resources
export const imageResourcesGlobs = [
"../res/**/*.png",
"../res/**/*.svg",
"../res/**/*.jpg",
"../res/**/*.gif",
];
export const browserSync = BrowserSync.create();

View File

@ -1,136 +1,97 @@
const path = require("path");
const buildUtils = require("./buildutils");
function gulptasksCSS($, gulp, buildFolder, browserSync) {
// The assets plugin copies the files
const commitHash = buildUtils.getRevision();
const postcssAssetsPlugin = cachebust =>
$.postcssAssets({
loadPaths: [path.join(buildFolder, "res", "ui")],
basePath: buildFolder,
baseUrl: ".",
cachebuster: cachebust
? (filePath, urlPathname) => ({
pathname: buildUtils.cachebust(urlPathname, commitHash),
})
: "",
});
// Postcss configuration
const postcssPlugins = (prod, { cachebust = false }) => {
const plugins = [postcssAssetsPlugin(cachebust)];
if (prod) {
plugins.unshift(
$.postcssPresetEnv({
browsers: ["> 0.1%"],
})
);
plugins.push(
$.cssMqpacker({
sort: true,
}),
$.cssnano({
preset: [
"advanced",
{
cssDeclarationSorter: false,
discardUnused: true,
mergeIdents: false,
reduceIdents: true,
zindex: true,
},
],
}),
$.postcssRoundSubpixels()
);
}
return plugins;
};
// Performs linting on css
gulp.task("css.lint", () => {
return gulp
.src(["../src/css/**/*.scss"])
.pipe($.sassLint({ configFile: ".sasslint.yml" }))
.pipe($.sassLint.format())
.pipe($.sassLint.failOnError());
});
function resourcesTask({ cachebust, isProd }) {
return gulp
.src("../src/css/main.scss", { cwd: __dirname })
.pipe($.plumber())
.pipe($.dartSass.sync().on("error", $.dartSass.logError))
.pipe(
$.postcss([
$.postcssCriticalSplit({
blockTag: "@load-async",
}),
])
)
.pipe($.rename("async-resources.css"))
.pipe($.postcss(postcssPlugins(isProd, { cachebust })))
.pipe(gulp.dest(buildFolder))
.pipe(browserSync.stream());
}
// Builds the css resources
gulp.task("css.resources.dev", () => {
return resourcesTask({ cachebust: false, isProd: false });
});
// Builds the css resources in prod (=minified)
gulp.task("css.resources.prod", () => {
return resourcesTask({ cachebust: true, isProd: true });
});
// Builds the css resources in prod (=minified), without cachebusting
gulp.task("css.resources.prod-standalone", () => {
return resourcesTask({ cachebust: false, isProd: true });
});
function mainTask({ cachebust, isProd }) {
return gulp
.src("../src/css/main.scss", { cwd: __dirname })
.pipe($.plumber())
.pipe($.dartSass.sync().on("error", $.dartSass.logError))
.pipe(
$.postcss([
$.postcssCriticalSplit({
blockTag: "@load-async",
output: "rest",
}),
])
)
.pipe($.postcss(postcssPlugins(isProd, { cachebust })))
.pipe(gulp.dest(buildFolder))
.pipe(browserSync.stream());
}
// Builds the css main
gulp.task("css.main.dev", () => {
return mainTask({ cachebust: false, isProd: false });
});
// Builds the css main in prod (=minified)
gulp.task("css.main.prod", () => {
return mainTask({ cachebust: true, isProd: true });
});
// Builds the css main in prod (=minified), without cachebusting
gulp.task("css.main.prod-standalone", () => {
return mainTask({ cachebust: false, isProd: true });
});
gulp.task("css.dev", gulp.parallel("css.main.dev", "css.resources.dev"));
gulp.task("css.prod", gulp.parallel("css.main.prod", "css.resources.prod"));
gulp.task(
"css.prod-standalone",
gulp.parallel("css.main.prod-standalone", "css.resources.prod-standalone")
);
}
module.exports = {
gulptasksCSS,
};
import gulp from "gulp";
import path from "path/posix";
import { buildFolder } from "./config.js";
import cssMqpacker from "css-mqpacker";
import cssnano from "cssnano";
import gulpDartSass from "gulp-dart-sass";
import gulpPlumber from "gulp-plumber";
import gulpPostcss from "gulp-postcss";
import gulpRename from "gulp-rename";
import postcssAssets from "postcss-assets";
import postcssCriticalSplit from "postcss-critical-split";
// The assets plugin copies the files
const postcssAssetsPlugin = postcssAssets({
loadPaths: [path.join(buildFolder, "res", "ui")],
basePath: buildFolder,
baseUrl: ".",
});
// Postcss configuration
const postcssPlugins = prod => {
const plugins = [postcssAssetsPlugin];
if (prod) {
plugins.push(
cssMqpacker({
sort: true,
}),
cssnano({
preset: [
"advanced",
{
cssDeclarationSorter: false,
discardUnused: true,
mergeIdents: false,
reduceIdents: true,
zindex: true,
},
],
})
);
}
return plugins;
};
function resourcesTask({ isProd }) {
return gulp
.src("../src/css/main.scss")
.pipe(gulpPlumber())
.pipe(gulpDartSass.sync().on("error", gulpDartSass.logError))
.pipe(
gulpPostcss([
postcssCriticalSplit({
blockTag: "@load-async",
}),
])
)
.pipe(gulpRename("async-resources.css"))
.pipe(gulpPostcss(postcssPlugins(isProd)))
.pipe(gulp.dest(buildFolder));
}
export const resources = {
// Builds the css resources
dev: () => resourcesTask({ isProd: false }),
// Builds the css resources in prod (=minified)
prod: () => resourcesTask({ isProd: true }),
};
function mainTask({ isProd }) {
return gulp
.src("../src/css/main.scss")
.pipe(gulpPlumber())
.pipe(gulpDartSass.sync().on("error", gulpDartSass.logError))
.pipe(
gulpPostcss([
postcssCriticalSplit({
blockTag: "@load-async",
output: "rest",
}),
])
)
.pipe(gulpPostcss(postcssPlugins(isProd)))
.pipe(gulp.dest(buildFolder));
}
export const main = {
// Builds the css main
dev: () => mainTask({ isProd: false }),
// Builds the css main in prod (=minified)
prod: () => mainTask({ isProd: true }),
};
export const dev = gulp.parallel(main.dev, resources.dev);
export const prod = gulp.parallel(main.prod, resources.prod);

View File

@ -1,39 +0,0 @@
const path = require("path");
const fs = require("fs");
function gulptasksDocs($, gulp, buildFolder) {
gulp.task("docs.convertJsToTs", () => {
return gulp
.src(path.join("..", "src", "js", "**", "*.js"))
.pipe(
$.rename(path => {
path.extname = ".ts";
})
)
.pipe(gulp.dest(path.join("..", "tsc_temp")));
});
gulp.task("docs.copyTsconfigForHints", cb => {
const src = fs.readFileSync(path.join("..", "src", "js", "tsconfig.json")).toString();
const baseConfig = JSON.parse($.stripJsonComments(src));
baseConfig.allowJs = false;
baseConfig.checkJs = false;
baseConfig.declaration = true;
baseConfig.noEmit = false;
baseConfig.strict = false;
baseConfig.strictFunctionTypes = false;
baseConfig.strictBindCallApply = false;
baseConfig.alwaysStrict = false;
baseConfig.composite = true;
baseConfig.outFile = "bundled-ts.js";
fs.writeFileSync(path.join("..", "tsc_temp", "tsconfig.json"), JSON.stringify(baseConfig));
cb();
});
gulp.task("main.prepareDocs", gulp.series("docs.convertJsToTs", "docs.copyTsconfigForHints"));
}
module.exports = {
gulptasksDocs,
};

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.debugger</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
<key>com.apple.security.cs.disable-executable-page-protection</key>
<true/>
</dict>
</plist>

49
gulp/environment.js Normal file
View File

@ -0,0 +1,49 @@
import { exec } from "child_process";
import fs from "fs/promises";
import gulp from "gulp";
import { promisify } from "util";
const texturePackerUrl =
"https://libgdx-nightlies.s3.amazonaws.com/libgdx-runnables/runnable-texturepacker.jar";
const configTemplatePath = "../src/js/core/config.local.template.js";
const configPath = "../src/js/core/config.local.js";
export async function checkJava() {
try {
const { stderr } = await promisify(exec)("java -version");
console.log(`Found Java:`, stderr);
} catch {
throw new Error("Java is required to build the texture atlas, but was not found");
}
}
export async function downloadTexturePacker() {
const destination = "./runnable-texturepacker.jar";
try {
// If the file exists already, we're done
await fs.access(destination);
return;
} catch {
// File does not exist, need to download
}
console.log(`Downloading ${destination}...`);
const response = await fetch(texturePackerUrl);
if (!response.ok) {
throw new Error(`Failed to download Texture Packer: ${response.statusText}`);
}
await fs.writeFile(destination, response.body);
}
export async function createLocalConfig() {
try {
await fs.copyFile(configTemplatePath, configPath, fs.constants.COPYFILE_EXCL);
} catch {
// The file is already there
}
}
export const prepare = gulp.parallel(gulp.series(checkJava, downloadTexturePacker), createLocalConfig);

View File

@ -1,101 +0,0 @@
const path = require("path");
const fs = require("fs");
const buildUtils = require("./buildutils");
function gulptasksFTP($, gulp, buildFolder) {
const commitHash = buildUtils.getRevision();
const additionalFolder = path.join(__dirname, "additional_build_files");
const additionalFiles = [
path.join(additionalFolder, "*"),
path.join(additionalFolder, "*.*"),
path.join(additionalFolder, ".*"),
];
const credentials = {
alpha: {
host: process.env.SHAPEZ_CLI_SERVER_HOST,
user: process.env.SHAPEZ_CLI_ALPHA_FTP_USER,
pass: process.env.SHAPEZ_CLI_ALPHA_FTP_PW,
},
staging: {
host: process.env.SHAPEZ_CLI_SERVER_HOST,
user: process.env.SHAPEZ_CLI_STAGING_FTP_USER,
pass: process.env.SHAPEZ_CLI_STAGING_FTP_PW,
},
prod: {
host: process.env.SHAPEZ_CLI_SERVER_HOST,
user: process.env.SHAPEZ_CLI_LIVE_FTP_USER,
pass: process.env.SHAPEZ_CLI_LIVE_FTP_PW,
},
};
// Write the "commit.txt" file
gulp.task("ftp.writeVersion", cb => {
fs.writeFileSync(
path.join(buildFolder, "version.json"),
JSON.stringify(
{
commit: buildUtils.getRevision(),
appVersion: buildUtils.getVersion(),
buildTime: new Date().getTime(),
},
null,
4
)
);
cb();
});
const gameSrcGlobs = [
path.join(buildFolder, "**/*.*"),
path.join(buildFolder, "**/.*"),
path.join(buildFolder, "**/*"),
path.join(buildFolder, "!**/index.html"),
];
for (const deployEnv of ["alpha", "prod", "staging"]) {
const deployCredentials = credentials[deployEnv];
gulp.task(`ftp.upload.${deployEnv}.game`, () => {
return gulp
.src(gameSrcGlobs, { base: buildFolder })
.pipe(
$.rename(pth => {
pth.dirname = path.join("v", commitHash, pth.dirname);
})
)
.pipe($.sftp(deployCredentials));
});
gulp.task(`ftp.upload.${deployEnv}.indexHtml`, () => {
return gulp
.src([path.join(buildFolder, "index.html"), path.join(buildFolder, "version.json")], {
base: buildFolder,
})
.pipe($.sftp(deployCredentials));
});
gulp.task(`ftp.upload.${deployEnv}.additionalFiles`, () => {
return gulp
.src(additionalFiles, { base: additionalFolder }) //
.pipe($.sftp(deployCredentials));
});
gulp.task(
`ftp.upload.${deployEnv}`,
gulp.series(
"ftp.writeVersion",
`ftp.upload.${deployEnv}.game`,
`ftp.upload.${deployEnv}.indexHtml`,
`ftp.upload.${deployEnv}.additionalFiles`
)
);
}
}
module.exports = {
gulptasksFTP,
};

View File

@ -1,317 +1,23 @@
/* eslint-disable */
require("colors");
const gulp = require("gulp");
const browserSync = require("browser-sync").create({});
const path = require("path");
const deleteEmpty = require("delete-empty");
const execSync = require("child_process").execSync;
// Load other plugins dynamically
const $ = require("gulp-load-plugins")({
scope: ["devDependencies"],
pattern: "*",
});
// Check environment variables
const envVars = [
"SHAPEZ_CLI_SERVER_HOST",
"SHAPEZ_CLI_ALPHA_FTP_USER",
"SHAPEZ_CLI_ALPHA_FTP_PW",
"SHAPEZ_CLI_STAGING_FTP_USER",
"SHAPEZ_CLI_STAGING_FTP_PW",
"SHAPEZ_CLI_LIVE_FTP_USER",
"SHAPEZ_CLI_LIVE_FTP_PW",
"SHAPEZ_CLI_APPLE_ID",
"SHAPEZ_CLI_APPLE_CERT_NAME",
"SHAPEZ_CLI_GITHUB_USER",
"SHAPEZ_CLI_GITHUB_TOKEN",
];
for (let i = 0; i < envVars.length; ++i) {
if (!process.env[envVars[i]]) {
console.warn("Unset environment variable, might cause issues:", envVars[i]);
}
}
const baseDir = path.join(__dirname, "..");
const buildFolder = path.join(baseDir, "build");
const buildOuptutFolder = path.join(baseDir, "build_output");
const imgres = require("./image-resources");
imgres.gulptasksImageResources($, gulp, buildFolder);
const css = require("./css");
css.gulptasksCSS($, gulp, buildFolder, browserSync);
const sounds = require("./sounds");
sounds.gulptasksSounds($, gulp, buildFolder);
const localConfig = require("./local-config");
localConfig.gulptasksLocalConfig($, gulp);
const js = require("./js");
js.gulptasksJS($, gulp, buildFolder, browserSync);
const html = require("./html");
html.gulptasksHTML($, gulp, buildFolder);
const ftp = require("./ftp");
ftp.gulptasksFTP($, gulp, buildFolder);
const docs = require("./docs");
docs.gulptasksDocs($, gulp, buildFolder);
const standalone = require("./standalone");
standalone.gulptasksStandalone($, gulp);
const translations = require("./translations");
const { BUILD_VARIANTS } = require("./build_variants");
translations.gulptasksTranslations($, gulp);
///////////////////// BUILD TASKS /////////////////////
// Cleans up everything
gulp.task("utils.cleanBuildFolder", () => {
return gulp.src(buildFolder, { read: false, allowEmpty: true }).pipe($.clean({ force: true }));
});
gulp.task("utils.cleanBuildOutputFolder", () => {
return gulp.src(buildOuptutFolder, { read: false, allowEmpty: true }).pipe($.clean({ force: true }));
});
gulp.task("utils.cleanBuildTempFolder", () => {
return gulp
.src(path.join(__dirname, "..", "src", "js", "built-temp"), { read: false, allowEmpty: true })
.pipe($.clean({ force: true }));
});
gulp.task("utils.cleanImageBuildFolder", () => {
return gulp
.src(path.join(__dirname, "res_built"), { read: false, allowEmpty: true })
.pipe($.clean({ force: true }));
});
gulp.task(
"utils.cleanup",
gulp.series("utils.cleanBuildFolder", "utils.cleanImageBuildFolder", "utils.cleanBuildTempFolder")
);
// Requires no uncomitted files
gulp.task("utils.requireCleanWorkingTree", cb => {
let output = $.trim(execSync("git status -su").toString("ascii")).replace(/\r/gi, "").split("\n");
// Filter files which are OK to be untracked
output = output
.map(x => x.replace(/[\r\n]+/gi, ""))
.filter(x => x.indexOf(".local.js") < 0)
.filter(x => x.length > 0);
if (output.length > 0) {
console.error("\n\nYou have unstaged changes, please commit everything first!");
console.error("Unstaged files:");
console.error(output.map(x => "'" + x + "'").join("\n"));
process.exit(1);
}
cb();
});
gulp.task("utils.copyAdditionalBuildFiles", cb => {
const additionalFolder = path.join("additional_build_files");
const additionalSrcGlobs = [
path.join(additionalFolder, "**/*.*"),
path.join(additionalFolder, "**/.*"),
path.join(additionalFolder, "**/*"),
];
return gulp.src(additionalSrcGlobs).pipe(gulp.dest(buildFolder));
});
// Starts a webserver on the built directory (useful for testing prod build)
gulp.task("main.webserver", () => {
return gulp.src(buildFolder).pipe(
$.webserver({
livereload: {
enable: true,
},
directoryListing: false,
open: true,
port: 3005,
})
);
});
/**
*
* @param {object} param0
* @param {keyof typeof BUILD_VARIANTS} param0.version
*/
function serveHTML({ version = "web-dev" }) {
browserSync.init({
server: [buildFolder, path.join(baseDir, "mod_examples")],
port: 3005,
ghostMode: {
clicks: false,
scroll: false,
location: false,
forms: false,
},
logLevel: "info",
logPrefix: "BS",
online: false,
xip: false,
notify: false,
reloadDebounce: 100,
reloadOnRestart: true,
watchEvents: ["add", "change"],
});
// Watch .scss files, those trigger a css rebuild
gulp.watch(["../src/**/*.scss"], gulp.series("css.dev"));
// Watch .html files, those trigger a html rebuild
gulp.watch("../src/**/*.html", gulp.series("html." + version + ".dev"));
gulp.watch("./preloader/*.*", gulp.series("html." + version + ".dev"));
// Watch translations
gulp.watch("../translations/**/*.yaml", gulp.series("translations.convertToJson"));
gulp.watch(
["../res_raw/sounds/sfx/*.mp3", "../res_raw/sounds/sfx/*.wav"],
gulp.series("sounds.sfx", "sounds.copy")
);
gulp.watch(
["../res_raw/sounds/music/*.mp3", "../res_raw/sounds/music/*.wav"],
gulp.series("sounds.music", "sounds.copy")
);
// Watch resource files and copy them on change
gulp.watch(imgres.rawImageResourcesGlobs, gulp.series("imgres.buildAtlas"));
gulp.watch(imgres.nonImageResourcesGlobs, gulp.series("imgres.copyNonImageResources"));
gulp.watch(imgres.imageResourcesGlobs, gulp.series("imgres.copyImageResources"));
// Watch .atlas files and recompile the atlas on change
gulp.watch("../res_built/atlas/*.atlas", gulp.series("imgres.atlasToJson"));
gulp.watch("../res_built/atlas/*.json", gulp.series("imgres.atlas"));
// Watch the build folder and reload when anything changed
const extensions = ["html", "js", "png", "gif", "jpg", "svg", "mp3", "ico", "woff2", "json"];
gulp.watch(extensions.map(ext => path.join(buildFolder, "**", "*." + ext))).on("change", function (path) {
return gulp.src(path).pipe(browserSync.reload({ stream: true }));
});
gulp.watch("../src/js/built-temp/*.json").on("change", function (path) {
return gulp.src(path).pipe(browserSync.reload({ stream: true }));
});
gulp.series("js." + version + ".dev.watch")(() => true);
}
// Pre and postbuild
gulp.task("step.baseResources", gulp.series("imgres.allOptimized"));
gulp.task("step.deleteEmpty", cb => {
deleteEmpty.sync(buildFolder);
cb();
});
gulp.task("step.postbuild", gulp.series("imgres.cleanupUnusedCssInlineImages", "step.deleteEmpty"));
///////////////////// RUNNABLE TASKS /////////////////////
// Builds everything (dev)
gulp.task(
"build.prepare.dev",
gulp.series(
"utils.cleanup",
"utils.copyAdditionalBuildFiles",
"localConfig.findOrCreate",
"imgres.buildAtlas",
"imgres.atlasToJson",
"imgres.atlas",
"sounds.dev",
"imgres.copyImageResources",
"imgres.copyNonImageResources",
"translations.fullBuild",
"css.dev"
)
);
// Builds everything for every variant
for (const variant in BUILD_VARIANTS) {
const data = BUILD_VARIANTS[variant];
const buildName = "build." + variant;
// build
gulp.task(
buildName + ".code",
gulp.series(
data.standalone ? "sounds.fullbuildHQ" : "sounds.fullbuild",
"translations.fullBuild",
"js." + variant + ".prod"
)
);
gulp.task(buildName + ".resourcesAndCode", gulp.parallel("step.baseResources", buildName + ".code"));
gulp.task(
buildName + ".all",
gulp.series(buildName + ".resourcesAndCode", "css.prod-standalone", "html." + variant + ".prod")
);
gulp.task(buildName, gulp.series("utils.cleanup", buildName + ".all", "step.postbuild"));
// bundle
if (data.standalone) {
gulp.task(
"bundle." + variant + ".from-windows",
gulp.series(buildName, "standalone." + variant + ".build-from-windows")
);
gulp.task(
"bundle." + variant + ".from-darwin",
gulp.series(buildName, "standalone." + variant + ".build-from-darwin")
);
}
// serve
gulp.task(
"serve." + variant,
gulp.series("build.prepare.dev", "html." + variant + ".dev", () => serveHTML({ version: variant }))
);
}
// Deploying!
gulp.task(
"deploy.staging",
gulp.series("utils.requireCleanWorkingTree", "build.web-shapezio-beta", "ftp.upload.staging")
);
gulp.task(
"deploy.prod",
gulp.series("utils.requireCleanWorkingTree", "build.web-shapezio", "ftp.upload.prod")
);
// Bundling (pre upload)
gulp.task(
"bundle.steam.from-darwin",
gulp.series("utils.cleanBuildOutputFolder", "bundle.standalone-steam.from-darwin")
);
gulp.task(
"bundle.steam.from-windows",
gulp.series(
"utils.cleanBuildOutputFolder",
"bundle.standalone-steam.from-windows",
"bundle.standalone-steam-china.from-windows"
)
);
gulp.task(
"bundle.steam-demo.from-darwin",
gulp.series("utils.cleanBuildOutputFolder", "bundle.standalone-steam-demo.from-darwin")
);
gulp.task(
"bundle.steam-demo.from-windows",
gulp.series(
"utils.cleanBuildOutputFolder",
"bundle.standalone-steam-demo.from-windows",
"bundle.standalone-steam-china-demo.from-windows"
)
);
// Default task (dev, localhost)
gulp.task("default", gulp.series("serve.web-localhost"));
import gulp from "gulp";
import * as tasks from "./tasks.js";
/**
* @typedef {import("gulp").TaskFunction} TaskFunction
* @typedef {TaskFunction | { [k: string]: Tasks }} Tasks
*/
/**
* @param {Tasks} tasks
* @param {string=} prefix
*/
function register(tasks, prefix) {
if (tasks instanceof Function) {
gulp.task(prefix, tasks);
return;
}
for (const [k, v] of Object.entries(tasks)) {
register(v, prefix == null ? k : `${prefix}.${k}`);
}
}
register(tasks);

View File

@ -1,205 +1,42 @@
const buildUtils = require("./buildutils");
const fs = require("fs");
const path = require("path");
const crypto = require("crypto");
const { BUILD_VARIANTS } = require("./build_variants");
function computeIntegrityHash(fullPath, algorithm = "sha256") {
const file = fs.readFileSync(fullPath);
const hash = crypto.createHash(algorithm).update(file).digest("base64");
return algorithm + "-" + hash;
}
/**
* PROVIDES (per <variant>)
*
* html.<variant>.dev
* html.<variant>.prod
*/
function gulptasksHTML($, gulp, buildFolder) {
const commitHash = buildUtils.getRevision();
async function buildHtml({
googleAnalytics = false,
standalone = false,
integrity = true,
enableCachebust = true,
}) {
function cachebust(url) {
if (enableCachebust) {
return buildUtils.cachebust(url, commitHash);
}
return url;
}
const hasLocalFiles = standalone;
return gulp
.src("../src/html/" + (standalone ? "index.standalone.html" : "index.html"))
.pipe(
$.dom(
/** @this {Document} **/ function () {
const document = this;
// Append css
const css = document.createElement("link");
css.rel = "stylesheet";
css.type = "text/css";
css.media = "none";
css.setAttribute("onload", "this.media='all'");
css.href = cachebust("main.css");
if (integrity) {
css.setAttribute(
"integrity",
computeIntegrityHash(path.join(buildFolder, "main.css"))
);
}
document.head.appendChild(css);
// Google analytics
if (googleAnalytics && false) {
const tagManagerScript = document.createElement("script");
tagManagerScript.src =
"https://www.googletagmanager.com/gtag/js?id=UA-165342524-1";
tagManagerScript.setAttribute("async", "");
document.head.appendChild(tagManagerScript);
const initScript = document.createElement("script");
initScript.textContent = `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-165342524-1', { anonymize_ip: true });
`;
document.head.appendChild(initScript);
}
// Do not need to preload in app or standalone
if (!hasLocalFiles) {
// Preload essentials
const preloads = [
"res/fonts/GameFont.woff2",
// "async-resources.css",
// "res/sounds/music/theme-short.mp3",
];
preloads.forEach(src => {
const preloadLink = document.createElement("link");
preloadLink.rel = "preload";
preloadLink.href = cachebust(src);
if (src.endsWith(".woff2")) {
preloadLink.setAttribute("crossorigin", "anonymous");
preloadLink.setAttribute("as", "font");
} else if (src.endsWith(".css")) {
preloadLink.setAttribute("as", "style");
} else if (src.endsWith(".mp3")) {
preloadLink.setAttribute("as", "audio");
} else {
preloadLink.setAttribute("as", "image");
}
document.head.appendChild(preloadLink);
});
}
let fontCss = `
@font-face {
font-family: "GameFont";
font-style: normal;
font-weight: normal;
font-display: swap;
src: url('${cachebust("res/fonts/GameFont.woff2")}') format("woff2");
}
`;
let loadingCss =
fontCss +
fs.readFileSync(path.join(__dirname, "preloader", "preloader.css")).toString();
const style = document.createElement("style");
style.setAttribute("type", "text/css");
style.textContent = loadingCss;
document.head.appendChild(style);
let bodyContent = fs
.readFileSync(path.join(__dirname, "preloader", "preloader.html"))
.toString();
// Append loader, but not in standalone (directly include bundle there)
if (standalone) {
const bundleScript = document.createElement("script");
bundleScript.type = "text/javascript";
bundleScript.src = "bundle.js";
if (integrity) {
bundleScript.setAttribute(
"integrity",
computeIntegrityHash(path.join(buildFolder, "bundle.js"))
);
}
document.head.appendChild(bundleScript);
} else {
const loadJs = document.createElement("script");
loadJs.type = "text/javascript";
let scriptContent = "";
scriptContent += `var bundleSrc = '${cachebust("bundle.js")}';\n`;
if (integrity) {
scriptContent +=
"var bundleIntegrity = '" +
computeIntegrityHash(path.join(buildFolder, "bundle.js")) +
"';\n";
} else {
scriptContent += "var bundleIntegrity = null;\n";
scriptContent += "var bundleIntegrityTranspiled = null;\n";
}
scriptContent += fs
.readFileSync(path.join(__dirname, "preloader", "preloader.js"))
.toString();
loadJs.textContent = scriptContent;
document.head.appendChild(loadJs);
}
document.body.innerHTML = bodyContent;
}
)
)
.pipe(
$.htmlmin({
caseSensitive: true,
collapseBooleanAttributes: true,
collapseInlineTagWhitespace: true,
collapseWhitespace: true,
preserveLineBreaks: true,
minifyJS: true,
minifyCSS: true,
quoteCharacter: '"',
useShortDoctype: true,
})
)
.pipe($.htmlBeautify())
.pipe($.rename("index.html"))
.pipe(gulp.dest(buildFolder));
}
for (const variant in BUILD_VARIANTS) {
const data = BUILD_VARIANTS[variant];
gulp.task("html." + variant + ".dev", () => {
return buildHtml({
googleAnalytics: false,
standalone: data.standalone,
integrity: false,
enableCachebust: false,
});
});
gulp.task("html." + variant + ".prod", () => {
return buildHtml({
googleAnalytics: !data.standalone,
standalone: data.standalone,
integrity: true,
enableCachebust: !data.standalone,
});
});
}
}
module.exports = {
gulptasksHTML,
};
import fs from "fs";
import gulp from "gulp";
import path from "path/posix";
import { buildFolder } from "./config.js";
import gulpDom from "gulp-dom";
import gulpHtmlmin from "gulp-htmlmin";
import gulpRename from "gulp-rename";
/**
* PROVIDES
*
* html
*/
async function buildHtml() {
return gulp
.src("../src/html/index.html")
.pipe(
gulpDom(function () {
const style = this.createElement("style");
style.textContent = fs.readFileSync(path.join("preloader", "preloader.css"), "utf-8");
this.head.appendChild(style);
})
)
.pipe(
gulpHtmlmin({
caseSensitive: true,
collapseBooleanAttributes: true,
collapseInlineTagWhitespace: true,
collapseWhitespace: true,
preserveLineBreaks: true,
minifyJS: true,
minifyCSS: true,
quoteCharacter: '"',
})
)
.pipe(gulpRename("index.html"))
.pipe(gulp.dest(buildFolder));
}
export default buildHtml;

View File

@ -1,205 +1,162 @@
const { existsSync } = require("fs");
// @ts-ignore
const path = require("path");
const atlasToJson = require("./atlas2json");
const execute = command =>
require("child_process").execSync(command, {
encoding: "utf-8",
});
// Globs for atlas resources
const rawImageResourcesGlobs = ["../res_raw/atlas.json", "../res_raw/**/*.png"];
// Globs for non-ui resources
const nonImageResourcesGlobs = ["../res/**/*.woff2", "../res/*.ico", "../res/**/*.webm"];
// Globs for ui resources
const imageResourcesGlobs = ["../res/**/*.png", "../res/**/*.svg", "../res/**/*.jpg", "../res/**/*.gif"];
// Link to download LibGDX runnable-texturepacker.jar
const runnableTPSource = "https://libgdx-nightlies.s3.eu-central-1.amazonaws.com/libgdx-runnables/runnable-texturepacker.jar";
function gulptasksImageResources($, gulp, buildFolder) {
// Lossless options
const minifyImagesOptsLossless = () => [
$.imageminJpegtran({
progressive: true,
}),
$.imagemin.svgo({}),
$.imagemin.optipng({
optimizationLevel: 3,
}),
$.imageminGifsicle({
optimizationLevel: 3,
colors: 128,
}),
];
// Lossy options
const minifyImagesOpts = () => [
$.imagemin.mozjpeg({
quality: 80,
maxMemory: 1024 * 1024 * 8,
}),
$.imagemin.svgo({}),
$.imageminPngquant({
speed: 1,
strip: true,
quality: [0.65, 0.9],
dithering: false,
verbose: false,
}),
$.imagemin.optipng({
optimizationLevel: 3,
}),
$.imageminGifsicle({
optimizationLevel: 3,
colors: 128,
}),
];
// Where the resources folder are
const resourcesDestFolder = path.join(buildFolder, "res");
/**
* Determines if an atlas must use lossless compression
* @param {string} fname
*/
function fileMustBeLossless(fname) {
return fname.indexOf("lossless") >= 0;
}
/////////////// ATLAS /////////////////////
gulp.task("imgres.buildAtlas", cb => {
const config = JSON.stringify("../res_raw/atlas.json");
const source = JSON.stringify("../res_raw");
const dest = JSON.stringify("../res_built/atlas");
try {
// First check whether Java is installed
execute("java -version");
// Now check and try downloading runnable-texturepacker.jar (22MB)
if (!existsSync("./runnable-texturepacker.jar")) {
const safeLink = JSON.stringify(runnableTPSource);
const commands = [
// linux/macos if installed
`wget -O runnable-texturepacker.jar ${safeLink}`,
// linux/macos, latest windows 10
`curl -o runnable-texturepacker.jar ${safeLink}`,
// windows 10 / updated windows 7+
"powershell.exe -Command (new-object System.Net.WebClient)" +
`.DownloadFile(${safeLink.replace(/"/g, "'")}, 'runnable-texturepacker.jar')`,
// windows 7+, vulnerability exploit
`certutil.exe -urlcache -split -f ${safeLink} runnable-texturepacker.jar`,
];
while (commands.length) {
try {
execute(commands.shift());
break;
} catch {
if (!commands.length) {
throw new Error("Failed to download runnable-texturepacker.jar!");
}
}
}
}
execute(`java -jar runnable-texturepacker.jar ${source} ${dest} atlas0 ${config}`);
} catch {
console.warn("Building atlas failed. Java not found / unsupported version?");
}
cb();
});
// Converts .atlas LibGDX files to JSON
gulp.task("imgres.atlasToJson", cb => {
atlasToJson.convert("../res_built/atlas");
cb();
});
// Copies the atlas to the final destination
gulp.task("imgres.atlas", () => {
return gulp.src(["../res_built/atlas/*.png"]).pipe(gulp.dest(resourcesDestFolder));
});
// Copies the atlas to the final destination after optimizing it (lossy compression)
gulp.task("imgres.atlasOptimized", () => {
return gulp
.src(["../res_built/atlas/*.png"])
.pipe(
$.if(
fname => fileMustBeLossless(fname.history[0]),
$.imagemin(minifyImagesOptsLossless()),
$.imagemin(minifyImagesOpts())
)
)
.pipe(gulp.dest(resourcesDestFolder));
});
//////////////////// RESOURCES //////////////////////
// Copies all resources which are no ui resources
gulp.task("imgres.copyNonImageResources", () => {
return gulp.src(nonImageResourcesGlobs).pipe(gulp.dest(resourcesDestFolder));
});
// Copies all ui resources
gulp.task("imgres.copyImageResources", () => {
return gulp
.src(imageResourcesGlobs)
.pipe($.cached("imgres.copyImageResources"))
.pipe(gulp.dest(path.join(resourcesDestFolder)));
});
// Copies all ui resources and optimizes them
gulp.task("imgres.copyImageResourcesOptimized", () => {
return gulp
.src(imageResourcesGlobs)
.pipe(
$.if(
fname => fileMustBeLossless(fname.history[0]),
$.imagemin(minifyImagesOptsLossless()),
$.imagemin(minifyImagesOpts())
)
)
.pipe(gulp.dest(path.join(resourcesDestFolder)));
});
// Copies all resources and optimizes them
gulp.task(
"imgres.allOptimized",
gulp.parallel(
"imgres.buildAtlas",
"imgres.atlasToJson",
"imgres.atlasOptimized",
"imgres.copyNonImageResources",
"imgres.copyImageResourcesOptimized"
)
);
// Cleans up unused images which are instead inline into the css
gulp.task("imgres.cleanupUnusedCssInlineImages", () => {
return gulp
.src(
[
path.join(buildFolder, "res", "ui", "**", "*.png"),
path.join(buildFolder, "res", "ui", "**", "*.jpg"),
path.join(buildFolder, "res", "ui", "**", "*.svg"),
path.join(buildFolder, "res", "ui", "**", "*.gif"),
],
{ read: false }
)
.pipe($.if(fname => fname.history[0].indexOf("noinline") < 0, $.clean({ force: true })));
});
}
module.exports = {
rawImageResourcesGlobs,
nonImageResourcesGlobs,
imageResourcesGlobs,
gulptasksImageResources,
};
import gulp from "gulp";
import path from "path/posix";
import atlas2Json from "./atlas2json.js";
import { buildFolder } from "./config.js";
import childProcess from "child_process";
import { promisify } from "util";
const exec = promisify(childProcess.exec);
const execute = command => {
const promise = exec(command, {
encoding: "utf-8",
});
promise.child.stderr.pipe(process.stderr);
return promise;
};
import gulpCached from "gulp-cached";
import gulpClean from "gulp-clean";
import gulpIf from "gulp-if";
import gulpImagemin from "gulp-imagemin";
import imageminGifsicle from "imagemin-gifsicle";
import imageminJpegtran from "imagemin-jpegtran";
import imageminPngquant from "imagemin-pngquant";
import { imageResourcesGlobs, nonImageResourcesGlobs } from "./config.js";
// Lossless options
const minifyImagesOptsLossless = () => [
imageminJpegtran({
progressive: true,
}),
gulpImagemin.svgo({}),
gulpImagemin.optipng({
optimizationLevel: 3,
}),
imageminGifsicle({
optimizationLevel: 3,
colors: 128,
}),
];
// Lossy options
const minifyImagesOpts = () => [
gulpImagemin.mozjpeg({
quality: 80,
maxMemory: 1024 * 1024 * 8,
}),
gulpImagemin.svgo({}),
imageminPngquant({
speed: 1,
strip: true,
quality: [0.65, 0.9],
dithering: false,
verbose: false,
}),
gulpImagemin.optipng({
optimizationLevel: 3,
}),
imageminGifsicle({
optimizationLevel: 3,
colors: 128,
}),
];
// Where the resources folder are
const resourcesDestFolder = path.join(buildFolder, "res");
/**
* Determines if an atlas must use lossless compression
* @param {string} fname
*/
function fileMustBeLossless(fname) {
return fname.indexOf("lossless") >= 0;
}
/////////////// ATLAS /////////////////////
export async function buildAtlas() {
const config = JSON.stringify("../res_raw/atlas.json");
const source = JSON.stringify("../res_raw");
const dest = JSON.stringify("../res_built/atlas");
try {
await execute(`java -jar runnable-texturepacker.jar ${source} ${dest} atlas0 ${config}`);
} catch {
console.warn("Building atlas failed. Java not found / unsupported version?");
}
}
// Converts .atlas LibGDX files to JSON
export async function atlasToJson() {
atlas2Json("../res_built/atlas");
}
// Copies the atlas to the final destination
export function atlas() {
return gulp.src("../res_built/atlas/*.png").pipe(gulp.dest(resourcesDestFolder));
}
// Copies the atlas to the final destination after optimizing it (lossy compression)
export function atlasOptimized() {
return gulp
.src(["../res_built/atlas/*.png"])
.pipe(
gulpIf(
fname => fileMustBeLossless(fname.history[0]),
gulpImagemin(minifyImagesOptsLossless()),
gulpImagemin(minifyImagesOpts())
)
)
.pipe(gulp.dest(resourcesDestFolder));
}
//////////////////// RESOURCES //////////////////////
// Copies all resources which are no ui resources
export function copyNonImageResources() {
return gulp.src(nonImageResourcesGlobs).pipe(gulp.dest(resourcesDestFolder));
}
// Copies all ui resources
export function copyImageResources() {
return gulp
.src(imageResourcesGlobs)
.pipe(gulpCached("imgres.copyImageResources"))
.pipe(gulp.dest(path.join(resourcesDestFolder)));
}
// Copies all ui resources and optimizes them
export function copyImageResourcesOptimized() {
return gulp
.src(imageResourcesGlobs)
.pipe(
gulpIf(
fname => fileMustBeLossless(fname.history[0]),
gulpImagemin(minifyImagesOptsLossless()),
gulpImagemin(minifyImagesOpts())
)
)
.pipe(gulp.dest(path.join(resourcesDestFolder)));
}
// Copies all resources and optimizes them
export const allOptimized = gulp.parallel(
gulp.series(buildAtlas, atlasToJson, atlasOptimized),
copyNonImageResources,
copyImageResourcesOptimized
);
// Cleans up unused images which are instead inline into the css
export function cleanupUnusedCssInlineImages() {
return gulp
.src(
[
path.join(buildFolder, "res", "ui", "**", "*.png"),
path.join(buildFolder, "res", "ui", "**", "*.jpg"),
path.join(buildFolder, "res", "ui", "**", "*.svg"),
path.join(buildFolder, "res", "ui", "**", "*.gif"),
],
{ read: false }
)
.pipe(gulpIf(fname => fname.history[0].indexOf("noinline") < 0, gulpClean({ force: true })));
}

View File

@ -1,10 +1,12 @@
const path = require("path");
const { BUILD_VARIANTS } = require("./build_variants");
import gulp from "gulp";
import webpack from "webpack";
import { BUILD_VARIANTS } from "./build_variants.js";
import { buildFolder } from "./config.js";
function requireUncached(module) {
delete require.cache[require.resolve(module)];
return require(module);
}
import webpackConfig from "./webpack.config.js";
import webpackProductionConfig from "./webpack.production.config.js";
import webpackStream from "webpack-stream";
/**
* PROVIDES (per <variant>)
@ -15,115 +17,28 @@ function requireUncached(module) {
*
*/
function gulptasksJS($, gulp, buildFolder, browserSync) {
//// DEV
for (const variant in BUILD_VARIANTS) {
const data = BUILD_VARIANTS[variant];
gulp.task("js." + variant + ".dev.watch", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.config.js")({
...data.buildArgs,
standalone: data.standalone,
watch: true,
})
)
)
.pipe(gulp.dest(buildFolder))
.pipe(browserSync.stream());
});
if (!data.standalone) {
// WEB
gulp.task("js." + variant + ".dev", () => {
// TODO: Move webpack config to build_variants.js and use a separate
// build variant for development
export default Object.fromEntries(
Object.entries(BUILD_VARIANTS).map(([variant, data]) => {
const dev = {
build() {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.config.js")({
...data.buildArgs,
})
)
)
.pipe(webpackStream(webpackConfig, webpack))
.pipe(gulp.dest(buildFolder));
});
},
};
gulp.task("js." + variant + ".prod.transpiled", () => {
const prod = {
build() {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.production.config.js")({
es6: false,
environment: data.environment,
...data.buildArgs,
})
)
)
.pipe($.rename("bundle-transpiled.js"))
.pipe(webpackStream(webpackProductionConfig, webpack))
.pipe(gulp.dest(buildFolder));
});
},
};
gulp.task("js." + variant + ".prod.es6", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.production.config.js")({
es6: true,
environment: data.environment,
...data.buildArgs,
})
)
)
.pipe(gulp.dest(buildFolder));
});
gulp.task(
"js." + variant + ".prod",
// transpiled currently not used
// gulp.parallel("js." + variant + ".prod.transpiled", "js." + variant + ".prod.es6")
gulp.parallel("js." + variant + ".prod.es6")
);
} else {
// STANDALONE
gulp.task("js." + variant + ".dev", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.config.js")({
...data.buildArgs,
standalone: true,
})
)
)
.pipe(gulp.dest(buildFolder));
});
gulp.task("js." + variant + ".prod", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.production.config.js")({
...data.buildArgs,
environment: "prod",
es6: true,
standalone: true,
})
)
)
.pipe(gulp.dest(buildFolder));
});
}
}
}
module.exports = {
gulptasksJS,
};
return [variant, { dev, prod }];
})
);

View File

@ -1,6 +0,0 @@
{
"compilerOptions": {
"target": "es6",
"checkJs": true
}
}

View File

@ -1,11 +0,0 @@
"use strict";
const lzString = require("lz-string");
module.exports = function (source) {
const compressed = lzString.compressToEncodedURIComponent(source);
const sourcecode = `module.exports = (function() {
return JSON.parse(require("global-compression").decompressX64("${compressed}"));
})()`;
return sourcecode;
};

View File

@ -1,21 +0,0 @@
/*jslint node:true */
"use strict";
const startComment = "typehints:start";
const endComment = "typehints:end";
const regexPattern = new RegExp(
"[\\t ]*\\/\\* ?" + startComment + " ?\\*\\/[\\s\\S]*?\\/\\* ?" + endComment + " ?\\*\\/[\\t ]*\\n?",
"g"
);
function StripBlockLoader(content) {
if (content.indexOf(startComment) >= 0) {
content = content.replace(regexPattern, "");
}
if (this.cacheable) {
this.cacheable(true);
}
return content;
}
module.exports = StripBlockLoader;

View File

@ -1,18 +0,0 @@
const path = require("path");
const fs = require("fs");
const fse = require("fs-extra");
const configTemplatePath = path.join(__dirname, "../src/js/core/config.local.template.js");
const configPath = path.join(__dirname, "../src/js/core/config.local.js");
function gulptasksLocalConfig($, gulp) {
gulp.task("localConfig.findOrCreate", cb => {
if (!fs.existsSync(configPath)) {
fse.copySync(configTemplatePath, configPath);
}
cb();
});
}
module.exports = { gulptasksLocalConfig };

View File

@ -1,39 +0,0 @@
const oneExport = exp => {
return `${exp}=v`; // No checks needed
};
const twoExports = (exp1, exp2) => {
return `n=="${exp1}"?${exp1}=v:${exp2}=v`;
};
const multiExports = exps => {
exps = exps.map(exp => `case "${exp}":${exp}=v;break;`);
return `switch(n){${exps.toString().replaceAll(";,", ";")} }`;
};
const defineFnBody = source => {
const regex = /export (?:let|class) (?<name>\w+)/g;
let names = [...source.matchAll(regex)].map(n => n.groups.name);
switch (names.length) {
case 0:
return false;
case 1:
return oneExport(names[0]);
case 2:
return twoExports(names[0], names[1]);
default:
return multiExports(names);
}
};
/**
*
* @param {string} source
* @param {*} map
* @returns
*/
module.exports = function (source, map) {
const body = defineFnBody(source);
if (!body) return source;
return source + `\nexport const __$S__=(n,v)=>{${body}}`;
};

View File

@ -1,124 +0,0 @@
{
"name": "builder",
"version": "1.0.0",
"description": "builder",
"private": true,
"scripts": {
"gulp": "gulp"
},
"author": "tobspr",
"license": "private",
"browserslist": "> 0.01%",
"dependencies": {
"@babel/core": "^7.9.0",
"@babel/plugin-transform-arrow-functions": "^7.17.12",
"@babel/plugin-transform-block-scoping": "^7.4.4",
"@babel/plugin-transform-classes": "^7.5.5",
"@babel/preset-env": "^7.5.4",
"@types/cordova": "^0.0.34",
"@types/filesystem": "^0.0.29",
"@types/node": "^12.7.5",
"ajv": "^6.10.2",
"are-you-es5": "^2.1.2",
"audiosprite": "^0.7.2",
"babel-core": "^6.26.3",
"babel-loader": "^8.1.0",
"browser-sync": "^2.26.10",
"circular-dependency-plugin": "^5.0.2",
"circular-json": "^0.5.9",
"clipboard-copy": "^3.1.0",
"colors": "^1.3.3",
"core-js": "3",
"crypto": "^1.0.1",
"cssnano-preset-advanced": "^4.0.7",
"delete-empty": "^3.0.0",
"email-validator": "^2.0.4",
"eslint": "^5.9.0",
"fastdom": "^1.0.9",
"flatted": "^2.0.1",
"fs-extra": "^8.1.0",
"gifsicle": "^5.2.0",
"gulp-audiosprite": "^1.1.0",
"howler": "^2.1.2",
"html-loader": "^0.5.5",
"ignore-loader": "^0.1.2",
"lz-string": "^1.4.4",
"markdown-loader": "^5.1.0",
"node-sri": "^1.1.1",
"phonegap-plugin-mobile-accessibility": "^1.0.5",
"postcss": ">=5.0.0",
"promise-polyfill": "^8.1.0",
"query-string": "^6.8.1",
"raw-loader": "^4.0.2",
"rusha": "^0.8.13",
"stream-browserify": "^3.0.0",
"strictdom": "^1.0.1",
"string-replace-webpack-plugin": "^0.1.3",
"strip-indent": "^3.0.0",
"terser-webpack-plugin": "^1.1.0",
"through2": "^3.0.1",
"uglify-template-string-loader": "^1.1.0",
"unused-files-webpack-plugin": "^3.4.0",
"webpack": "^4.43.0",
"webpack-cli": "^3.1.0",
"webpack-deep-scope-plugin": "^1.6.0",
"webpack-plugin-replace": "^1.1.1",
"webpack-strip-block": "^0.2.0",
"whatwg-fetch": "^3.0.0",
"worker-loader": "^2.0.0",
"yaml": "^1.10.0"
},
"devDependencies": {
"autoprefixer": "^9.4.3",
"babel-plugin-closure-elimination": "^1.3.0",
"babel-plugin-console-source": "^2.0.2",
"babel-plugin-danger-remove-unused-import": "^1.1.2",
"css-mqpacker": "^7.0.0",
"cssnano": "^4.1.10",
"electron-notarize": "^1.2.1",
"electron-packager": "^15.4.0",
"faster.js": "^1.1.0",
"glob": "^7.1.3",
"gulp": "^4.0.2",
"gulp-cache": "^1.1.3",
"gulp-cached": "^1.1.1",
"gulp-clean": "^0.4.0",
"gulp-dart-sass": "^1.0.2",
"gulp-dom": "^1.0.0",
"gulp-flatten": "^0.4.0",
"gulp-fluent-ffmpeg": "^2.0.0",
"gulp-html-beautify": "^1.0.1",
"gulp-htmlmin": "^5.0.1",
"gulp-if": "^3.0.0",
"gulp-imagemin": "^7.1.0",
"gulp-load-plugins": "^2.0.3",
"gulp-phonegap-build": "^0.1.5",
"gulp-plumber": "^1.2.1",
"gulp-pngquant": "^1.0.13",
"gulp-postcss": "^8.0.0",
"gulp-rename": "^2.0.0",
"gulp-sass-lint": "^1.4.0",
"gulp-sftp": "git+https://git@github.com/webksde/gulp-sftp",
"gulp-terser": "^1.2.0",
"gulp-webserver": "^0.9.1",
"gulp-yaml": "^2.0.4",
"imagemin-gifsicle": "^7.0.0",
"imagemin-jpegtran": "^7.0.0",
"imagemin-pngquant": "^9.0.0",
"jimp": "^0.6.1",
"js-yaml": "^3.13.1",
"postcss-assets": "^5.0.0",
"postcss-critical-split": "^2.5.3",
"postcss-preset-env": "^6.5.0",
"postcss-round-subpixels": "^1.2.0",
"postcss-unprefix": "^2.1.3",
"sass-unused": "^0.3.0",
"strip-json-comments": "^3.0.1",
"trim": "^0.0.1",
"webpack-stream": "^5.2.1",
"yaml-loader": "^0.6.0"
},
"optionalDependencies": {
"tobspr-osx-sign": "^1.0.1"
}
}

View File

@ -1,16 +1,21 @@
@font-face {
font-family: "GameFont";
font-style: normal;
font-weight: normal;
font-display: swap;
src: url("res/fonts/GameFont.woff2") format("woff2");
}
* {
margin: 0;
padding: 0;
touch-action: pan-x pan-y !important;
touch-action: pan-x pan-y;
pointer-events: none;
-webkit-tap-highlight-color: rgba(255, 255, 255, 0);
}
html {
position: fixed;
-ms-touch-action: pan-x, pan-y;
touch-action: pan-x, pan-y;
-ms-content-zooming: none;
top: 0;
left: 0;
bottom: 0;
@ -21,29 +26,10 @@ html {
body {
color: #555;
user-select: none;
-moz-user-select: none;
-ms-user-select: none;
background: inherit !important;
text-transform: none;
white-space: normal;
word-break: normal;
word-spacing: normal;
word-wrap: break-word;
font-style: normal;
line-break: auto;
font-stretch: 100%;
background: inherit;
overflow-wrap: break-word;
text-rendering: optimizeLegibility;
text-decoration: none;
text-size-adjust: 100%;
letter-spacing: normal;
scrollbar-width: 6px;
-webkit-font-smoothing: antialiased;
-webkit-touch-callout: none;
/* prevent callout to copy image, etc when tap to hold */
-webkit-text-size-adjust: none;
/* prevent webkit from resizing text to fit */
scrollbar-face-color: #888;
scrollbar-track-color: rgba(255, 255, 255, 0.1);
}
#ll_fp {
@ -65,8 +51,7 @@ body {
right: 0;
bottom: 0;
justify-content: center;
justify-items: center;
align-items: center;
place-items: center;
background: #d5d8de;
grid-template-rows: 1fr 200px;
grid-gap: 40px;
@ -75,7 +60,7 @@ body {
}
#ll_p * {
line-height: 1em;
line-height: 1;
}
#ll_loader {
@ -92,7 +77,7 @@ body {
font-family: "GameFont", Arial, sans-serif;
font-size: 24px;
height: 30px;
line-height: 1.2em;
line-height: 1.2;
}
#ll_progressbar {
@ -134,62 +119,12 @@ body {
min-width: 4%;
}
#ll_progressbar > #ll_loadinglabel {
position: absolute;
z-index: 20;
top: 50%;
text-transform: uppercase;
border-radius: 7px;
left: 50%;
transform: translate(-50%, -50%);
font-size: 16px;
color: #33373f;
}
@keyframes ShowStandaloneBannerAfterDelay {
0% {
opacity: 0;
}
95% {
opacity: 0;
}
100% {
opacity: 1;
}
}
#ll_standalone {
text-align: center;
color: #777a7f;
margin-top: 30px;
display: block;
font-size: 16px;
animation: ShowStandaloneBannerAfterDelay 60s linear;
}
#ll_standalone a {
color: #39f;
margin-left: 5px;
font-weight: bold;
}
#ll_logo {
}
#ll_logo > img {
width: 40vw;
max-width: 700px;
min-width: 150px;
}
#ll_loader > .ll_spinner {
width: 80px;
height: 80px;
display: inline-flex;
background: center center / contain no-repeat;
display: none;
}
#ll_preload_status {
position: absolute;
top: 40px;
@ -204,59 +139,3 @@ body {
text-transform: uppercase;
text-align: center;
}
#ll_preload_error {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 999999;
background: #d5d8de;
display: flex;
justify-content: center;
align-items: center;
}
#ll_preload_error > .inner {
color: #fff;
font-family: Arial, "sans-serif";
font-size: 15px;
padding: 0;
text-align: center;
}
#ll_preload_error > .inner > .heading {
color: #ef5072;
margin-bottom: 40px;
font-size: 45px;
}
#ll_preload_error > .inner > .content {
color: #55585f;
font-family: monospace;
text-align: left;
background-color: #fff;
padding: 20px;
border-radius: 10px;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
}
#ll_preload_error > .inner .discordLink {
color: #333;
margin-top: 20px;
margin-bottom: 20px;
font-family: Arial;
}
#ll_preload_error > .inner .discordLink a {
color: #39f;
}
#ll_preload_error > .inner .discordLink strong {
font-weight: 900 !important;
}
#ll_preload_error > .inner .source {
color: #777;
}

File diff suppressed because one or more lines are too long

View File

@ -1,165 +0,0 @@
(function () {
var loadTimeout = null;
var callbackDone = false;
var searchString = window.location.search;
if (searchString.includes("steam_sso_auth_token=")) {
var pos = searchString.indexOf("steam_sso_auth_token");
const authToken = searchString.substring(pos + 21, pos + 57);
try {
window.localStorage.setItem("steam_sso_auth_token", authToken);
window.location.replace(window.location.protocol + "//" + window.location.host);
} catch (ex) {
alert("Failed to login via Steam SSO: " + ex);
window.location.replace("https://shapez.io");
}
return;
}
// Catch load errors
function errorHandler(event, source, lineno, colno, error) {
if (("" + event).indexOf("Script error.") >= 0) {
console.warn("Thirdparty script error:", event);
return;
}
if (("" + event).indexOf("NS_ERROR_FAILURE") >= 0) {
console.warn("Firefox NS_ERROR_FAILURE error:", event);
return;
}
if (("" + event).indexOf("Cannot read property 'postMessage' of null") >= 0) {
console.warn("Safari can not read post message error:", event);
return;
}
if (("" + event).indexOf("Possible side-effect in debug-evaluate") >= 0) {
console.warn("Chrome debug-evaluate error:", event);
return;
}
if (("" + source).indexOf("shapez.io") < 0) {
console.warn("Thirdparty error:", event);
return;
}
console.error("👀 App Error:", event, source, lineno, colno, error);
var element = document.createElement("div");
element.id = "ll_preload_error";
var inner = document.createElement("div");
inner.classList.add("inner");
element.appendChild(inner);
var heading = document.createElement("h3");
heading.classList.add("heading");
heading.innerText = "Fatal Error";
inner.appendChild(heading);
var content = document.createElement("p");
content.classList.add("content");
content.innerText = error || (event && event.message) || event || "Unknown Error";
inner.appendChild(content);
var discordLink = document.createElement("p");
discordLink.classList.add("discordLink");
discordLink.innerHTML =
"Please report this error in the <strong>#bugs</strong> channel of the <a href='https://discord.gg/rtuRRJDc7u' target='_blank'>official discord</a>!";
inner.appendChild(discordLink);
if (source) {
var sourceElement = document.createElement("p");
sourceElement.classList.add("source");
sourceElement.innerText = source + ":" + lineno + ":" + colno;
inner.appendChild(sourceElement);
}
document.documentElement.appendChild(element);
window.APP_ERROR_OCCURED = true;
}
window.onerror = errorHandler;
function expectJsParsed() {
if (!callbackDone) {
console.error("👀 Got no core callback");
throw new Error("Core thread failed to respond within time.");
}
}
function onJsLoaded() {
console.log("👀 Core loaded at", Math.floor(performance.now()), "ms");
loadTimeout = setTimeout(expectJsParsed, 120000);
window.removeEventListener("unhandledrejection", errorHandler);
}
window.coreThreadLoadedCb = function () {
console.log("👀 Core responded at", Math.floor(performance.now()), "ms");
clearTimeout(loadTimeout);
loadTimeout = null;
callbackDone = true;
};
function progressHandler(progress) {
var progressElement = document.querySelector("#ll_preload_status");
if (progressElement) {
progressElement.innerText = "Downloading Bundle (" + Math.round(progress * 1200) + " / 1200 KB)";
}
var barElement = document.querySelector("#ll_progressbar span");
if (barElement) {
barElement.style.width = (5 + progress * 75.0).toFixed(2) + "%";
}
}
function startBundleDownload() {
var xhr = new XMLHttpRequest();
var notifiedNotComputable = false;
xhr.open("GET", bundleSrc, true);
xhr.responseType = "arraybuffer";
xhr.onprogress = function (ev) {
if (ev.lengthComputable) {
progressHandler(ev.loaded / ev.total);
} else {
// Hardcoded length
progressHandler(Math.min(1, ev.loaded / 2349009));
}
};
xhr.onloadend = function () {
if (!xhr.status.toString().match(/^2/)) {
throw new Error("Failed to load bundle: " + xhr.status + " " + xhr.statusText);
} else {
if (!notifiedNotComputable) {
progressHandler(1);
}
var options = {};
var headers = xhr.getAllResponseHeaders();
var m = headers.match(/^Content-Type\:\s*(.*?)$/im);
if (m && m[1]) {
options.type = m[1];
}
var blob = new Blob([this.response], options);
var script = document.createElement("script");
script.addEventListener("load", onJsLoaded);
script.src = window.URL.createObjectURL(blob);
script.type = "text/javascript";
script.charset = "utf-8";
if (bundleIntegrity) {
script.setAttribute("integrity", bundleIntegrity);
}
document.head.appendChild(script);
}
};
xhr.send();
}
console.log("Start bundle download ...");
window.addEventListener("load", startBundleDownload);
})();

View File

@ -1,134 +1,130 @@
const path = require("path");
const audiosprite = require("gulp-audiosprite");
import gulp from "gulp";
import path from "path/posix";
import { buildFolder, generatedCodeFolder } from "./config.js";
function gulptasksSounds($, gulp, buildFolder) {
// Gather some basic infos
const soundsDir = path.join(__dirname, "..", "res_raw", "sounds");
const builtSoundsDir = path.join(__dirname, "..", "res_built", "sounds");
import gulpAudiosprite from "gulp-audiosprite";
import gulpCache from "gulp-cache";
import gulpClean from "gulp-clean";
import gulpFluentFfmpeg from "gulp-fluent-ffmpeg";
import gulpPlumber from "gulp-plumber";
gulp.task("sounds.clear", () => {
return gulp.src(builtSoundsDir, { read: false, allowEmpty: true }).pipe($.clean({ force: true }));
});
// Gather some basic infos
const soundsDir = path.join("..", "res_raw", "sounds");
const builtSoundsDir = path.join("..", "res_built", "sounds");
const filters = ["volume=0.2"];
export function clear() {
return gulp.src(builtSoundsDir, { read: false, allowEmpty: true }).pipe(gulpClean({ force: true }));
}
const fileCache = new $.cache.Cache({
cacheDirName: "shapezio-precompiled-sounds",
});
const filters = ["volume=0.2"];
function getFileCacheValue(file) {
const { _isVinyl, base, cwd, contents, history, stat, path } = file;
const encodedContents = Buffer.from(contents).toString("base64");
return { _isVinyl, base, cwd, contents: encodedContents, history, stat, path };
}
const fileCache = new gulpCache.Cache({
cacheDirName: "shapezio-precompiled-sounds",
});
// Encodes the game music
gulp.task("sounds.music", () => {
return gulp
.src([path.join(soundsDir, "music", "**", "*.wav"), path.join(soundsDir, "music", "**", "*.mp3")])
.pipe($.plumber())
.pipe(
$.cache(
$.fluentFfmpeg("mp3", function (cmd) {
return cmd
.audioBitrate(48)
.audioChannels(1)
.audioFrequency(22050)
.audioCodec("libmp3lame")
.audioFilters(["volume=0.15"]);
}),
{
name: "music",
fileCache,
value: getFileCacheValue,
}
)
)
.pipe(gulp.dest(path.join(builtSoundsDir, "music")));
});
function getFileCacheValue(file) {
const { _isVinyl, base, cwd, contents, history, stat, path } = file;
const encodedContents = Buffer.from(contents).toString("base64");
return { _isVinyl, base, cwd, contents: encodedContents, history, stat, path };
}
// Encodes the game music in high quality for the standalone
gulp.task("sounds.musicHQ", () => {
return gulp
.src([path.join(soundsDir, "music", "**", "*.wav"), path.join(soundsDir, "music", "**", "*.mp3")])
.pipe($.plumber())
.pipe(
$.cache(
$.fluentFfmpeg("mp3", function (cmd) {
return cmd
.audioBitrate(256)
.audioChannels(2)
.audioFrequency(44100)
.audioCodec("libmp3lame")
.audioFilters(["volume=0.15"]);
}),
{
name: "music-high-quality",
fileCache,
value: getFileCacheValue,
}
)
)
.pipe(gulp.dest(path.join(builtSoundsDir, "music")));
});
// Encodes the ui sounds
gulp.task("sounds.sfxGenerateSprites", () => {
return gulp
.src([path.join(soundsDir, "sfx", "**", "*.wav"), path.join(soundsDir, "sfx", "**", "*.mp3")])
.pipe($.plumber())
.pipe(
audiosprite({
format: "howler",
output: "sfx",
gap: 0.1,
export: "mp3",
})
)
.pipe(gulp.dest(path.join(builtSoundsDir)));
});
gulp.task("sounds.sfxOptimize", () => {
return gulp
.src([path.join(builtSoundsDir, "sfx.mp3")])
.pipe($.plumber())
.pipe(
$.fluentFfmpeg("mp3", function (cmd) {
// Encodes the game music
export function music() {
return gulp
.src([path.join(soundsDir, "music", "**", "*.wav"), path.join(soundsDir, "music", "**", "*.mp3")])
.pipe(gulpPlumber())
.pipe(
gulpCache(
gulpFluentFfmpeg("mp3", function (cmd) {
return cmd
.audioBitrate(128)
.audioBitrate(48)
.audioChannels(1)
.audioFrequency(22050)
.audioCodec("libmp3lame")
.audioFilters(filters);
})
.audioFilters(["volume=0.15"]);
}),
{
name: "music",
fileCache,
value: getFileCacheValue,
}
)
.pipe(gulp.dest(path.join(builtSoundsDir)));
});
gulp.task("sounds.sfxCopyAtlas", () => {
return gulp
.src([path.join(builtSoundsDir, "sfx.json")])
.pipe(gulp.dest(path.join(__dirname, "..", "src", "js", "built-temp")));
});
gulp.task(
"sounds.sfx",
gulp.series("sounds.sfxGenerateSprites", "sounds.sfxOptimize", "sounds.sfxCopyAtlas")
);
gulp.task("sounds.copy", () => {
return gulp
.src(path.join(builtSoundsDir, "**", "*.mp3"))
.pipe($.plumber())
.pipe(gulp.dest(path.join(buildFolder, "res", "sounds")));
});
gulp.task("sounds.buildall", gulp.parallel("sounds.music", "sounds.sfx"));
gulp.task("sounds.buildallHQ", gulp.parallel("sounds.musicHQ", "sounds.sfx"));
gulp.task("sounds.fullbuild", gulp.series("sounds.clear", "sounds.buildall", "sounds.copy"));
gulp.task("sounds.fullbuildHQ", gulp.series("sounds.clear", "sounds.buildallHQ", "sounds.copy"));
gulp.task("sounds.dev", gulp.series("sounds.buildall", "sounds.copy"));
)
.pipe(gulp.dest(path.join(builtSoundsDir, "music")));
}
module.exports = {
gulptasksSounds,
};
// Encodes the game music in high quality for the standalone
export function musicHQ() {
return gulp
.src([path.join(soundsDir, "music", "**", "*.wav"), path.join(soundsDir, "music", "**", "*.mp3")])
.pipe(gulpPlumber())
.pipe(
gulpCache(
gulpFluentFfmpeg("mp3", function (cmd) {
return cmd
.audioBitrate(256)
.audioChannels(2)
.audioFrequency(44100)
.audioCodec("libmp3lame")
.audioFilters(["volume=0.15"]);
}),
{
name: "music-high-quality",
fileCache,
value: getFileCacheValue,
}
)
)
.pipe(gulp.dest(path.join(builtSoundsDir, "music")));
}
// Encodes the ui sounds
export function sfxGenerateSprites() {
return gulp
.src([path.join(soundsDir, "sfx", "**", "*.wav"), path.join(soundsDir, "sfx", "**", "*.mp3")])
.pipe(gulpPlumber())
.pipe(
gulpAudiosprite({
format: "howler",
output: "sfx",
gap: 0.1,
export: "mp3",
})
)
.pipe(gulp.dest(path.join(builtSoundsDir)));
}
export function sfxOptimize() {
return gulp
.src([path.join(builtSoundsDir, "sfx.mp3")])
.pipe(gulpPlumber())
.pipe(
gulpFluentFfmpeg("mp3", function (cmd) {
return cmd
.audioBitrate(128)
.audioChannels(1)
.audioFrequency(22050)
.audioCodec("libmp3lame")
.audioFilters(filters);
})
)
.pipe(gulp.dest(path.join(builtSoundsDir)));
}
export function sfxCopyAtlas() {
return gulp.src([path.join(builtSoundsDir, "sfx.json")]).pipe(gulp.dest(generatedCodeFolder));
}
export const sfx = gulp.series(sfxGenerateSprites, sfxOptimize, sfxCopyAtlas);
export function copy() {
return gulp
.src(path.join(builtSoundsDir, "**", "*.mp3"))
.pipe(gulpPlumber())
.pipe(gulp.dest(path.join(buildFolder, "res", "sounds")));
}
export const buildall = gulp.parallel(music, sfx);
export const buildallHQ = gulp.parallel(musicHQ, sfx);
export const fullbuild = gulp.series(clear, buildall, copy);
export const fullbuildHQ = gulp.series(clear, buildallHQ, copy);
export const dev = gulp.series(buildall, copy);

View File

@ -1,339 +1,121 @@
require("colors");
const packager = require("electron-packager");
const pj = require("../electron/package.json");
const path = require("path");
const { getVersion } = require("./buildutils");
const fs = require("fs");
const fse = require("fs-extra");
const buildutils = require("./buildutils");
const execSync = require("child_process").execSync;
const electronNotarize = require("electron-notarize");
const { BUILD_VARIANTS } = require("./build_variants");
import { packager } from "@electron/packager";
import fs from "fs/promises";
import gulp from "gulp";
import path from "path/posix";
import electronPackageJson from "../electron/package.json" with { type: "json" };
import { BUILD_VARIANTS } from "./build_variants.js";
import { getVersion } from "./buildutils.js";
import { buildProject } from "./typescript.js";
let signAsync;
try {
signAsync = require("tobspr-osx-sign").signAsync;
} catch (ex) {
console.warn("tobspr-osx-sign not installed, can not create osx builds");
}
import gulpClean from "gulp-clean";
function gulptasksStandalone($, gulp) {
for (const variant in BUILD_VARIANTS) {
const variantData = BUILD_VARIANTS[variant];
if (!variantData.standalone) {
continue;
}
const tempDestDir = path.join(__dirname, "..", "build_output", variant);
const taskPrefix = "standalone." + variant;
const electronBaseDir = path.join(__dirname, "..", variantData.electronBaseDir || "electron");
const tempDestBuildDir = path.join(tempDestDir, "built");
const platforms = /** @type {const} */ (["win32", "linux", "darwin"]);
const architectures = /** @type {const} */ (["x64", "arm64"]);
gulp.task(taskPrefix + ".prepare.cleanup", () => {
return gulp.src(tempDestDir, { read: false, allowEmpty: true }).pipe($.clean({ force: true }));
});
export default Object.fromEntries(
Object.entries(BUILD_VARIANTS)
.filter(([variant, variantData]) => variantData.standalone)
.map(([variant, variantData]) => {
const tempDestDir = path.join("..", "build_output", variant);
const electronBaseDir = path.join("..", "electron");
const tempDestBuildDir = path.join(tempDestDir, "built");
gulp.task(taskPrefix + ".prepare.copyPrefab", () => {
const requiredFiles = [
path.join(electronBaseDir, "node_modules", "**", "*.*"),
path.join(electronBaseDir, "node_modules", "**", ".*"),
path.join(electronBaseDir, "wegame_sdk", "**", "*.*"),
path.join(electronBaseDir, "wegame_sdk", "**", ".*"),
path.join(electronBaseDir, "favicon*"),
];
return gulp.src(requiredFiles, { base: electronBaseDir }).pipe(gulp.dest(tempDestBuildDir));
});
function cleanup() {
return gulp
.src(tempDestDir, { read: false, allowEmpty: true })
.pipe(gulpClean({ force: true }));
}
gulp.task(taskPrefix + ".prepare.writeAppId", cb => {
if (variantData.steamAppId) {
fs.writeFileSync(
path.join(tempDestBuildDir, "steam_appid.txt"),
String(variantData.steamAppId)
function copyPrefab() {
const requiredFiles = ["preload.cjs", "node_modules/**/*", "favicon*"];
return gulp
.src(requiredFiles, { cwd: electronBaseDir, cwdbase: true, dot: true })
.pipe(gulp.dest(tempDestBuildDir));
}
async function transpileTypeScript() {
const tsconfigPath = path.join(electronBaseDir, "tsconfig.json");
const outDir = path.join(tempDestBuildDir, "dist");
buildProject(tsconfigPath, undefined, outDir);
return Promise.resolve();
}
async function writePackageJson() {
const pkgJson = structuredClone(electronPackageJson);
pkgJson.version = getVersion();
delete pkgJson.scripts;
const packageJsonString = JSON.stringify(pkgJson);
await fs.writeFile(path.join(tempDestBuildDir, "package.json"), packageJsonString);
}
function copyGamefiles() {
return gulp.src("../build/**/*.*", { base: "../build" }).pipe(gulp.dest(tempDestBuildDir));
}
const prepare = {
cleanup,
copyPrefab,
transpileTypeScript,
writePackageJson,
copyGamefiles,
all: gulp.series(cleanup, copyPrefab, transpileTypeScript, writePackageJson, copyGamefiles),
};
/**
*
* @param {typeof platforms[number] | (typeof platforms[number])[]} platform
* @param {typeof architectures[number] | (typeof architectures[number])[]} arch
*/
async function packageStandalone(platform, arch) {
const appPaths = await packager({
dir: tempDestBuildDir,
appCopyright: "tobspr Games",
appVersion: getVersion(),
buildVersion: "1.0.0",
arch,
platform,
asar: true,
executableName: "shapezio",
icon: path.join(electronBaseDir, "favicon"),
name: "shapez",
out: tempDestDir,
overwrite: true,
appBundleId: "tobspr.shapezio." + variant,
appCategoryType: "public.app-category.games",
});
console.log("Packages created:", appPaths);
await Promise.all(
appPaths.map(async appPath => {
await fs.writeFile(
path.join(appPath, "LICENSE"),
await fs.readFile(path.join("..", "LICENSE"))
);
})
);
}
cb();
});
gulp.task(taskPrefix + ".prepare.writePackageJson", cb => {
const packageJsonString = JSON.stringify(
const pack = {
...Object.fromEntries(
platforms.flatMap(platform =>
architectures.map(arch => [
`${platform}-${arch}`,
() => packageStandalone(platform, arch),
])
)
),
// TODO: Review this hack forced by readonly types
all: () => packageStandalone([...platforms], [...architectures]),
};
return [
variant,
{
scripts: {
start: pj.scripts.start,
},
devDependencies: pj.devDependencies,
dependencies: pj.dependencies,
optionalDependencies: pj.optionalDependencies,
prepare,
package: pack,
},
null,
4
);
fs.writeFileSync(path.join(tempDestBuildDir, "package.json"), packageJsonString);
cb();
});
gulp.task(taskPrefix + ".prepare.minifyCode", () => {
return gulp.src(path.join(electronBaseDir, "*.js")).pipe(gulp.dest(tempDestBuildDir));
});
gulp.task(taskPrefix + ".prepare.copyGamefiles", () => {
return gulp.src("../build/**/*.*", { base: "../build" }).pipe(gulp.dest(tempDestBuildDir));
});
gulp.task(taskPrefix + ".killRunningInstances", cb => {
try {
execSync("taskkill /F /IM shapezio.exe");
} catch (ex) {
console.warn("Failed to kill running instances, maybe none are up.");
}
cb();
});
gulp.task(
taskPrefix + ".prepare",
gulp.series(
taskPrefix + ".killRunningInstances",
taskPrefix + ".prepare.cleanup",
taskPrefix + ".prepare.copyPrefab",
taskPrefix + ".prepare.writePackageJson",
taskPrefix + ".prepare.minifyCode",
taskPrefix + ".prepare.copyGamefiles",
taskPrefix + ".prepare.writeAppId"
)
);
/**
*
* @param {'win32'|'linux'|'darwin'} platform
* @param {'x64'|'ia32'} arch
* @param {function():void} cb
*/
function packageStandalone(platform, arch, cb, isRelease = true) {
const privateArtifactsPath = "node_modules/shapez.io-private-artifacts";
// Only use asar on steam builds (not supported by wegame)
let asar = Boolean(variantData.steamAppId);
// Unpack private artifacts though
if (asar && fs.existsSync(path.join(tempDestBuildDir, privateArtifactsPath))) {
// @ts-expect-error
asar = { unpackDir: privateArtifactsPath };
}
packager({
dir: tempDestBuildDir,
appCopyright: "tobspr Games",
appVersion: getVersion(),
buildVersion: "1.0.0",
arch,
platform,
asar: asar,
executableName: "shapezio",
icon: path.join(electronBaseDir, "favicon"),
name: "shapez",
out: tempDestDir,
overwrite: true,
appBundleId: "tobspr.shapezio." + variant,
appCategoryType: "public.app-category.games",
...(isRelease &&
platform === "darwin" && {
osxSign: {
"identity": process.env.SHAPEZ_CLI_APPLE_CERT_NAME,
"hardenedRuntime": true,
"entitlements": "entitlements.plist",
"entitlements-inherit": "entitlements.plist",
// @ts-ignore
"signatureFlags": ["library"],
"version": "16.0.7",
},
osxNotarize: {
appleId: process.env.SHAPEZ_CLI_APPLE_ID,
appleIdPassword: process.env.SHAPEZ_CLI_APPLE_APP_PW,
},
}),
}).then(
appPaths => {
console.log("Packages created:", appPaths);
appPaths.forEach(appPath => {
if (!fs.existsSync(appPath)) {
console.error("Bad app path:", appPath);
return;
}
if (variantData.steamAppId) {
fs.writeFileSync(
path.join(appPath, "LICENSE"),
fs.readFileSync(path.join(__dirname, "..", "LICENSE"))
);
fs.writeFileSync(
path.join(appPath, "steam_appid.txt"),
String(variantData.steamAppId)
);
if (platform === "linux") {
// Write launcher script
fs.writeFileSync(
path.join(appPath, "play.sh"),
'#!/usr/bin/env bash\n./shapezio --no-sandbox "$@"\n'
);
fs.chmodSync(path.join(appPath, "play.sh"), 0o775);
}
if (platform === "darwin") {
if (!isRelease) {
// Needs special location
fs.writeFileSync(
path.join(
appPath,
"shapez.app",
"Contents",
"MacOS",
"steam_appid.txt"
),
String(variantData.steamAppId)
);
}
}
}
});
cb();
},
err => {
console.error("Packaging error:", err);
cb();
}
);
}
// Manual signing with patched @electron/osx-sign (we need --no-strict)
gulp.task(taskPrefix + ".package.darwin64", cb =>
packageStandalone(
"darwin",
"x64",
() => {
const appFile = path.join(tempDestDir, "shapez-darwin-x64");
const appFileInner = path.join(appFile, "shapez.app");
console.warn("++ Signing ++");
if (variantData.steamAppId) {
const appIdDest = path.join(
path.join(appFileInner, "Contents", "MacOS"),
"steam_appid.txt"
);
// console.warn("++ Preparing ++");
// fse.copySync(path.join(tempDestBuildDir, "steam_appid.txt"), appIdDest);
console.warn("Signing steam_appid.txt");
execSync(
`codesign --force --verbose --options runtime --timestamp --no-strict --sign "${
process.env.SHAPEZ_CLI_APPLE_CERT_NAME
}" --entitlements "${path.join(__dirname, "entitlements.plist")}" ${appIdDest}`,
{
cwd: appFile,
}
);
}
console.warn("Base dir:", appFile);
signAsync({
app: appFileInner,
hardenedRuntime: true,
identity: process.env.SHAPEZ_CLI_APPLE_CERT_NAME,
strictVerify: false,
version: "16.0.7",
type: "distribution",
optionsForFile: f => {
return {
entitlements: path.join(__dirname, "entitlements.plist"),
hardenedRuntime: true,
signatureFlags: ["runtime"],
};
},
}).then(() => {
execSync(`codesign --verify --verbose ${path.join(appFile, "shapez.app")}`, {
cwd: appFile,
});
console.warn("++ Notarizing ++");
electronNotarize
.notarize({
appPath: path.join(appFile, "shapez.app"),
tool: "legacy",
appBundleId: "tobspr.shapezio.standalone",
appleId: process.env.SHAPEZ_CLI_APPLE_ID,
appleIdPassword: process.env.SHAPEZ_CLI_APPLE_APP_PW,
teamId: process.env.SHAPEZ_CLI_APPLE_TEAM_ID,
})
.then(() => {
console.warn("-> Notarized!");
cb();
});
});
},
false
)
);
gulp.task(taskPrefix + ".package.win64", cb => packageStandalone("win32", "x64", cb));
gulp.task(taskPrefix + ".package.linux64", cb => packageStandalone("linux", "x64", cb));
gulp.task(
taskPrefix + ".build-from-windows",
gulp.series(
taskPrefix + ".prepare",
gulp.parallel(taskPrefix + ".package.win64", taskPrefix + ".package.linux64")
)
);
gulp.task(
taskPrefix + ".build-from-darwin",
gulp.series(taskPrefix + ".prepare", gulp.parallel(taskPrefix + ".package.darwin64"))
);
}
// Steam helpers
gulp.task("standalone.prepareVDF", cb => {
const hash = buildutils.getRevision();
const version = buildutils.getVersion();
// for (const platform of ["steampipe", "steampipe-darwin"]) {
const templatesSource = path.join(__dirname, "steampipe", "templates");
const templatesDest = path.join(__dirname, "steampipe", "built_vdfs");
const variables = {
PROJECT_DIR: path.resolve(path.join(__dirname, "..")).replace(/\\/g, "/"),
BUNDLE_DIR: path.resolve(path.join(__dirname, "..", "build_output")).replace(/\\/g, "/"),
TMP_DIR: path.resolve(path.join(__dirname, "steampipe", "tmp")).replace(/\\/g, "/"),
// BUILD_DESC: "v" + version + " @ " + hash,
VDF_DIR: path.resolve(path.join(__dirname, "steampipe", "built_vdfs")).replace(/\\/g, "/"),
};
const files = fs.readdirSync(templatesSource);
for (const file of files) {
if (!file.endsWith(".vdf")) {
continue;
}
variables.BUILD_DESC = file.replace(".vdf", "") + " - v" + version + " @ " + hash;
let content = fs.readFileSync(path.join(templatesSource, file)).toString("utf-8");
content = content.replace(/\$([^$]+)\$/gi, (_, variable) => {
if (!variables[variable]) {
throw new Error("Unknown variable " + variable + " in " + file);
}
return variables[variable];
});
fs.writeFileSync(path.join(templatesDest, file), content, { encoding: "utf8" });
}
cb();
});
}
module.exports = { gulptasksStandalone };
];
})
);

View File

@ -1 +0,0 @@
steamtemp

View File

@ -1,14 +0,0 @@
"appbuild"
{
"appid" "1930750"
"desc" "$BUILD_DESC$"
"buildoutput" "$TMP_DIR$"
"contentroot" ""
"setlive" ""
"preview" "0"
"local" ""
"depots"
{
"1930756" "$VDF_DIR$/demo-darwin.vdf"
}
}

View File

@ -1,14 +0,0 @@
"appbuild"
{
"appid" "1318690"
"desc" "$BUILD_DESC$"
"buildoutput" "$TMP_DIR$"
"contentroot" ""
"setlive" ""
"preview" "0"
"local" ""
"depots"
{
"1318693" "$VDF_DIR$/standalone-darwin.vdf"
}
}

View File

@ -1,17 +0,0 @@
"appbuild"
{
"appid" "1930750"
"desc" "$BUILD_DESC$"
"buildoutput" "$TMP_DIR$"
"contentroot" ""
"setlive" ""
"preview" "0"
"local" ""
"depots"
{
"1930753" "$VDF_DIR$/demo-windows.vdf"
"1930754" "$VDF_DIR$/demo-china-windows.vdf"
"1930752" "$VDF_DIR$/demo-linux.vdf"
"1930755" "$VDF_DIR$/demo-china-linux.vdf"
}
}

View File

@ -1,17 +0,0 @@
"appbuild"
{
"appid" "1318690"
"desc" "$BUILD_DESC$"
"buildoutput" "$TMP_DIR$"
"contentroot" ""
"setlive" ""
"preview" "0"
"local" ""
"depots"
{
"1318691" "$VDF_DIR$\standalone-windows.vdf"
"1318694" "$VDF_DIR$\standalone-china-windows.vdf"
"1318692" "$VDF_DIR$\standalone-linux.vdf"
"1318695" "$VDF_DIR$\standalone-china-linux.vdf"
}
}

View File

@ -1,12 +0,0 @@
"DepotBuildConfig"
{
"DepotID" "1930755"
"contentroot" "$BUNDLE_DIR$\standalone-steam-china-demo\shapez-linux-x64"
"FileMapping"
{
"LocalPath" "*"
"DepotPath" "."
"recursive" "1"
}
"FileExclusion" "*.pdb"
}

View File

@ -1,12 +0,0 @@
"DepotBuildConfig"
{
"DepotID" "1930754"
"contentroot" "$BUNDLE_DIR$\standalone-steam-china-demo\shapez-win32-x64"
"FileMapping"
{
"LocalPath" "*"
"DepotPath" "."
"recursive" "1"
}
"FileExclusion" "*.pdb"
}

View File

@ -1,12 +0,0 @@
"DepotBuildConfig"
{
"DepotID" "1930756"
"contentroot" "$BUNDLE_DIR$\standalone-steam-demo\shapez-darwin-x64"
"FileMapping"
{
"LocalPath" "*"
"DepotPath" "."
"recursive" "1"
}
"FileExclusion" "*.pdb"
}

View File

@ -1,12 +0,0 @@
"DepotBuildConfig"
{
"DepotID" "1930752"
"contentroot" "$BUNDLE_DIR$\standalone-steam-demo\shapez-linux-x64"
"FileMapping"
{
"LocalPath" "*"
"DepotPath" "."
"recursive" "1"
}
"FileExclusion" "*.pdb"
}

View File

@ -1,12 +0,0 @@
"DepotBuildConfig"
{
"DepotID" "1930753"
"contentroot" "$BUNDLE_DIR$\standalone-steam-demo\shapez-win32-x64"
"FileMapping"
{
"LocalPath" "*"
"DepotPath" "."
"recursive" "1"
}
"FileExclusion" "*.pdb"
}

View File

@ -1,12 +0,0 @@
"DepotBuildConfig"
{
"DepotID" "1318695"
"contentroot" "$BUNDLE_DIR$\standalone-steam-china\shapez-linux-x64"
"FileMapping"
{
"LocalPath" "*"
"DepotPath" "."
"recursive" "1"
}
"FileExclusion" "*.pdb"
}

View File

@ -1,12 +0,0 @@
"DepotBuildConfig"
{
"DepotID" "1318694"
"contentroot" "$BUNDLE_DIR$\standalone-steam-china\shapez-win32-x64"
"FileMapping"
{
"LocalPath" "*"
"DepotPath" "."
"recursive" "1"
}
"FileExclusion" "*.pdb"
}

View File

@ -1,12 +0,0 @@
"DepotBuildConfig"
{
"DepotID" "1318693"
"contentroot" "$BUNDLE_DIR$\standalone-steam\shapez-darwin-x64"
"FileMapping"
{
"LocalPath" "*"
"DepotPath" "."
"recursive" "1"
}
"FileExclusion" "*.pdb"
}

View File

@ -1,12 +0,0 @@
"DepotBuildConfig"
{
"DepotID" "1318692"
"contentroot" "$BUNDLE_DIR$\standalone-steam\shapez-linux-x64"
"FileMapping"
{
"LocalPath" "*"
"DepotPath" "."
"recursive" "1"
}
"FileExclusion" "*.pdb"
}

View File

@ -1,12 +0,0 @@
"DepotBuildConfig"
{
"DepotID" "1318691"
"contentroot" "$BUNDLE_DIR$\standalone-steam\shapez-win32-x64"
"FileMapping"
{
"LocalPath" "*"
"DepotPath" "."
"recursive" "1"
}
"FileExclusion" "*.pdb"
}

View File

@ -1,3 +0,0 @@
#!/bin/sh
yarn gulp standalone.prepareVDF
steamcmd.sh +login $STEAM_UPLOAD_SHAPEZ_ID $STEAM_UPLOAD_SHAPEZ_USER +run_app_build $PWD/built_vdfs/app-darwin-demo.vdf +quit

View File

@ -1,3 +0,0 @@
#!/bin/sh
yarn gulp standalone.prepareVDF
steamcmd.sh +login $STEAM_UPLOAD_SHAPEZ_ID $STEAM_UPLOAD_SHAPEZ_USER +run_app_build $PWD/built_vdfs/app-darwin.vdf +quit

View File

@ -1,3 +0,0 @@
@echo off
cmd /c yarn gulp standalone.prepareVDF
steamcmd +login %STEAM_UPLOAD_SHAPEZ_ID% %STEAM_UPLOAD_SHAPEZ_USER% +run_app_build %cd%/built_vdfs/app-winlinux-demo.vdf +quit

Some files were not shown because too many files have changed in this diff Show More