Merge steam-demo branch

pull/1434/head
tobspr 2 years ago
parent 09551fa0e5
commit 222ea8c261

@ -1,9 +0,0 @@
[[actions]]
name = "play"
path = "shapezio.exe"
platform = "windows"
[[actions]]
name = "play"
path = "play.sh"
platform = "linux"

@ -0,0 +1,66 @@
/**
* @type {Record<string, {
* standalone: boolean,
* environment?: 'dev' | 'staging' | 'prod',
* electronBaseDir?: string,
* steamAppId?: number,
* executableName?: string,
* buildArgs: {
* chineseVersion?: boolean,
* wegameVersion?: boolean,
* steamDemo?: 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": {
standalone: true,
executableName: "shapez",
steamAppId: 1318690,
buildArgs: {},
},
"standalone-steam-china": {n
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,
},
},
};
module.exports = { BUILD_VARIANTS };

139
gulp/cordova.js vendored

@ -1,139 +0,0 @@
const path = require("path");
const fs = require("fs");
const buildUtils = require("./buildutils");
function gulptasksCordova($, gulp, buildFolder) {
const cdvRes = path.join("..", "..", "res");
// Cleans up the app assets
// Removes all temporary folders used while optimizing the assets
gulp.task("cleanupAppAssetsBuiltFolder", () => {
return gulp
.src(path.join(cdvRes, "built"), { read: false, allowEmpty: true })
.pipe($.clean({ force: true }));
});
// Optimizes all built assets
gulp.task("optimizeBuiltAppAssets", () => {
return gulp
.src(path.join(cdvRes, "built", "**", "*.png"))
.pipe($.flatten())
.pipe($.imagemin([$.imagemin.optipng({ optimizationLevel: 1 })]))
.pipe(gulp.dest(path.join(cdvRes, "built")));
});
// Scales the icon resources
gulp.task("scaleIconIos", async () => {
const sizes = [
180,
60,
120,
76,
152,
40,
80,
57,
114,
72,
144,
167,
29,
58,
87,
50,
100,
167,
20,
1024,
24,
48,
55,
172,
196,
];
for (let i = 0; i < sizes.length; ++i) {
const size = sizes[i];
console.log("Scaling icon to", size, "x", size);
const img = await $.jimp.read(path.join(cdvRes, "ios", "icon-prefab.png"));
await img.resize(size, size).write(path.join(cdvRes, "built", "ios", "icon@" + size + ".png"));
}
});
gulp.task("copyOtherIosResources", () => {
return gulp
.src(path.join(cdvRes, "ios", "splash-prefab.png"))
.pipe($.rename("Default@2x~universal~anyany.png"))
.pipe(gulp.dest(path.join(cdvRes, "built", "ios")));
});
gulp.task("prepareIosRes", gulp.series("scaleIconIos", "copyOtherIosResources"));
gulp.task("copyAndroidResources", () => {
return gulp
.src(path.join(cdvRes, "android", "**", "*.*"))
.pipe(gulp.dest(path.join(cdvRes, "built", "android")));
});
gulp.task("prepareAndroidRes", gulp.series("copyAndroidResources"));
gulp.task(
"prepareCordovaAssets",
gulp.series(
"cleanupAppAssetsBuiltFolder",
gulp.parallel("prepareIosRes", "prepareAndroidRes"),
"optimizeBuiltAppAssets"
)
);
// Patches the config.xml by replacing the app id to app_beta
gulp.task("patchConfigXML", cb => {
const configUrl = path.join("..", "..", "config.xml");
let configContent = fs.readFileSync(configUrl).toString();
const version = buildUtils.getVersion();
configContent = configContent.replace("%VERSION%", version);
configContent = configContent.replace(' id="io.shapez.app" ', ' id="io.shapez.app_beta" ');
configContent = configContent.replace("<name>Shapez.io</name>", "<name>Shapez.io BETA</name>");
fs.writeFileSync(configUrl, configContent);
cb();
});
gulp.task("patchConfigXMLChangeStagingToProd", cb => {
const configUrl = path.join("..", "..", "config.xml");
let configContent = fs.readFileSync(configUrl).toString();
configContent = configContent.replace(' id="io.shapez.app_beta" ', ' id="io.shapez.app" ');
configContent = configContent.replace("<name>Shapez.io BETA</name>", "<name>Shapez.io</name>");
fs.writeFileSync(configUrl, configContent);
cb();
});
// Triggers a new build on phonegap
gulp.task("triggerPhonegapBuild", () => {
return gulp
.src("src/html/", { dot: false })
.pipe(
$.phonegapBuild({
isRepository: true,
appId: "3339820",
platforms: ["android", "ios"],
user: {
token: process.env.SHAPEZ_CLI_PHONEGAP_KEY,
},
})
)
.pipe(
$.phonegapBuild({
isRepository: true,
appId: "3537816",
platforms: ["android", "ios"],
user: {
token: process.env.SHAPEZ_CLI_PHONEGAP_KEY,
},
})
);
});
}
module.exports = {
gulptasksCordova,
};

@ -18,7 +18,6 @@ const $ = require("gulp-load-plugins")({
const envVars = [
"SHAPEZ_CLI_SERVER_HOST",
// "SHAPEZ_CLI_PHONEGAP_KEY",
"SHAPEZ_CLI_ALPHA_FTP_USER",
"SHAPEZ_CLI_ALPHA_FTP_PW",
"SHAPEZ_CLI_STAGING_FTP_USER",
@ -33,8 +32,7 @@ const envVars = [
for (let i = 0; i < envVars.length; ++i) {
if (!process.env[envVars[i]]) {
console.warn("Please set", envVars[i]);
// process.exit(1);
console.warn("Unset environment variable, might cause issues:", envVars[i]);
}
}
@ -57,7 +55,7 @@ const js = require("./js");
js.gulptasksJS($, gulp, buildFolder, browserSync);
const html = require("./html");
html.gulptasksHTML($, gulp, buildFolder, browserSync);
html.gulptasksHTML($, gulp, buildFolder);
const ftp = require("./ftp");
ftp.gulptasksFTP($, gulp, buildFolder);
@ -66,13 +64,11 @@ const docs = require("./docs");
docs.gulptasksDocs($, gulp, buildFolder);
const standalone = require("./standalone");
standalone.gulptasksStandalone($, gulp, buildFolder);
const releaseUploader = require("./release-uploader");
releaseUploader.gulptasksReleaseUploader($, gulp, buildFolder);
standalone.gulptasksStandalone($, gulp);
const translations = require("./translations");
translations.gulptasksTranslations($, gulp, buildFolder);
const { BUILD_VARIANTS } = require("./build_variants");
translations.gulptasksTranslations($, gulp);
///////////////////// BUILD TASKS /////////////////////
@ -142,9 +138,9 @@ gulp.task("main.webserver", () => {
/**
*
* @param {object} param0
* @param {"web"|"standalone"|"china"|"wegame"} param0.version
* @param {keyof typeof BUILD_VARIANTS} param0.version
*/
function serve({ version = "web" }) {
function serveHTML({ version = "web-dev" }) {
browserSync.init({
server: [buildFolder, path.join(baseDir, "mod_examples")],
port: 3005,
@ -168,10 +164,7 @@ function serve({ version = "web" }) {
gulp.watch(["../src/**/*.scss"], gulp.series("css.dev"));
// Watch .html files, those trigger a html rebuild
gulp.watch("../src/**/*.html", gulp.series(version === "web" ? "html.dev" : "html.standalone-dev"));
// Watch sound files
// gulp.watch(["../res_raw/sounds/**/*.mp3", "../res_raw/sounds/**/*.wav"], gulp.series("sounds.dev"));
gulp.watch("../src/**/*.html", gulp.series("html." + version + ".dev"));
// Watch translations
gulp.watch("../translations/**/*.yaml", gulp.series("translations.convertToJson"));
@ -204,27 +197,7 @@ function serve({ version = "web" }) {
return gulp.src(path).pipe(browserSync.reload({ stream: true }));
});
switch (version) {
case "web": {
gulp.series("js.dev.watch")(() => true);
break;
}
case "standalone": {
gulp.series("js.standalone-dev.watch")(() => true);
break;
}
case "china": {
gulp.series("china.js.dev.watch")(() => true);
break;
}
case "wegame": {
gulp.series("wegame.js.dev.watch")(() => true);
break;
}
default: {
throw new Error("Unknown version " + version);
}
}
gulp.series("js." + version + ".dev.watch")(() => true);
}
///////////////////// RUNNABLE TASKS /////////////////////
@ -238,9 +211,9 @@ gulp.task("step.deleteEmpty", cb => {
gulp.task("step.postbuild", gulp.series("imgres.cleanupUnusedCssInlineImages", "step.deleteEmpty"));
// Builds everything (dev)
// // Builds everything (dev)
gulp.task(
"build.dev",
"build.prepare.dev",
gulp.series(
"utils.cleanup",
"utils.copyAdditionalBuildFiles",
@ -252,146 +225,95 @@ gulp.task(
"imgres.copyImageResources",
"imgres.copyNonImageResources",
"translations.fullBuild",
"css.dev",
"html.dev"
)
);
// Builds everything (standalone -dev)
gulp.task(
"build.standalone.dev",
gulp.series(
"utils.cleanup",
"localConfig.findOrCreate",
"imgres.buildAtlas",
"imgres.atlasToJson",
"imgres.atlas",
"sounds.dev",
"imgres.copyImageResources",
"imgres.copyNonImageResources",
"translations.fullBuild",
"css.dev",
"html.standalone-dev"
"css.dev"
)
);
// Builds everything (staging)
gulp.task("step.staging.code", gulp.series("sounds.fullbuild", "translations.fullBuild", "js.staging"));
gulp.task(
"step.staging.mainbuild",
gulp.parallel("utils.copyAdditionalBuildFiles", "step.baseResources", "step.staging.code")
);
gulp.task("step.staging.all", gulp.series("step.staging.mainbuild", "css.prod", "html.staging"));
gulp.task("build.staging", gulp.series("utils.cleanup", "step.staging.all", "step.postbuild"));
// Builds everything (prod)
gulp.task("step.prod.code", gulp.series("sounds.fullbuild", "translations.fullBuild", "js.prod"));
gulp.task(
"step.prod.mainbuild",
gulp.parallel("utils.copyAdditionalBuildFiles", "step.baseResources", "step.prod.code")
);
gulp.task("step.prod.all", gulp.series("step.prod.mainbuild", "css.prod", "html.prod"));
gulp.task("build.prod", gulp.series("utils.cleanup", "step.prod.all", "step.postbuild"));
// Builds everything (standalone-beta)
gulp.task(
"step.standalone-beta.code",
gulp.series("sounds.fullbuildHQ", "translations.fullBuild", "js.standalone-beta")
);
gulp.task("step.standalone-beta.mainbuild", gulp.parallel("step.baseResources", "step.standalone-beta.code"));
gulp.task(
"step.standalone-beta.all",
gulp.series("step.standalone-beta.mainbuild", "css.prod-standalone", "html.standalone-beta")
);
gulp.task(
"build.standalone-beta",
gulp.series("utils.cleanup", "step.standalone-beta.all", "step.postbuild")
);
// Builds everything (standalone-prod)
// // Builds everything (staging)
// gulp.task("step.staging.code", gulp.series("sounds.fullbuild", "translations.fullBuild", "js.staging"));
// gulp.task(
// "step.staging.mainbuild",
// gulp.parallel("utils.copyAdditionalBuildFiles", "step.baseResources", "step.staging.code")
// );
// gulp.task("step.staging.all", gulp.series("step.staging.mainbuild", "css.prod", "html.staging"));
// gulp.task("build.staging", gulp.series("utils.cleanup", "step.staging.all", "step.postbuild"));
// // Builds everything (prod)
// gulp.task("step.prod.code", gulp.series("sounds.fullbuild", "translations.fullBuild", "js.prod"));
// gulp.task(
// "step.prod.mainbuild",
// gulp.parallel("utils.copyAdditionalBuildFiles", "step.baseResources", "step.prod.code")
// );
// gulp.task("step.prod.all", gulp.series("step.prod.mainbuild", "css.prod", "html.prod"));
// gulp.task("build.prod", gulp.series("utils.cleanup", "step.prod.all", "step.postbuild"));
// Builds everything for every variant
for (const variant in BUILD_VARIANTS) {
const data = BUILD_VARIANTS[variant];
const buildName = "build." + variant;
for (const prefix of ["", "china.", "wegame."]) {
gulp.task(
prefix + "step.standalone-prod.code",
gulp.series("sounds.fullbuildHQ", "translations.fullBuild", prefix + "js.standalone-prod")
buildName + ".code",
gulp.series(
data.standalone ? "sounds.fullbuildHQ" : "sounds.fullbuild",
"translations.fullBuild",
"js." + variant + ".prod"
)
);
gulp.task(
prefix + "step.standalone-prod.mainbuild",
gulp.parallel("step.baseResources", prefix + "step.standalone-prod.code")
);
gulp.task(buildName + ".resourcesAndCode", gulp.parallel("step.baseResources", buildName + ".code"));
gulp.task(
prefix + "step.standalone-prod.all",
gulp.series(prefix + "step.standalone-prod.mainbuild", "css.prod-standalone", "html.standalone-prod")
buildName + ".all",
gulp.series(buildName + ".resourcesAndCode", "css.prod-standalone", "html." + variant + ".prod")
);
gulp.task(buildName, gulp.series("utils.cleanup", buildName + ".all", "step.postbuild"));
// serve
gulp.task(
prefix + "build.standalone-prod",
gulp.series("utils.cleanup", prefix + "step.standalone-prod.all", "step.postbuild")
"serve." + variant,
gulp.series("build.prepare.dev", "html." + variant + ".dev", () => serveHTML({ version: variant }))
);
}
// OS X build and release upload
gulp.task(
"build.darwin64-prod",
gulp.series(
"build.standalone-prod",
"standalone.prepare",
"standalone.package.prod.darwin64.signManually"
)
);
// gulp.task(
// "build.darwin64-prod",
// gulp.series(
// "build.standalone-prod",
// "standalone.prepare",
// "standalone.package.prod.darwin64.signManually"
// )
// );
// Deploying!
gulp.task(
"main.deploy.alpha",
gulp.series("utils.requireCleanWorkingTree", "build.staging", "ftp.upload.alpha")
);
gulp.task(
"main.deploy.staging",
gulp.series("utils.requireCleanWorkingTree", "build.staging", "ftp.upload.staging")
);
gulp.task("main.deploy.prod", gulp.series("utils.requireCleanWorkingTree", "build.prod", "ftp.upload.prod"));
gulp.task("main.deploy.all", gulp.series("main.deploy.staging", "main.deploy.prod"));
// steam
gulp.task("regular.main.standalone", gulp.series("build.standalone-prod", "standalone.package.prod"));
// china
gulp.task(
"china.main.standalone",
gulp.series("china.build.standalone-prod", "china.standalone.package.prod")
);
// wegame
gulp.task(
"wegame.main.standalone",
gulp.series("wegame.build.standalone-prod", "wegame.standalone.package.prod")
);
// all (except wegame)
gulp.task("standalone.steam", gulp.series("regular.main.standalone", "china.main.standalone"));
gulp.task(
"standalone.all",
gulp.series("regular.main.standalone", "china.main.standalone", "wegame.main.standalone")
);
// Live-development
gulp.task(
"main.serveDev",
gulp.series("build.dev", () => serve({ version: "web" }))
);
gulp.task(
"main.serveStandalone",
gulp.series("build.standalone.dev", () => serve({ version: "standalone" }))
);
gulp.task(
"china.main.serveDev",
gulp.series("build.dev", () => serve({ version: "china" }))
"deploy.staging",
gulp.series("utils.requireCleanWorkingTree", "build.web-shapezio-beta", "ftp.upload.staging")
);
gulp.task(
"wegame.main.serveDev",
gulp.series("build.dev", () => serve({ version: "wegame" }))
"deploy.prod",
gulp.series("utils.requireCleanWorkingTree", "build.web-shapezio", "ftp.upload.prod")
);
gulp.task("default", gulp.series("main.serveDev"));
// // china
// gulp.task(
// "china.main.standalone",
// gulp.series("china.build.standalone-prod", "china.standalone.package.prod")
// );
// // wegame
// gulp.task(
// "wegame.main.standalone",
// gulp.series("wegame.build.standalone-prod", "wegame.standalone.package.prod")
// );
// // all (except wegame)
// gulp.task("standalone.steam", gulp.series("regular.main.standalone", "china.main.standalone"));
// gulp.task(
// "standalone.all",
// gulp.series("regular.main.standalone", "china.main.standalone", "wegame.main.standalone")
// );
gulp.task("default", gulp.series("serve.web-localhost"));

@ -2,6 +2,7 @@ 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);
@ -9,12 +10,20 @@ function computeIntegrityHash(fullPath, algorithm = "sha256") {
return algorithm + "-" + hash;
}
/**
* PROVIDES (per <variant>)
*
* html.<variant>.dev
* html.<variant>.prod
*/
function gulptasksHTML($, gulp, buildFolder) {
const commitHash = buildUtils.getRevision();
async function buildHtml(
apiUrl,
{ analytics = false, standalone = false, app = false, integrity = true, enableCachebust = true }
) {
async function buildHtml({
googleAnalytics = false,
standalone = false,
integrity = true,
enableCachebust = true,
}) {
function cachebust(url) {
if (enableCachebust) {
return buildUtils.cachebust(url, commitHash);
@ -22,7 +31,7 @@ function gulptasksHTML($, gulp, buildFolder) {
return url;
}
const hasLocalFiles = standalone || app;
const hasLocalFiles = standalone;
return gulp
.src("../src/html/" + (standalone ? "index.standalone.html" : "index.html"))
@ -31,13 +40,6 @@ function gulptasksHTML($, gulp, buildFolder) {
/** @this {Document} **/ function () {
const document = this;
// Preconnect to api
const prefetchLink = document.createElement("link");
prefetchLink.rel = "preconnect";
prefetchLink.href = apiUrl;
prefetchLink.setAttribute("crossorigin", "anonymous");
document.head.appendChild(prefetchLink);
// Append css
const css = document.createElement("link");
css.rel = "stylesheet";
@ -53,31 +55,8 @@ function gulptasksHTML($, gulp, buildFolder) {
}
document.head.appendChild(css);
// Append async css
// const asyncCss = document.createElement("link");
// asyncCss.rel = "stylesheet";
// asyncCss.type = "text/css";
// asyncCss.media = "none";
// asyncCss.setAttribute("onload", "this.media='all'");
// asyncCss.href = cachebust("async-resources.css");
// if (integrity) {
// asyncCss.setAttribute(
// "integrity",
// computeIntegrityHash(path.join(buildFolder, "async-resources.css"))
// );
// }
// document.head.appendChild(asyncCss);
if (app) {
// Append cordova link
const cdv = document.createElement("script");
cdv.src = "cordova.js";
cdv.type = "text/javascript";
document.head.appendChild(cdv);
}
// Google analytics
if (analytics) {
if (googleAnalytics) {
const tagManagerScript = document.createElement("script");
tagManagerScript.src =
"https://www.googletagmanager.com/gtag/js?id=UA-165342524-1";
@ -92,14 +71,6 @@ function gulptasksHTML($, gulp, buildFolder) {
gtag('config', 'UA-165342524-1', { anonymize_ip: true });
`;
document.head.appendChild(initScript);
const abTestingScript = document.createElement("script");
abTestingScript.setAttribute(
"src",
"https://www.googleoptimize.com/optimize.js?id=OPT-M5NHCV7"
);
abTestingScript.setAttribute("async", "");
document.head.appendChild(abTestingScript);
}
// Do not need to preload in app or standalone
@ -250,50 +221,25 @@ function gulptasksHTML($, gulp, buildFolder) {
.pipe(gulp.dest(buildFolder));
}
gulp.task("html.dev", () => {
return buildHtml("http://localhost:5005", {
analytics: false,
integrity: false,
enableCachebust: false,
});
});
gulp.task("html.staging", () => {
return buildHtml("https://api-staging.shapez.io", {
analytics: true,
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.prod", () => {
return buildHtml("https://analytics.shapez.io", {
analytics: true,
});
});
gulp.task("html.standalone-dev", () => {
return buildHtml("https://localhost:5005", {
analytics: false,
standalone: true,
integrity: false,
enableCachebust: false,
});
});
gulp.task("html.standalone-beta", () => {
return buildHtml("https://api-staging.shapez.io", {
analytics: false,
standalone: true,
enableCachebust: false,
});
});
gulp.task("html.standalone-prod", () => {
return buildHtml("https://analytics.shapez.io", {
analytics: false,
standalone: true,
enableCachebust: false,
gulp.task("html." + variant + ".prod", () => {
return buildHtml({
googleAnalytics: !data.standalone,
standalone: data.standalone,
integrity: true,
enableCachebust: !data.standalone,
});
});
});
}
}
module.exports = {

@ -1,260 +1,122 @@
const path = require("path");
const { BUILD_VARIANTS } = require("./build_variants");
function requireUncached(module) {
delete require.cache[require.resolve(module)];
return require(module);
}
/**
* PROVIDES (per <variant>)
*
* js.<variant>.dev.watch
* js.<variant>.dev
* js.<variant>.prod
*
*/
function gulptasksJS($, gulp, buildFolder, browserSync) {
//// DEV
gulp.task("js.dev.watch", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.config.js")({
watch: true,
})
)
)
.pipe(gulp.dest(buildFolder))
.pipe(browserSync.stream());
});
gulp.task("js.dev", () => {
return gulp
.src("../src/js/main.js")
.pipe($.webpackStream(requireUncached("./webpack.config.js")({})))
.pipe(gulp.dest(buildFolder));
});
//// DEV CHINA
gulp.task("china.js.dev.watch", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.config.js")({
watch: true,
chineseVersion: true,
})
)
)
.pipe(gulp.dest(buildFolder))
.pipe(browserSync.stream());
});
gulp.task("china.js.dev", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.config.js")({
chineseVersion: true,
})
)
)
.pipe(gulp.dest(buildFolder));
});
//// DEV WEGAME
gulp.task("wegame.js.dev.watch", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.config.js")({
watch: true,
wegameVersion: true,
})
)
)
.pipe(gulp.dest(buildFolder))
.pipe(browserSync.stream());
});
gulp.task("wegame.js.dev", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.config.js")({
wegameVersion: true,
})
)
)
.pipe(gulp.dest(buildFolder));
});
//// STAGING
gulp.task("js.staging.transpiled", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.production.config.js")({
enableAssert: true,
environment: "staging",
es6: false,
})
)
)
.pipe($.rename("bundle-transpiled.js"))
.pipe(gulp.dest(buildFolder));
});
gulp.task("js.staging.latest", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.production.config.js")({
enableAssert: true,
environment: "staging",
es6: true,
})
)
)
.pipe(gulp.dest(buildFolder));
});
gulp.task("js.staging", gulp.parallel("js.staging.transpiled", "js.staging.latest"));
//// PROD
gulp.task("js.prod.transpiled", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.production.config.js")({
enableAssert: false,
environment: "prod",
es6: false,
})
)
)
.pipe($.rename("bundle-transpiled.js"))
.pipe(gulp.dest(buildFolder))
.pipe(browserSync.stream());
});
gulp.task("js.prod.latest", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.production.config.js")({
enableAssert: false,
environment: "prod",
es6: true,
})
)
)
.pipe(gulp.dest(buildFolder))
.pipe(browserSync.stream());
});
gulp.task("js.prod", gulp.parallel("js.prod.transpiled", "js.prod.latest"));
//// STANDALONE
gulp.task("js.standalone-dev.watch", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.config.js")({
watch: true,
standalone: true,
})
)
)
.pipe(gulp.dest(buildFolder))
.pipe(browserSync.stream());
});
gulp.task("js.standalone-dev", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.config.js")({
standalone: true,
})
)
)
.pipe(gulp.dest(buildFolder));
});
gulp.task("js.standalone-beta", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.production.config.js")({
enableAssert: true,
environment: "staging",
es6: true,
standalone: true,
})
)
)
.pipe(gulp.dest(buildFolder));
});
gulp.task("js.standalone-prod", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.production.config.js")({
enableAssert: false,
environment: "prod",
es6: true,
standalone: true,
})
)
)
.pipe(gulp.dest(buildFolder));
});
gulp.task("china.js.standalone-prod", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.production.config.js")({
enableAssert: false,
environment: "prod",
es6: true,
standalone: true,
chineseVersion: true,
})
)
)
.pipe(gulp.dest(buildFolder));
});
gulp.task("wegame.js.standalone-prod", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.production.config.js")({
enableAssert: false,
environment: "prod",
es6: false,
standalone: true,
wegameVersion: true,
})
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(gulp.dest(buildFolder))
.pipe(browserSync.stream());
});
if (!data.standalone) {
// WEB
gulp.task("js." + variant + ".dev", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.config.js")({
...data.buildArgs,
})
)
)
.pipe(gulp.dest(buildFolder));
});
gulp.task("js." + variant + ".prod.transpiled", () => {
return gulp
.src("../src/js/main.js")
.pipe(
$.webpackStream(
requireUncached("./webpack.production.config.js")({
es6: false,
...data.buildArgs,
})
)
)
.pipe($.rename("bundle-transpiled.js"))
.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,
...data.buildArgs,
})
)
)
.pipe(gulp.dest(buildFolder));
});
gulp.task(
"js." + variant + ".prod",
gulp.parallel("js." + variant + ".prod.transpiled", "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 = {

@ -1,66 +0,0 @@
const path = require("path");
const fs = require("fs");
const execSync = require("child_process").execSync;
const { Octokit } = require("@octokit/rest");
const buildutils = require("./buildutils");
function gulptasksReleaseUploader($, gulp, buildFolder) {
const standaloneDir = path.join(__dirname, "..", "tmp_standalone_files");
const darwinApp = path.join(standaloneDir, "shapez.io-standalone-darwin-x64", "shapez.io-standalone.app");
const dmgName = "shapez.io-standalone.dmg";
const dmgPath = path.join(standaloneDir, "shapez.io-standalone-darwin-x64", dmgName);
gulp.task("standalone.uploadRelease.darwin64.cleanup", () => {
return gulp.src(dmgPath, { read: false, allowEmpty: true }).pipe($.clean({ force: true }));
});
gulp.task("standalone.uploadRelease.darwin64.compress", cb => {
console.log("Packaging disk image", dmgPath);
execSync(`hdiutil create -format UDBZ -srcfolder ${darwinApp} ${dmgPath}`);
cb();
});
gulp.task("standalone.uploadRelease.darwin64.upload", async cb => {
const currentTag = buildutils.getTag();
const octokit = new Octokit({
auth: process.env.SHAPEZ_CLI_GITHUB_TOKEN
});
const createdRelease = await octokit.request("POST /repos/{owner}/{repo}/releases", {
owner: process.env.SHAPEZ_CLI_GITHUB_USER,
repo: "shapez.io",
tag_name: currentTag,
name: currentTag,
draft: true
});
const { data: { id, upload_url } } = createdRelease;
console.log(`Created release ${id} for tag ${currentTag}`);
const dmgContents = fs.readFileSync(dmgPath);
const dmgSize = fs.statSync(dmgPath).size;
console.log("Uploading", dmgContents.length / 1024 / 1024, "MB to", upload_url);
await octokit.request({
method: "POST",
url: upload_url,
headers: {
"content-type": "application/x-apple-diskimage"
},
name: dmgName,
data: dmgContents
});
cb();
});
gulp.task("standalone.uploadRelease.darwin64",
gulp.series(
"standalone.uploadRelease.darwin64.cleanup",
"standalone.uploadRelease.darwin64.compress",
"standalone.uploadRelease.darwin64.upload"
));
}
module.exports = { gulptasksReleaseUploader };

@ -8,6 +8,7 @@ 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");
let signAsync;
try {
@ -17,56 +18,32 @@ try {
}
function gulptasksStandalone($, gulp) {
const targets = [
{
tempDestDir: path.join(__dirname, "..", "tmp_standalone_files"),
suffix: "",
taskPrefix: "",
electronBaseDir: path.join(__dirname, "..", "electron"),
steam: true,
},
{
tempDestDir: path.join(__dirname, "..", "tmp_standalone_files_china"),
suffix: "china",
taskPrefix: "china.",
electronBaseDir: path.join(__dirname, "..", "electron"),
steam: true,
},
{
tempDestDir: path.join(__dirname, "..", "tmp_standalone_files_wegame"),
suffix: "wegame",
taskPrefix: "wegame.",
electronBaseDir: path.join(__dirname, "..", "electron_wegame"),
steam: false,
},
];
for (const { tempDestDir, suffix, taskPrefix, electronBaseDir, steam } of targets) {
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");
gulp.task(taskPrefix + "standalone.prepare.cleanup", () => {
gulp.task(taskPrefix + ".prepare.cleanup", () => {
return gulp.src(tempDestDir, { read: false, allowEmpty: true }).pipe($.clean({ force: true }));
});
gulp.task(taskPrefix + "standalone.prepare.copyPrefab", () => {
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*"),
// fails on platforms which support symlinks
// https://github.com/gulpjs/gulp/issues/1427
// path.join(electronBaseDir, "node_modules", "**", "*"),
];
if (steam) {
requiredFiles.push(path.join(electronBaseDir, "steam_appid.txt"));
}
return gulp.src(requiredFiles, { base: electronBaseDir }).pipe(gulp.dest(tempDestBuildDir));
});
gulp.task(taskPrefix + "standalone.prepare.writePackageJson", cb => {
gulp.task(taskPrefix + ".prepare.writePackageJson", cb => {
const packageJsonString = JSON.stringify(
{
scripts: {
@ -85,52 +62,15 @@ function gulptasksStandalone($, gulp) {
cb();
});
gulp.task(taskPrefix + "standalone.prepareVDF", cb => {
if (!steam) {
cb();
return;
}
const hash = buildutils.getRevision();
const steampipeDir = path.join(__dirname, "steampipe", "scripts");
const templateContents = fs
.readFileSync(path.join(steampipeDir, "app.vdf.template"), { encoding: "utf-8" })
.toString();
const convertedContents = templateContents.replace("$DESC$", "Commit " + hash);
fs.writeFileSync(path.join(steampipeDir, "app.vdf"), convertedContents);
cb();
});
gulp.task(taskPrefix + "standalone.prepareVDF.darwin", cb => {
if (!steam) {
cb();
return;
}
const hash = buildutils.getRevision();
const steampipeDir = path.join(__dirname, "steampipe-darwin", "scripts");
const templateContents = fs
.readFileSync(path.join(steampipeDir, "app.vdf.template"), { encoding: "utf-8" })
.toString();
const convertedContents = templateContents.replace("$DESC$", "Commit " + hash);
fs.writeFileSync(path.join(steampipeDir, "app.vdf"), convertedContents);
cb();
});
gulp.task(taskPrefix + "standalone.prepare.minifyCode", () => {
gulp.task(taskPrefix + ".prepare.minifyCode", () => {
return gulp.src(path.join(electronBaseDir, "*.js")).pipe(gulp.dest(tempDestBuildDir));
});
gulp.task(taskPrefix + "standalone.prepare.copyGamefiles", () => {
gulp.task(taskPrefix + ".prepare.copyGamefiles", () => {
return gulp.src("../build/**/*.*", { base: "../build" }).pipe(gulp.dest(tempDestBuildDir));
});
gulp.task(taskPrefix + "standalone.killRunningInstances", cb => {
gulp.task(taskPrefix + ".killRunningInstances", cb => {
try {
execSync("taskkill /F /IM shapezio.exe");
} catch (ex) {
@ -140,14 +80,14 @@ function gulptasksStandalone($, gulp) {
});
gulp.task(
taskPrefix + "standalone.prepare",
taskPrefix + ".prepare",
gulp.series(
taskPrefix + "standalone.killRunningInstances",
taskPrefix + "standalone.prepare.cleanup",
taskPrefix + "standalone.prepare.copyPrefab",
taskPrefix + "standalone.prepare.writePackageJson",
taskPrefix + "standalone.prepare.minifyCode",
taskPrefix + "standalone.prepare.copyGamefiles"
taskPrefix + ".killRunningInstances",
taskPrefix + ".prepare.cleanup",
taskPrefix + ".prepare.copyPrefab",
taskPrefix + ".prepare.writePackageJson",
taskPrefix + ".prepare.minifyCode",
taskPrefix + ".prepare.copyGamefiles"
)
);
@ -158,11 +98,13 @@ function gulptasksStandalone($, gulp) {
* @param {function():void} cb
*/
function packageStandalone(platform, arch, cb, isRelease = true) {
const tomlFile = fs.readFileSync(path.join(__dirname, ".itch.toml"));
const privateArtifactsPath = "node_modules/shapez.io-private-artifacts";
let asar = steam;
if (steam && fs.existsSync(path.join(tempDestBuildDir, privateArtifactsPath))) {
// 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 };
}
@ -177,10 +119,10 @@ function gulptasksStandalone($, gulp) {
asar: asar,
executableName: "shapezio",
icon: path.join(electronBaseDir, "favicon"),
name: "shapez.io-standalone" + suffix,
name: "shapez-" + variant,
out: tempDestDir,
overwrite: true,
appBundleId: "tobspr.shapezio.standalone",
appBundleId: "tobspr.shapezio." + variant,
appCategoryType: "public.app-category.games",
...(isRelease &&
platform === "darwin" && {
@ -189,6 +131,7 @@ function gulptasksStandalone($, gulp) {
"hardenedRuntime": true,
"entitlements": "entitlements.plist",
"entitlements-inherit": "entitlements.plist",
// @ts-ignore
"signatureFlags": ["library"],
"version": "16.0.7",
},
@ -202,43 +145,42 @@ function gulptasksStandalone($, gulp) {
console.log("Packages created:", appPaths);
appPaths.forEach(appPath => {
if (!fs.existsSync(appPath)) {
console.error("Bad app path gotten:", appPath);
console.error("Bad app path:", appPath);
return;
}
if (steam) {
if (variantData.steamAppId) {
fs.writeFileSync(
path.join(appPath, "LICENSE"),
fs.readFileSync(path.join(__dirname, "..", "LICENSE"))
);
fse.copySync(
path.join(tempDestBuildDir, "steam_appid.txt"),
path.join(appPath, "steam_appid.txt")
fs.writeFileSync(
path.join(appPath, "steam_appid.txt"),
String(variantData.steamAppId)
);
fs.writeFileSync(path.join(appPath, ".itch.toml"), tomlFile);
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) {
fse.copySync(
path.join(tempDestBuildDir, "steam_appid.txt"),
// Needs special location
fs.writeFileSync(
path.join(
path.join(
appPath,
"shapez.io-standalone.app",
"Contents",
"MacOS"
),
appPath,
"shapez.io-standalone.app",
"Contents",
"MacOS",
"steam_appid.txt"
)
),
String(variantData.steamAppId)
);
}
}
@ -255,7 +197,7 @@ function gulptasksStandalone($, gulp) {
}
// Manual signing with patched @electron/osx-sign (we need --no-strict)
gulp.task(taskPrefix + "standalone.package.prod.darwin64.signManually", cb =>
gulp.task(taskPrefix + ".package.darwin64", cb =>
packageStandalone(
"darwin",
"x64",
@ -327,29 +269,44 @@ function gulptasksStandalone($, gulp) {
)
);
gulp.task(taskPrefix + "standalone.package.prod.win64", cb => packageStandalone("win32", "x64", cb));
gulp.task(taskPrefix + "standalone.package.prod.linux64", cb =>
packageStandalone("linux", "x64", cb)
);
gulp.task(taskPrefix + "standalone.package.prod.darwin64", cb =>
packageStandalone("darwin", "x64", cb)
);
gulp.task(taskPrefix + "standalone.package.prod.darwin64.unsigned", cb =>
packageStandalone("darwin", "x64", 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 + "standalone.package.prod",
taskPrefix + ".build-from-windows",
gulp.series(
taskPrefix + "standalone.prepare",
gulp.parallel(
taskPrefix + "standalone.package.prod.win64",
taskPrefix + "standalone.package.prod.linux64",
taskPrefix + "standalone.package.prod.darwin64"
)
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 steampipeDir = path.join(__dirname, platform, "scripts");
for (const buildVariant of ["app", "app-demo"]) {
const templateContents = fs
.readFileSync(path.join(steampipeDir, buildVariant + ".vdf.template"), {
encoding: "utf-8",
})
.toString();
const convertedContents = templateContents.replace(
"$DESC$",
platform + " " + buildVariant + " version " + version + ", commit " + hash
);
fs.writeFileSync(path.join(steampipeDir, buildVariant + ".vdf"), convertedContents);
}
}
cb();
});
}
module.exports = { gulptasksStandalone };

@ -0,0 +1,14 @@
"appbuild"
{
"appid" "1930750"
"desc" "$DESC$"
"buildoutput" "$STEAMPIPE_DIR$/steamtmp"
"contentroot" ""
"setlive" ""
"preview" "0"
"local" ""
"depots"
{
"1930756" "$STEAMPIPE_DIR$/scripts/demo-darwin.vdf"
}
}

@ -2,13 +2,13 @@
{
"appid" "1318690"
"desc" "$DESC$"
"buildoutput" "/Users/tobiasspringer/work/shapez.io/gulp/steampipe-darwin/steamtmp"
"buildoutput" "$PROJECT_DIR$/gulp/steampipe-darwin/steamtmp"
"contentroot" ""
"setlive" ""
"preview" "0"
"local" ""
"depots"
{
"1318693" "/Users/tobiasspringer/work/shapez.io/gulp/steampipe-darwin/scripts/darwin.vdf"
"1318693" "$PROJECT_DIR$/gulp/steampipe-darwin/scripts/darwin.vdf"
}
}

@ -1,7 +1,7 @@
"DepotBuildConfig"
{
"DepotID" "1318693"
"contentroot" "/Users/tobiasspringer/work/shapez.io/tmp_standalone_files/shapez.io-standalone-darwin-x64"
"contentroot" "$PROJECT_DIR$/tmp_standalone_files/shapez.io-standalone-darwin-x64"
"FileMapping"
{
"LocalPath" "*"

@ -0,0 +1,12 @@
"DepotBuildConfig"
{
"DepotID" "1930756"
"contentroot" "$PROJECT_DIR$/tmp_standalone_files/shapez.io-demo-darwin-x64"
"FileMapping"
{
"LocalPath" "*"
"DepotPath" "."
"recursive" "1"
}
"FileExclusion" "*.pdb"
}

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

@ -0,0 +1,17 @@
"appbuild"
{
"appid" "1930750"
"desc" "$DESC$"
"buildoutput" "$STEAMPIPE_DIR$\steamtemp"
"contentroot" ""
"setlive" ""
"preview" "0"
"local" ""
"depots"
{
"1930753" "$STEAMPIPE_DIR$\scripts\demo-windows.vdf"
"1930754" "$STEAMPIPE_DIR$\scripts\demo-china-windows.vdf"
"1930752" "$STEAMPIPE_DIR$\scripts\demo-linux.vdf"
"1930755" "$STEAMPIPE_DIR$\scripts\demo-china-linux.vdf"
}
}

@ -2,16 +2,16 @@
{
"appid" "1318690"
"desc" "$DESC$"
"buildoutput" "C:\work\shapez\shapez.io\gulp\steampipe\steamtemp"
"buildoutput" "$STEAMPIPE_DIR$\steamtemp"
"contentroot" ""
"setlive" ""
"preview" "0"
"local" ""
"depots"
{
"1318691" "C:\work\shapez\shapez.io\gulp\steampipe\scripts\windows.vdf"
"1318694" "C:\work\shapez\shapez.io\gulp\steampipe\scripts\china-windows.vdf"
"1318692" "C:\work\shapez\shapez.io\gulp\steampipe\scripts\linux.vdf"
"1318695" "C:\work\shapez\shapez.io\gulp\steampipe\scripts\china-linux.vdf"
"1318691" "$STEAMPIPE_DIR$\scripts\windows.vdf"
"1318694" "$STEAMPIPE_DIR$\scripts\china-windows.vdf"
"1318692" "$STEAMPIPE_DIR$\scripts\linux.vdf"
"1318695" "$STEAMPIPE_DIR$\scripts\china-linux.vdf"
}
}

@ -1,7 +1,7 @@
"DepotBuildConfig"
{
"DepotID" "1318695"
"contentroot" "C:\work\shapez\shapez.io\tmp_standalone_files_china\shapez.io-standalonechina-linux-x64"
"contentroot" "$PROJECT_DIR$\tmp_standalone_files_china\shapez.io-standalonechina-linux-x64"
"FileMapping"
{
"LocalPath" "*"

@ -1,7 +1,7 @@
"DepotBuildConfig"
{
"DepotID" "1318694"
"contentroot" "C:\work\shapez\shapez.io\tmp_standalone_files_china\shapez.io-standalonechina-win32-x64"
"contentroot" "$PROJECT_DIR$\shapez.io-standalonechina-win32-x64"
"FileMapping"
{
"LocalPath" "*"

@ -0,0 +1,12 @@
"DepotBuildConfig"
{
"DepotID" "1930755"
"contentroot" "$PROJECT_DIR$\tmp_standalone_files_china\shapez.io-demochina-linux-x64"
"FileMapping"
{
"LocalPath" "*"
"DepotPath" "."
"recursive" "1"
}
"FileExclusion" "*.pdb"
}

@ -0,0 +1,12 @@
"DepotBuildConfig"
{
"DepotID" "1930754"
"contentroot" "$PROJECT_DIR$\tmp_standalone_files_china\shapez.io-demochina-win32-x64"
"FileMapping"
{
"LocalPath" "*"
"DepotPath" "."
"recursive" "1"
}
"FileExclusion" "*.pdb"
}

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

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

@ -1,7 +1,7 @@
"DepotBuildConfig"
{
"DepotID" "1318692"
"contentroot" "C:\work\shapez\shapez.io\tmp_standalone_files\shapez.io-standalone-linux-x64"
"contentroot" "$PROJECT_DIR$\tmp_standalone_files\shapez.io-standalone-linux-x64"
"FileMapping"
{
"LocalPath" "*"

@ -1,7 +1,7 @@
"DepotBuildConfig"
{
"DepotID" "1318691"
"contentroot" "C:\work\shapez\shapez.io\tmp_standalone_files\shapez.io-standalone-win32-x64"
"contentroot" "$PROJECT_DIR$\tmp_standalone_files\shapez.io-standalone-win32-x64"
"FileMapping"
{
"LocalPath" "*"

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

@ -1,4 +1,3 @@
@echo off
cmd /c yarn gulp standalone.prepareVDF
steamcmd +login %STEAM_UPLOAD_SHAPEZ_ID% %STEAM_UPLOAD_SHAPEZ_USER% +run_app_build %cd%/scripts/app.vdf +quit
start https://partner.steamgames.com/apps/builds/1318690

@ -3,10 +3,15 @@
const path = require("path");
const webpack = require("webpack");
const { getRevision, getVersion, getAllResourceImages } = require("./buildutils");
const lzString = require("lz-string");
const CircularDependencyPlugin = require("circular-dependency-plugin");
module.exports = ({ watch = false, standalone = false, chineseVersion = false, wegameVersion = false }) => {
module.exports = ({
watch = false,
standalone = false,
chineseVersion = false,
wegameVersion = false,
steamDemo = false,
}) => {
return {
mode: "development",
devtool: "cheap-source-map",
@ -31,16 +36,13 @@ module.exports = ({ watch = false, standalone = false, chineseVersion = false, w
"window.assert(false, 'abstract method called of: ' + (this.name || (this.constructor && this.constructor.name)));",
G_HAVE_ASSERT: "true",
G_APP_ENVIRONMENT: JSON.stringify("dev"),
G_TRACKING_ENDPOINT: JSON.stringify(
lzString.compressToEncodedURIComponent("http://localhost:10005/v1")
),
G_CHINA_VERSION: JSON.stringify(chineseVersion),
G_WEGAME_VERSION: JSON.stringify(wegameVersion),
G_IS_DEV: "true",
G_IS_RELEASE: "false",
G_IS_MOBILE_APP: "false",
G_IS_BROWSER: "true",
G_IS_STANDALONE: standalone ? "true" : "false",
G_IS_STANDALONE: JSON.stringify(standalone),
G_IS_STEAM_DEMO: JSON.stringify(steamDemo),
G_BUILD_TIME: "" + new Date().getTime(),
G_BUILD_COMMIT_HASH: JSON.stringify(getRevision()),
G_BUILD_VERSION: JSON.stringify(getVersion()),

@ -3,24 +3,24 @@
const path = require("path");
const webpack = require("webpack");
const { getRevision, getVersion, getAllResourceImages } = require("./buildutils");
const lzString = require("lz-string");
const TerserPlugin = require("terser-webpack-plugin");
const StringReplacePlugin = require("string-replace-webpack-plugin");
const UnusedFilesPlugin = require("unused-files-webpack-plugin").UnusedFilesWebpackPlugin;
module.exports = ({
enableAssert = false,
environment,
es6 = false,
standalone = false,
isBrowser = true,
mobileApp = false,
chineseVersion = false,
wegameVersion = false,
steamDemo = false,
}) => {
const globalDefs = {
assert: enableAssert ? "window.assert" : "false && window.assert",
assert: "false && window.assert",
assertAlways: "window.assert",
abstract: "window.assert(false, 'abstract method called');",
G_IS_DEV: "false",
@ -29,13 +29,10 @@ module.exports = ({
G_WEGAME_VERSION: JSON.stringify(wegameVersion),
G_IS_RELEASE: environment === "prod" ? "true" : "false",
G_IS_STANDALONE: standalone ? "true" : "false",
G_IS_STEAM_DEMO: JSON.stringify(steamDemo),
G_IS_BROWSER: isBrowser ? "true" : "false",
G_IS_MOBILE_APP: mobileApp ? "true" : "false",
G_TRACKING_ENDPOINT: JSON.stringify(
lzString.compressToEncodedURIComponent("https://tracking.shapez.io/v1")
),
G_APP_ENVIRONMENT: JSON.stringify(environment),
G_HAVE_ASSERT: enableAssert ? "true" : "false",
G_HAVE_ASSERT: "false",
G_BUILD_TIME: "" + new Date().getTime(),
G_BUILD_COMMIT_HASH: JSON.stringify(getRevision()),
G_BUILD_VERSION: JSON.stringify(getVersion()),

Binary file not shown.

Before

Width:  |  Height:  |  Size: 236 KiB

After

Width:  |  Height:  |  Size: 210 KiB

@ -52,6 +52,11 @@
}
}
.playtimeDisclaimer {
@include S(margin-bottom, 10px);
@include PlainText;
}
.steamLinkButton {
@include IncreasedClickArea(5px);
@include S(margin, 0);

@ -1,10 +1,16 @@
#ingame_HUD_CatMemes {
#ingame_HUD_SteamCapsule {
position: absolute;
@include S(width, 150px);
@include S(height, 150px);
background: transparent center center / contain no-repeat;
@include S(height, 119px);
background: transparent center center / cover no-repeat;
right: 0;
pointer-events: all;
overflow: hidden;
@include S(right, 10px);
border: D(2px) solid #000;
@include S(border-radius, $globalBorderRadius);
cursor: pointer;
@include S(bottom, 150px);
& {
@ -12,6 +18,10 @@
background-image: uiResource("res/ui/memes/cat1.png") !important;
}
&:hover {
opacity: 0.95;
}
@include InlineAnimation(0.5s ease-in-out) {
0% {
transform: translateX(100%);

@ -57,7 +57,7 @@
@import "ingame_hud/shape_viewer";
@import "ingame_hud/sandbox_controller";
@import "ingame_hud/standalone_advantages";
@import "ingame_hud/cat_memes";
@import "ingame_hud/steam_capsule";
@import "ingame_hud/puzzle_back_to_menu";
@import "ingame_hud/puzzle_editor_review";
@import "ingame_hud/puzzle_dlc_logo";
@ -105,6 +105,7 @@ ingame_HUD_Waypoints_Hint,
ingame_HUD_WatermarkClicker,
ingame_HUD_Watermark,
ingame_HUD_ColorBlindBelowTileHelper,
ingame_HUD_SteamCapsule,
ingame_HUD_SandboxController,
// Overlays
@ -118,8 +119,7 @@ ingame_HUD_StandaloneAdvantages,
ingame_HUD_UnlockNotification,
ingame_HUD_PuzzleCompleteNotification,
ingame_HUD_SettingsMenu,
ingame_HUD_ModalDialogs,
ingame_HUD_CatMemes;
ingame_HUD_ModalDialogs;
$zindex: 100;
@ -132,7 +132,7 @@ $zindex: 100;
}
body.uiHidden {
> div {
> div:not(.ingameDialog):not(#ingame_HUD_SettingsMenu):not(#ingame_HUD_ModalDialogs):not(#ingame_HUD_UnlockNotification):not(#ingame_HUD_PuzzleCompleteNotification) {
display: none !important;
}
}

@ -97,7 +97,7 @@
}
.standaloneBanner {
background: rgb(216, 79, 76);
background: rgba(12, 168, 93, 0.957);
@include S(border-radius, $globalBorderRadius);
box-sizing: border-box;
@include S(padding, 15px);
@ -129,8 +129,18 @@
@include S(padding-left, 20px);
li {
@include Text;
color: #fff;
}
}
strong {
margin: 0;
}
.playtimeDisclaimer {
color: #fff;
@include S(margin-top, 15px);
@include SuperSmallText;
}
.steamLink {
align-self: center;
@ -153,6 +163,12 @@
opacity: 0.9;
}
@include InlineAnimation(1s ease-in-out infinite) {
50% {
transform: scale(1.02, 1.03);
}
}
> .discount {
position: absolute;
@include S(top, -7px);
@ -186,9 +202,8 @@
img {
@include S(width, 300px);
}
position: relative;
@include S(left, -22px);
@include S(left, -8px);
.updateLabel {
position: absolute;
@ -677,25 +692,19 @@
}
.footer {
display: grid;
display: flex;
flex-grow: 1;
justify-content: center;
align-items: flex-end;
width: 100%;
grid-template-columns: auto auto auto 1fr;
@include S(padding, 10px);
box-sizing: border-box;
@include S(grid-gap, 4px);
&.noLinks {
grid-template-columns: auto 1fr;
}
&.wegameDisclaimer {
@include SuperSmallText;
display: grid;
justify-content: center;
grid-template-columns: 1fr auto 1fr;
text-align: center;
> .disclaimer {

@ -47,6 +47,10 @@
align-self: end;
margin-top: auto;
&.noabout {
align-self: start;
}
@include StyleBelowWidth($layoutBreak) {
margin-top: 0;
display: grid;

@ -41,7 +41,6 @@ import { ModsState } from "./states/mods";
/**
* @typedef {import("./platform/achievement_provider").AchievementProviderInterface} AchievementProviderInterface
* @typedef {import("./platform/game_analytics").GameAnalyticsInterface} GameAnalyticsInterface
* @typedef {import("./platform/sound").SoundInterface} SoundInterface
* @typedef {import("./platform/storage").StorageInterface} StorageInterface
*/
@ -118,7 +117,7 @@ export class Application {
/** @type {AnalyticsInterface} */
this.analytics = null;
/** @type {GameAnalyticsInterface} */
/** @type {ShapezGameAnalytics} */
this.gameAnalytics = null;
this.initPlatformDependentInstances();
@ -227,12 +226,10 @@ export class Application {
window.addEventListener("resize", () => this.checkResize(), true);
window.addEventListener("orientationchange", () => this.checkResize(), true);
if (!G_IS_MOBILE_APP && !IS_MOBILE) {
window.addEventListener("mousemove", this.handleMousemove.bind(this));
window.addEventListener("mouseout", this.handleMousemove.bind(this));
window.addEventListener("mouseover", this.handleMousemove.bind(this));
window.addEventListener("mouseleave", this.handleMousemove.bind(this));
}
window.addEventListener("mousemove", this.handleMousemove.bind(this));
window.addEventListener("mouseout", this.handleMousemove.bind(this));
window.addEventListener("mouseover", this.handleMousemove.bind(this));
window.addEventListener("mouseleave", this.handleMousemove.bind(this));
// Unload events
window.addEventListener("beforeunload", this.onBeforeUnload.bind(this), true);

@ -17,6 +17,10 @@ export function getLogoSprite() {
return "logo_wegame.png";
}
if (G_IS_STEAM_DEMO) {
return "logo_demo.png";
}
if (G_CHINA_VERSION) {
return "logo_cn.png";
}

@ -117,13 +117,7 @@ export const globalConfig = {
rendering: {},
debug: require("./config.local").default,
currentDiscount: {
amount: 50,
from: Date.parse("May 23 2022 17:00 +2:00"),
until: Date.parse("May 30 2022 23:59 +2:00"),
active: false, // computed later
},
currentDiscount: 0,
// Secret vars
info: {
@ -169,8 +163,3 @@ if (G_IS_DEV && globalConfig.debug.noArtificialDelays) {
globalConfig.warmupTimeSecondsFast = 0;
globalConfig.warmupTimeSecondsRegular = 0;
}
globalConfig.currentDiscount.active =
!G_IS_STANDALONE &&
new Date().getTime() < globalConfig.currentDiscount.until &&
new Date().getTime() > globalConfig.currentDiscount.from;

@ -19,12 +19,10 @@ export function setGlobalApp(app) {
export const BUILD_OPTIONS = {
HAVE_ASSERT: G_HAVE_ASSERT,
APP_ENVIRONMENT: G_APP_ENVIRONMENT,
TRACKING_ENDPOINT: G_TRACKING_ENDPOINT,
CHINA_VERSION: G_CHINA_VERSION,
WEGAME_VERSION: G_WEGAME_VERSION,
IS_DEV: G_IS_DEV,
IS_RELEASE: G_IS_RELEASE,
IS_MOBILE_APP: G_IS_MOBILE_APP,
IS_BROWSER: G_IS_BROWSER,
IS_STANDALONE: G_IS_STANDALONE,
BUILD_TIME: G_BUILD_TIME,

@ -2,7 +2,6 @@
import { Application } from "../application";
/* typehints:end */
import { ExplainedResult } from "./explained_result";
import { queryParamOptions } from "./query_parameters";
import { ReadWriteProxy } from "./read_write_proxy";
export class RestrictionManager extends ReadWriteProxy {
@ -56,13 +55,12 @@ export class RestrictionManager extends ReadWriteProxy {
* @returns {boolean}
*/
isLimitedVersion() {
if (G_IS_STANDALONE) {
// Standalone is never limited
return false;
if (G_IS_STEAM_DEMO) {
return true;
}
if (queryParamOptions.embedProvider === "gamedistribution") {
// also full version on gamedistribution
if (G_IS_STANDALONE) {
// Standalone is never limited
return false;
}

@ -2,29 +2,6 @@ import { T } from "../translations";
const bigNumberSuffixTranslationKeys = ["thousands", "millions", "billions", "trillions"];
/**
* Returns if this platform is android
* @returns {boolean}
*/
export function isAndroid() {
if (!G_IS_MOBILE_APP) {
return false;
}
const platform = window.device.platform;
return platform === "Android" || platform === "amazon-fireos";
}
/**
* Returns if this platform is iOs
* @returns {boolean}
*/
export function isIos() {
if (!G_IS_MOBILE_APP) {
return false;
}
return window.device.platform === "iOS";
}
/**
* Returns a platform name
* @returns {"android" | "browser" | "ios" | "standalone" | "unknown"}
@ -34,10 +11,6 @@ export function getPlatformName() {
return "standalone";
} else if (G_IS_BROWSER) {
return "browser";
} else if (G_IS_MOBILE_APP && isAndroid()) {
return "android";
} else if (G_IS_MOBILE_APP && isIos()) {
return "ios";
}
return "unknown";
}
@ -456,7 +429,7 @@ export function isSupportedBrowser() {
// and if not iOS Chrome check
// so use the below updated condition
if (G_IS_MOBILE_APP || G_IS_STANDALONE) {
if (G_IS_STANDALONE) {
return true;
}

@ -1,21 +0,0 @@
import { makeDiv } from "../../../core/utils";
import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach";
const memeShowIntervalSeconds = 70 * 60;
const memeShowDuration = 5;
export class HUDCatMemes extends BaseHUDPart {
createElements(parent) {
this.element = makeDiv(parent, "ingame_HUD_CatMemes");
}
initialize() {
this.domAttach = new DynamicDomAttach(this.root, this.element);
}
update() {
const now = this.root.time.realtimeNow();
this.domAttach.update(now % memeShowIntervalSeconds > memeShowIntervalSeconds - memeShowDuration);
}
}

@ -125,7 +125,11 @@ export class HUDModalDialogs extends BaseHUDPart {
dialog.buttonSignals.getStandalone.add(() => {
this.app.analytics.trackUiClick("demo_dialog_click");
window.open(THIRDPARTY_URLS.stanaloneCampaignLink + "/shapez_demo_dialog");
window.open(
THIRDPARTY_URLS.stanaloneCampaignLink +
"/shapez_demo_dialog" +
(G_IS_STEAM_DEMO ? "_steamdemo" : "")
);
});
return dialog.buttonSignals;

@ -5,8 +5,6 @@ import { T } from "../../../translations";
import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach";
const showIntervalSeconds = 9 * 60;
export class HUDStandaloneAdvantages extends BaseHUDPart {
createElements(parent) {
this.background = makeDiv(parent, "ingame_HUD_StandaloneAdvantages", ["ingameDialog"]);
@ -33,10 +31,11 @@ export class HUDStandaloneAdvantages extends BaseHUDPart {
</div>
<div class="lowerBar">
<div class="playtimeDisclaimer">${T.demoBanners.playtimeDisclaimer}</div>
<button class="steamLinkButton ${A_B_TESTING_LINK_TYPE}">
${
globalConfig.currentDiscount.active
? `<span class='discount'>${globalConfig.currentDiscount.amount}% off!</span>`
globalConfig.currentDiscount > 0
? `<span class='discount'>${globalConfig.currentDiscount}% off!</span>`
: ""
}
</button>
@ -46,13 +45,15 @@ export class HUDStandaloneAdvantages extends BaseHUDPart {
);
this.trackClicks(this.contentDiv.querySelector("button.steamLinkButton"), () => {
const discount = globalConfig.currentDiscount.active
? "_discount" + globalConfig.currentDiscount.amount
: "";
const discount =
globalConfig.currentDiscount > 0 ? "_discount" + globalConfig.currentDiscount : "";
this.root.app.analytics.trackUiClick("standalone_advantage_visit_steam");
this.root.app.platformWrapper.openExternalLink(
THIRDPARTY_URLS.stanaloneCampaignLink + "/shapez_std_advg" + discount
THIRDPARTY_URLS.stanaloneCampaignLink +
"/shapez_std_advg" +
discount +
(G_IS_STEAM_DEMO ? "_steamdemo" : "")
);
this.close();
});
@ -62,6 +63,22 @@ export class HUDStandaloneAdvantages extends BaseHUDPart {
});
}
get showIntervalSeconds() {
switch (this.root.app.gameAnalytics.abtVariant) {
case "0":
return 5 * 60;
case "1":
return 10 * 60;
case "2":
default:
return 15 * 60;
case "3":
return 20 * 60;
case "4":
return 1e14;
}
}
initialize() {
this.domAttach = new DynamicDomAttach(this.root, this.background, {
attachClass: "visible",
@ -86,7 +103,7 @@ export class HUDStandaloneAdvantages extends BaseHUDPart {
}
update() {
if (!this.visible && this.root.time.now() - this.lastShown > showIntervalSeconds) {
if (!this.visible && this.root.time.now() - this.lastShown > this.showIntervalSeconds) {
this.show();
}

@ -0,0 +1,31 @@
import { globalConfig, THIRDPARTY_URLS } from "../../../core/config";
import { makeDiv } from "../../../core/utils";
import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach";
const showCapsuleAfter = 30 * 60;
export class HUDSteamCapsule extends BaseHUDPart {
createElements(parent) {
this.element = makeDiv(parent, "ingame_HUD_SteamCapsule");
}
initialize() {
const discount = globalConfig.currentDiscount > 0 ? "_discount" + globalConfig.currentDiscount : "";
this.domAttach = new DynamicDomAttach(this.root, this.element);
this.trackClicks(this.element, () => {
this.root.app.platformWrapper.openExternalLink(
THIRDPARTY_URLS.stanaloneCampaignLink +
"/shapez_steamcapsule" +
discount +
(G_IS_STEAM_DEMO ? "_steamdemo" : "")
);
});
}
update() {
this.domAttach.update(this.root.time.now() > showCapsuleAfter);
}
}

@ -75,7 +75,7 @@ export class HUDUnlockNotification extends BaseHUDPart {
<div class="rewardName">
${T.ingame.levelCompleteNotification.unlockText.replace("<reward>", rewardName)}
</div>
<div class="rewardDesc">
${T.storyRewards[reward].desc}
</div>
@ -131,6 +131,13 @@ export class HUDUnlockNotification extends BaseHUDPart {
this.root.hud.signals.unlockNotificationFinished.dispatch();
if (
this.root.hubGoals.level === 7 &&
this.root.app.restrictionMgr.getIsStandaloneMarketingActive()
) {
this.root.hud.parts.standaloneAdvantages.show();
}
if (!this.root.app.settings.getAllSettings().offerHints) {
return;
}

@ -2,66 +2,35 @@ import { globalConfig, THIRDPARTY_URLS } from "../../../core/config";
import { makeDiv } from "../../../core/utils";
import { T } from "../../../translations";
import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach";
const watermarkShowIntervalSeconds = G_IS_DEV ? 120 : 7 * 60;
const watermarkShowDuration = 5;
export class HUDWatermark extends BaseHUDPart {
createElements(parent) {
this.element = makeDiv(
parent,
"ingame_HUD_Watermark",
[],
`
<strong>${T.ingame.watermark.title}</strong>
<p>${T.ingame.watermark.desc}
</p>
`
);
this.linkElement = makeDiv(
parent,
"ingame_HUD_WatermarkClicker",
globalConfig.currentDiscount.active ? ["withDiscount"] : [],
globalConfig.currentDiscount > 0 ? ["withDiscount"] : [],
T.ingame.watermark.get_on_steam +
(globalConfig.currentDiscount.active
? `<span class='discount'>${globalConfig.currentDiscount.amount}% off!</span>`
(globalConfig.currentDiscount > 0
? `<span class='discount'>${globalConfig.currentDiscount}% off!</span>`
: "")
);
this.trackClicks(this.linkElement, () => {
this.root.app.analytics.trackUiClick("watermark_click_2_direct");
const discount = globalConfig.currentDiscount.active
? "_discount" + globalConfig.currentDiscount.amount
: "";
const discount =
globalConfig.currentDiscount > 0 ? "_discount" + globalConfig.currentDiscount : "";
this.root.app.platformWrapper.openExternalLink(
THIRDPARTY_URLS.stanaloneCampaignLink + "/shapez_watermark" + discount
THIRDPARTY_URLS.stanaloneCampaignLink +
"/shapez_watermark" +
discount +
(G_IS_STEAM_DEMO ? "_steamdemo" : "")
);
});
}
initialize() {
this.trackClicks(this.element, this.onWatermarkClick);
initialize() {}
this.domAttach = new DynamicDomAttach(this.root, this.element, {
attachClass: "visible",
timeToKeepSeconds: 0.5,
});
}
update() {
this.domAttach.update(
this.root.time.realtimeNow() % watermarkShowIntervalSeconds < watermarkShowDuration
);
}
onWatermarkClick() {
this.root.app.analytics.trackUiClick("watermark_click_2_new");
this.root.hud.parts.standaloneAdvantages.show();
}
update() {}
/**
*
@ -70,7 +39,7 @@ export class HUDWatermark extends BaseHUDPart {
drawOverlays(parameters) {
const w = this.root.gameWidth;
parameters.context.fillStyle = "rgba(230, 230, 230, 0.9)";
parameters.context.fillStyle = "rgba(20, 30, 40, 0.25)";
parameters.context.font = "bold " + this.root.app.getEffectiveUiScale() * 40 + "px GameFont";
parameters.context.textAlign = "center";
parameters.context.fillText(

@ -31,11 +31,9 @@ import { IS_MOBILE } from "../../core/config";
import { HUDKeybindingOverlay } from "../hud/parts/keybinding_overlay";
import { HUDWatermark } from "../hud/parts/watermark";
import { HUDStandaloneAdvantages } from "../hud/parts/standalone_advantages";
import { HUDCatMemes } from "../hud/parts/cat_memes";
import { HUDSteamCapsule } from "../hud/parts/steam_capsule";
import { HUDPartTutorialHints } from "../hud/parts/tutorial_hints";
import { HUDInteractiveTutorial } from "../hud/parts/interactive_tutorial";
import { HUDSandboxController } from "../hud/parts/sandbox_controller";
import { queryParamOptions } from "../../core/query_parameters";
import { MetaBlockBuilding } from "../buildings/block";
import { MetaItemProducerBuilding } from "../buildings/item_producer";
import { MOD_SIGNALS } from "../../mods/mod_signals";
@ -584,7 +582,7 @@ export class RegularGameMode extends GameMode {
if (this.root.app.restrictionMgr.getIsStandaloneMarketingActive()) {
this.additionalHudParts.watermark = HUDWatermark;
this.additionalHudParts.standaloneAdvantages = HUDStandaloneAdvantages;
this.additionalHudParts.catMemes = HUDCatMemes;
this.additionalHudParts.catMemes = HUDSteamCapsule;
}
if (this.root.app.settings.getAllSettings().offerHints) {

@ -10,11 +10,10 @@ declare const G_APP_ENVIRONMENT: string;
declare const G_HAVE_ASSERT: boolean;
declare const G_BUILD_TIME: number;
declare const G_IS_STANDALONE: boolean;
declare const G_IS_STEAM_DEMO: boolean;
declare const G_IS_BROWSER: boolean;
declare const G_IS_MOBILE_APP: boolean;
declare const G_BUILD_COMMIT_HASH: string;
declare const G_TRACKING_ENDPOINT: string;
declare const G_BUILD_VERSION: string;
declare const G_ALL_UI_IMAGES: Array<string>;
declare const G_IS_RELEASE: boolean;

@ -105,6 +105,10 @@ export class ModLoader {
}
exposeExports() {
if (G_IS_STEAM_DEMO) {
return;
}
if (G_IS_DEV || G_IS_STANDALONE) {
let exports = {};
const modules = require.context("../", true, /\.js$/);
@ -136,6 +140,11 @@ export class ModLoader {
}
async initMods() {
if (G_IS_STEAM_DEMO) {
this.initialized = true;
return;
}
if (!G_IS_STANDALONE && !G_IS_DEV) {
this.initialized = true;
return;

@ -1,5 +1,6 @@
import { globalConfig } from "../../core/config";
import { createLogger } from "../../core/logging";
import { randomInt } from "../../core/utils";
import { BeltComponent } from "../../game/components/belt";
import { StaticMapEntityComponent } from "../../game/components/static_map_entity";
import { RegularGameMode } from "../../game/modes/regular";
@ -13,16 +14,26 @@ const logger = createLogger("game_analytics");
const analyticsUrl = G_IS_DEV ? "http://localhost:8001" : "https://analytics.shapez.io";
// Be sure to increment the ID whenever it changes to make sure all
// users are tracked
const analyticsLocalFile = "shapez_token_123.bin";
// Be sure to increment the ID whenever it changes
const analyticsLocalFile = G_IS_STEAM_DEMO ? "shapez_token_steamdemo.bin" : "shapez_token_123.bin";
const currentABT = "abt_sa_si";
export class ShapezGameAnalytics extends GameAnalyticsInterface {
constructor(app) {
super(app);
this.abtVariant = "0";
}
get environment() {
if (G_IS_DEV) {
return "dev";
}
if (G_IS_STEAM_DEMO) {
return "steam-demo";
}
if (G_IS_STANDALONE) {
return "steam";
}
@ -38,6 +49,22 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface {
}
}
fetchABVariant() {
return this.app.storage.readFileAsync("shapez_" + currentABT + ".bin").then(
abt => {
this.abtVariant = abt;
logger.log("Got abtVariant:", abt);
},
err => {
if (err === FILE_NOT_FOUND) {
this.abtVariant = String(randomInt(0, 4));
logger.log("Determing abt variant to", this.abtVariant);
this.app.storage.writeFileAsync("shapez_" + currentABT + ".bin", this.abtVariant);
}
}
);
}
/**
* @returns {Promise<void>}
*/
@ -48,46 +75,68 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface {
return;
}
setInterval(() => this.sendTimePoints(), 60 * 1000);
// Retrieve sync key from player
return this.app.storage.readFileAsync(analyticsLocalFile).then(
syncKey => {
this.syncKey = syncKey;
logger.log("Player sync key read:", this.syncKey);
},
error => {
// File was not found, retrieve new key
if (error === FILE_NOT_FOUND) {
logger.log("Retrieving new player key");
// Perform call to get a new key from the API
this.sendToApi("/v1/register", {
environment: this.environment,
standalone:
G_IS_STANDALONE &&
this.app.achievementProvider instanceof SteamAchievementProvider,
commit: G_BUILD_COMMIT_HASH,
})
.then(res => {
// Try to read and parse the key from the api
if (res.key && typeof res.key === "string" && res.key.length === 40) {
this.syncKey = res.key;
logger.log("Key retrieved:", this.syncKey);
this.app.storage.writeFileAsync(analyticsLocalFile, res.key);
} else {
throw new Error("Bad response from analytics server: " + res);
}
return this.fetchABVariant().then(() => {
setInterval(() => this.sendTimePoints(), 60 * 1000);
if (this.app.restrictionMgr.isLimitedVersion()) {
fetch(
analyticsUrl +
"/track/shapez_launch_" +
this.environment +
"_" +
currentABT +
"_" +
this.abtVariant,
{
method: "GET",
mode: "no-cors",
cache: "no-cache",
referrer: "no-referrer",
credentials: "omit",
}
).catch(err => {});
}
return this.app.storage.readFileAsync(analyticsLocalFile).then(
syncKey => {
this.syncKey = syncKey;
logger.log("Player sync key read:", this.syncKey);
},
error => {
// File was not found, retrieve new key
if (error === FILE_NOT_FOUND) {
logger.log("Retrieving new player key");
// Perform call to get a new key from the API
this.sendToApi("/v1/register", {
environment: this.environment,
standalone:
G_IS_STANDALONE &&
!G_IS_STEAM_DEMO &&
this.app.achievementProvider instanceof SteamAchievementProvider,
commit: G_BUILD_COMMIT_HASH,
})
.catch(err => {
logger.error("Failed to register on analytics api:", err);
});
} else {
logger.error("Failed to read ga key:", error);
.then(res => {
// Try to read and parse the key from the api
if (res.key && typeof res.key === "string" && res.key.length === 40) {
this.syncKey = res.key;
logger.log("Key retrieved:", this.syncKey);
this.app.storage.writeFileAsync(analyticsLocalFile, res.key);
} else {
throw new Error("Bad response from analytics server: " + res);
}
})
.catch(err => {
logger.error("Failed to register on analytics api:", err);
});
} else {
logger.error("Failed to read ga key:", error);
}
return;
}
return;
}
);
);
});
}
/**

@ -1,7 +0,0 @@
import { GameAnalyticsInterface } from "../game_analytics";
export class NoGameAnalytics extends GameAnalyticsInterface {
initialize() {
return Promise.resolve();
}
}

@ -189,7 +189,7 @@ function initializeSettings() {
},
/**
* @param {Application} app
*/ app => app.restrictionMgr.getHasExtendedSettings()
*/ app => G_IS_STANDALONE
),
new BoolSetting(
@ -514,7 +514,7 @@ export class ApplicationSettings extends ReadWriteProxy {
const settings = data.settings;
// MODS
if (!THEMES[settings.theme]) {
if (!THEMES[settings.theme] || !this.app.restrictionMgr.getHasExtendedSettings()) {
console.warn("Resetting theme because its no longer available: " + settings.theme);
settings.theme = "light";
}
@ -700,7 +700,7 @@ export class ApplicationSettings extends ReadWriteProxy {
}
// MODS
if (!THEMES[data.settings.theme]) {
if (!THEMES[data.settings.theme] || !this.app.restrictionMgr.getHasExtendedSettings()) {
console.warn("Resetting theme because its no longer available: " + data.settings.theme);
data.settings.theme = "light";
}

@ -38,9 +38,8 @@ export class MainMenuState extends GameState {
getInnerHTML() {
const showLanguageIcon = !G_CHINA_VERSION && !G_WEGAME_VERSION;
const showExitAppButton = G_IS_STANDALONE;
const showUpdateLabel = !G_WEGAME_VERSION;
const showBrowserWarning = !G_IS_STANDALONE && !isSupportedBrowser();
const showPuzzleDLC = !G_WEGAME_VERSION && G_IS_STANDALONE;
const showPuzzleDLC = !G_WEGAME_VERSION && G_IS_STANDALONE && !G_IS_STEAM_DEMO;
const showWegameFooter = G_WEGAME_VERSION;
const hasMods = MODS.anyModsActive();
@ -69,15 +68,17 @@ export class MainMenuState extends GameState {
const ownsPuzzleDLC =
G_IS_DEV ||
(G_IS_STANDALONE &&
!G_IS_STEAM_DEMO &&
/** @type { PlatformWrapperImplElectron}*/ (this.app.platformWrapper).dlcs.puzzle);
const bannerHtml = `
<h3>${T.demoBanners.title}</h3>
<p>${T.demoBanners.intro}</p>
<span class="playtimeDisclaimer">${T.demoBanners.playtimeDisclaimer}</span>
<a href="#" class="steamLink ${A_B_TESTING_LINK_TYPE}" target="_blank">
${
globalConfig.currentDiscount.active
? `<span class='discount'>${globalConfig.currentDiscount.amount}% off!</span>`
globalConfig.currentDiscount > 0
? `<span class='discount'>${globalConfig.currentDiscount}% off!</span>`
: ""
}
@ -201,7 +202,7 @@ export class MainMenuState extends GameState {
<div class="footer ${showExternalLinks ? "" : "noLinks"} ">
${
showExternalLinks
showExternalLinks && !G_IS_STEAM_DEMO
? `
<a class="githubLink boxLink" target="_blank">
${T.mainMenu.openSourceHint}
@ -453,11 +454,12 @@ export class MainMenuState extends GameState {
onSteamLinkClicked() {
this.app.analytics.trackUiClick("main_menu_steam_link_" + A_B_TESTING_LINK_TYPE);
const discount = globalConfig.currentDiscount.active
? "_discount" + globalConfig.currentDiscount.amount
: "";
const discount = globalConfig.currentDiscount > 0 ? "_discount" + globalConfig.currentDiscount : "";
this.app.platformWrapper.openExternalLink(
THIRDPARTY_URLS.stanaloneCampaignLink + "/shapez_mainmenu" + discount
THIRDPARTY_URLS.stanaloneCampaignLink +
"/shapez_mainmenu" +
discount +
(G_IS_STEAM_DEMO ? "_steamdemo" : "")
);
return false;
@ -743,7 +745,9 @@ export class MainMenuState extends GameState {
getStandalone.add(() => {
this.app.analytics.trackUiClick("visit_steampage_from_slot_limit");
this.app.platformWrapper.openExternalLink(
THIRDPARTY_URLS.stanaloneCampaignLink + "/shapez_slotlimit"
THIRDPARTY_URLS.stanaloneCampaignLink +
"/shapez_slotlimit" +
(G_IS_STEAM_DEMO ? "_steamdemo" : "")
);
});
}

@ -3,6 +3,8 @@ import { TextualGameState } from "../core/textual_game_state";
import { MODS } from "../mods/modloader";
import { T } from "../translations";
const MODS_SUPPORTED = !G_IS_STEAM_DEMO && (G_IS_STANDALONE || G_IS_DEV);
export class ModsState extends TextualGameState {
constructor() {
super("ModsState");
@ -19,12 +21,12 @@ export class ModsState extends TextualGameState {
<div class="actions">
${
(G_IS_STANDALONE || G_IS_DEV) && MODS.mods.length > 0
MODS_SUPPORTED && MODS.mods.length > 0
? `<button class="styledButton browseMods">${T.mods.browseMods}</button>`
: ""
}
${
G_IS_STANDALONE || G_IS_DEV
MODS_SUPPORTED
? `<button class="styledButton openModsFolder">${T.mods.openFolder}</button>`
: ""
}
@ -41,7 +43,7 @@ export class ModsState extends TextualGameState {
}
getMainContentHTML() {
if (!G_IS_STANDALONE && !G_IS_DEV) {
if (!MODS_SUPPORTED) {
return `
<div class="noModSupport">
@ -137,7 +139,9 @@ export class ModsState extends TextualGameState {
onSteamLinkClicked() {
this.app.analytics.trackUiClick("mods_steam_link");
this.app.platformWrapper.openExternalLink(
THIRDPARTY_URLS.stanaloneCampaignLink + "/shapez_modsettings"
THIRDPARTY_URLS.stanaloneCampaignLink +
"/shapez_modsettings" +
(G_IS_STEAM_DEMO ? "_steamdemo" : "")
);
return false;

@ -61,6 +61,26 @@ export class PreloadState extends GameState {
this.startLoading();
}
async fetchDiscounts() {
await Promise.race([
new Promise((resolve, reject) => {
setTimeout(() => {
reject("Failed to resolve steam discounts within timeout");
}, 2000);
}),
fetch("https://analytics.shapez.io/v1/discounts")
.then(res => res.json())
.then(data => {
globalConfig.currentDiscount = Number(
data["1318690"].data.price_overview.discount_percent
);
logger.log("Fetched current discount:", globalConfig.currentDiscount);
}),
]).catch(err => {
logger.warn("Failed to fetch current discount:", err);
});
}
onLeave() {
// this.dialogs.cleanup();
}
@ -101,6 +121,9 @@ export class PreloadState extends GameState {
.then(() => this.app.analytics.initialize())
.then(() => this.app.gameAnalytics.initialize())
.then(() => this.setStatus("Connecting to api"))
.then(() => this.fetchDiscounts())
.then(() => this.setStatus("Initializing settings"))
.then(() => {
return this.app.settings.initialize();

@ -36,13 +36,11 @@ export class SettingsState extends TextualGameState {
: `
<button class="styledButton categoryButton manageMods">${T.mods.title}
<span class="newBadge">${T.settings.newBadge}</span>
</button>
`
</button>`
}
<div class="other">
<div class="other ${G_CHINA_VERSION || G_WEGAME_VERSION ? "noabout" : ""}">
${
G_CHINA_VERSION || G_WEGAME_VERSION

@ -22,7 +22,7 @@
---
steamPage:
# This is the short text appearing on the steam page
shortText: shapez.io is a game about building factories to automate the creation and processing of increasingly complex shapes across an infinitely expanding map.
shortText: shapez is a game about building factories to automate the creation and processing of increasingly complex shapes across an infinitely expanding map.
# This is the text shown above the Discord link
discordLinkShort: Official Discord
@ -30,18 +30,18 @@ steamPage:
intro: >-
Do you like automation games? Then you are in the right place!
shapez.io is a relaxed game in which you have to build factories for the automated production of geometric shapes. As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map.
shapez is a relaxed game in which you have to build factories for the automated production of geometric shapes. As the level increases, the shapes become more and more complex, and you have to spread out on the infinite map.
And as if that wasn't enough, you also have to produce exponentially more to satisfy the demands - the only thing that helps is scaling! While you only have to process shapes at the beginning, you will later have to color them - by extracting and mixing colors!
Buying the game on Steam gives you access to the full version, but you can also play a demo at shapez.io first and decide later!
Buying the game on Steam gives you access to the full version, but you can also play a demo at shapez first and decide later!
what_others_say: What people say about shapez.io
what_others_say: What people say about shapez
nothernlion_comment: >-
This game is great - I'm having a wonderful time playing, and time has flown by.
notch_comment: >-
Oh crap. I really should sleep, but I think I just figured out how to make a computer in shapez.io
Oh crap. I really should sleep, but I think I just figured out how to make a computer in shapez
steam_review_comment: >-
This game has stolen my life and I don't want it back. Very chill factory game that won't let me stop making my lines more efficient.
@ -94,9 +94,18 @@ global:
demoBanners:
# This is the "advertisement" shown in the main menu and other various places
title: Demo Version
title: Demo
intro: >-
Get the full game to unlock all features and content!
Get the full game <strong>now</strong> to unlock:<ul>
<li>All 26 levels + infinite Freeplay</li>
<li>22 new buildings</li>
<li>Mod support</li>
<li>Achievements</li>
<li>Dark Mode</li>
<li>... and a lot more!</li>
</ul>
playtimeDisclaimer: >-
The full version contains more than <strong>24 hours of content</strong>.
mainMenu:
play: Play
@ -141,7 +150,7 @@ puzzleMenu:
validatingPuzzle: Validating Puzzle
submittingPuzzle: Submitting Puzzle
noPuzzles: There are currently no puzzles in this section.
dlcHint: Purchased the DLC already? Make sure it is activated by right clicking shapez.io in your library, selecting Properties > DLCs.
dlcHint: Purchased the DLC already? Make sure it is activated by right clicking shapez in your library, selecting Properties > DLCs.
categories:
levels: Levels
@ -654,7 +663,7 @@ ingame:
support:
title: Support me
desc: I develop the game in my spare time!
desc: I develop shapez in my spare time!
# puzzle mode
puzzleEditorSettings:
@ -1111,7 +1120,7 @@ mods:
modsInfo: >-
To install and manage mods, copy them to the mods folder (use the 'Open Mods Folder' button). Be sure to restart the game afterwards, otherwise the mods will not show up.
noModSupport: You need the full version on Steam to install mods.
noModSupport: Get the full version on Steam to install mods!
togglingComingSoon:
title: Coming Soon
@ -1427,7 +1436,7 @@ about:
body: >-
This game is open source and developed by <a href="https://github.com/tobspr" target="_blank">Tobias Springer</a> (this is me).<br><br>
If you want to contribute, check out <a href="<githublink>" target="_blank">shapez.io on GitHub</a>.<br><br>
If you want to contribute, check out <a href="<githublink>" target="_blank">shapez on GitHub</a>.<br><br>
This game wouldn't have been possible without the great Discord community around my games - You should really join the <a href="<discordlink>" target="_blank">Discord server</a>!<br><br>

Loading…
Cancel
Save