mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-06-13 13:04:03 +00:00
Inital Update
This commit is contained in:
parent
5074727efa
commit
5dd2f5b0fe
1
.gitignore
vendored
1
.gitignore
vendored
@ -48,6 +48,7 @@ gulp/runnable-texturepacker.jar
|
||||
tmp_standalone_files
|
||||
tmp_standalone_files_china
|
||||
tmp_standalone_files_wegame
|
||||
contributors.json
|
||||
|
||||
# Local config
|
||||
config.local.js
|
||||
|
156
gulp/contributors.js
Normal file
156
gulp/contributors.js
Normal file
@ -0,0 +1,156 @@
|
||||
const fs = require("fs");
|
||||
const fsp = require("fs/promises");
|
||||
const path = require("path");
|
||||
const fetch = require("node-fetch");
|
||||
|
||||
const APILink = "https://api.github.com/repos/tobspr/shapez.io";
|
||||
const numOfReqPerPage = 100; // Max is 100, change to something lower if loads are too long
|
||||
|
||||
const JSONFileLocation = path.join(__dirname, "..", "contributors.json");
|
||||
|
||||
function JSONFileExists() {
|
||||
return fs.existsSync(JSONFileLocation);
|
||||
}
|
||||
|
||||
function readJSONFile() {
|
||||
return fsp.readFile(JSONFileLocation, { encoding: "utf-8" }).then(res => JSON.parse(res));
|
||||
}
|
||||
|
||||
function writeJSONFile(translators, contributors) {
|
||||
return fsp.writeFile(
|
||||
JSONFileLocation,
|
||||
JSON.stringify({
|
||||
lastUpdatedAt: Date.now(),
|
||||
//@ts-ignore
|
||||
translators: Array.from(translators, ([username, value]) => ({ username, value })),
|
||||
//@ts-ignore
|
||||
contributors: Array.from(contributors, ([username, value]) => ({ username, value })),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function getTotalNumOfPRs() {
|
||||
return fetch(APILink + "/pulls?state=closed&per_page=1&page=0").then(res => {
|
||||
// This part is very messy
|
||||
let link = res.headers.get("Link");
|
||||
link = link.slice(link.indexOf(",") + 1); // Gets rid of the first "next" link
|
||||
return parseInt(link.slice(link.indexOf("&page=") + 6, link.indexOf(">")));
|
||||
});
|
||||
}
|
||||
|
||||
function shouldDownloadPRs() {
|
||||
if (!JSONFileExists()) return Promise.resolve(true);
|
||||
else {
|
||||
return readJSONFile().then(res => Date.now() - res.lastUpdatedAt > 1000 * 60 * 30); // once every 30 min
|
||||
}
|
||||
}
|
||||
|
||||
function PRIsTranslation(link) {
|
||||
// return true;
|
||||
// We just say that if a PR only edits translation files, its a translation, all others are something else
|
||||
return fetch(link + "/files")
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
if (res.message) {
|
||||
console.log("GITHUB HAS RATE-LIMITED THIS MACHINE, PLEASE WAIT ABOUT AN HOUR");
|
||||
throw new Error("rate-limit reached");
|
||||
}
|
||||
for (let file of res) {
|
||||
if (!file.filename.startsWith("translations/")) return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
async function sortPRs(prs) {
|
||||
const contributors = new Map();
|
||||
const translators = new Map();
|
||||
|
||||
for (let i = 0; i < prs.length; i++) {
|
||||
let map;
|
||||
|
||||
if (await PRIsTranslation(prs[i].url)) map = translators;
|
||||
else map = contributors;
|
||||
|
||||
if (!map.has(prs[i].username)) map.set(prs[i].username, []);
|
||||
|
||||
map.get(prs[i].username).push(prs[i]);
|
||||
}
|
||||
|
||||
return {
|
||||
contributors,
|
||||
translators,
|
||||
};
|
||||
}
|
||||
|
||||
function reqPage(page) {
|
||||
return fetch(APILink + `/pulls?state=closed&per_page=${numOfReqPerPage}&page=${page}`)
|
||||
.then(res => res.json())
|
||||
.then(async res => {
|
||||
const prs = [];
|
||||
|
||||
for (let i = 0; i < res.length - 1; i++) {
|
||||
if (!res[i].merged_at) continue; // Skip files that were never merged
|
||||
|
||||
const prInfo = {
|
||||
username: res[i].user.login,
|
||||
html_url: res[i].html_url,
|
||||
url: res[i].url,
|
||||
user_avatar: res[i].user.avatar_url,
|
||||
title: res[i].title,
|
||||
time: res[i].createdAt,
|
||||
};
|
||||
|
||||
prs.push(prInfo);
|
||||
// if (await PRIsTranslation(res[i].url)) {
|
||||
// translations.push(prInfo);
|
||||
// } else {
|
||||
// others.push(prInfo);
|
||||
// }
|
||||
}
|
||||
|
||||
return prs;
|
||||
});
|
||||
}
|
||||
|
||||
async function downloadAllPrs() {
|
||||
const totalNumOfPrs = await getTotalNumOfPRs();
|
||||
const numOfPageReqs = Math.ceil(totalNumOfPrs / numOfReqPerPage);
|
||||
|
||||
const prs = [];
|
||||
|
||||
for (let i = 0; i < numOfPageReqs; i++) {
|
||||
prs.push(...(await reqPage(i))); // Yes promise.all would be good, but I wanna keep them in order (at least for now)
|
||||
}
|
||||
|
||||
return prs;
|
||||
}
|
||||
|
||||
async function tryToUpdateContributors() {
|
||||
if (!(await shouldDownloadPRs())) {
|
||||
console.log("Not updating contributors to prevent github API from rate-limiting this computer");
|
||||
console.log("If you wish to force a contributors update, use contributors.build.force");
|
||||
return;
|
||||
}
|
||||
|
||||
await updateContributors();
|
||||
}
|
||||
|
||||
async function updateContributors() {
|
||||
const allPrs = await downloadAllPrs();
|
||||
console.log(`Received ${allPrs.length} PRs`);
|
||||
|
||||
const sorted = await sortPRs(allPrs);
|
||||
|
||||
await writeJSONFile(sorted.translators, sorted.contributors);
|
||||
console.log("Wrote JSON File");
|
||||
}
|
||||
|
||||
function gulpTaskContributors($, gulp) {
|
||||
gulp.task("contributors.build", cb => tryToUpdateContributors().then(() => cb));
|
||||
gulp.task("contributors.build.force", cb => updateContributors().then(() => cb));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
gulpTaskContributors,
|
||||
};
|
@ -74,6 +74,8 @@ releaseUploader.gulptasksReleaseUploader($, gulp, buildFolder);
|
||||
const translations = require("./translations");
|
||||
translations.gulptasksTranslations($, gulp, buildFolder);
|
||||
|
||||
const contributors = require("./contributors");
|
||||
contributors.gulpTaskContributors($, gulp, buildFolder);
|
||||
///////////////////// BUILD TASKS /////////////////////
|
||||
|
||||
// Cleans up everything
|
||||
@ -252,6 +254,7 @@ gulp.task(
|
||||
"imgres.copyImageResources",
|
||||
"imgres.copyNonImageResources",
|
||||
"translations.fullBuild",
|
||||
"contributors.build",
|
||||
"css.dev",
|
||||
"html.dev"
|
||||
)
|
||||
@ -270,13 +273,17 @@ gulp.task(
|
||||
"imgres.copyImageResources",
|
||||
"imgres.copyNonImageResources",
|
||||
"translations.fullBuild",
|
||||
"contributors.build",
|
||||
"css.dev",
|
||||
"html.standalone-dev"
|
||||
)
|
||||
);
|
||||
|
||||
// Builds everything (staging)
|
||||
gulp.task("step.staging.code", gulp.series("sounds.fullbuild", "translations.fullBuild", "js.staging"));
|
||||
gulp.task(
|
||||
"step.staging.code",
|
||||
gulp.series("sounds.fullbuild", "translations.fullBuild", "contributors.build", "js.staging")
|
||||
);
|
||||
gulp.task(
|
||||
"step.staging.mainbuild",
|
||||
gulp.parallel("utils.copyAdditionalBuildFiles", "step.baseResources", "step.staging.code")
|
||||
@ -285,7 +292,10 @@ gulp.task("step.staging.all", gulp.series("step.staging.mainbuild", "css.prod",
|
||||
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.code",
|
||||
gulp.series("sounds.fullbuild", "translations.fullBuild", "contributors.build", "js.prod")
|
||||
);
|
||||
gulp.task(
|
||||
"step.prod.mainbuild",
|
||||
gulp.parallel("utils.copyAdditionalBuildFiles", "step.baseResources", "step.prod.code")
|
||||
@ -296,7 +306,7 @@ gulp.task("build.prod", gulp.series("utils.cleanup", "step.prod.all", "step.post
|
||||
// Builds everything (standalone-beta)
|
||||
gulp.task(
|
||||
"step.standalone-beta.code",
|
||||
gulp.series("sounds.fullbuildHQ", "translations.fullBuild", "js.standalone-beta")
|
||||
gulp.series("sounds.fullbuildHQ", "translations.fullBuild", "contributors.build", "js.standalone-beta")
|
||||
);
|
||||
gulp.task("step.standalone-beta.mainbuild", gulp.parallel("step.baseResources", "step.standalone-beta.code"));
|
||||
gulp.task(
|
||||
@ -313,7 +323,12 @@ gulp.task(
|
||||
for (const prefix of ["", "china.", "wegame."]) {
|
||||
gulp.task(
|
||||
prefix + "step.standalone-prod.code",
|
||||
gulp.series("sounds.fullbuildHQ", "translations.fullBuild", prefix + "js.standalone-prod")
|
||||
gulp.series(
|
||||
"sounds.fullbuildHQ",
|
||||
"translations.fullBuild",
|
||||
"contributors.build",
|
||||
prefix + "js.standalone-prod"
|
||||
)
|
||||
);
|
||||
|
||||
gulp.task(
|
||||
|
@ -15,7 +15,7 @@
|
||||
"@babel/preset-env": "^7.5.4",
|
||||
"@types/cordova": "^0.0.34",
|
||||
"@types/filesystem": "^0.0.29",
|
||||
"@types/node": "^12.7.5",
|
||||
"@types/node": "^15.12.4",
|
||||
"ajv": "^6.10.2",
|
||||
"audiosprite": "^0.7.2",
|
||||
"babel-core": "^6.26.3",
|
||||
@ -41,6 +41,7 @@
|
||||
"ignore-loader": "^0.1.2",
|
||||
"lz-string": "^1.4.4",
|
||||
"markdown-loader": "^5.1.0",
|
||||
"node-fetch": "^2.6.1",
|
||||
"node-sri": "^1.1.1",
|
||||
"phonegap-plugin-mobile-accessibility": "^1.0.5",
|
||||
"postcss": ">=5.0.0",
|
||||
|
@ -994,11 +994,16 @@
|
||||
resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz"
|
||||
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
|
||||
|
||||
"@types/node@*", "@types/node@^12.7.5":
|
||||
"@types/node@*":
|
||||
version "12.7.5"
|
||||
resolved "https://registry.npmjs.org/@types/node/-/node-12.7.5.tgz"
|
||||
integrity sha512-9fq4jZVhPNW8r+UYKnxF1e2HkDWOWKM5bC2/7c9wPV835I0aOrVbS/Hw/pWPk2uKrNXQqg9Z959Kz+IYDd5p3w==
|
||||
|
||||
"@types/node@^15.12.4":
|
||||
version "15.12.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.4.tgz#e1cf817d70a1e118e81922c4ff6683ce9d422e26"
|
||||
integrity sha512-zrNj1+yqYF4WskCMOHwN+w9iuD12+dGm0rQ35HLl9/Ouuq52cEtd0CH9qMgrdNmi5ejC1/V7vKEXYubB+65DkA==
|
||||
|
||||
"@types/q@^1.5.1":
|
||||
version "1.5.2"
|
||||
resolved "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz"
|
||||
@ -8318,6 +8323,11 @@ no-case@^2.2.0:
|
||||
dependencies:
|
||||
lower-case "^1.1.1"
|
||||
|
||||
node-fetch@^2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||
|
||||
node-libs-browser@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz"
|
||||
|
@ -30,6 +30,7 @@
|
||||
@import "states/mobile_warning";
|
||||
@import "states/changelog";
|
||||
@import "states/puzzle_menu";
|
||||
@import "states/credits.scss";
|
||||
|
||||
@import "ingame_hud/buildings_toolbar";
|
||||
@import "ingame_hud/building_placer";
|
||||
|
40
src/css/states/credits.scss
Normal file
40
src/css/states/credits.scss
Normal file
@ -0,0 +1,40 @@
|
||||
#state_CreditsState {
|
||||
.container .content {
|
||||
text-align: center;
|
||||
|
||||
.section {
|
||||
@include S(margin-top, 10px);
|
||||
@include S(padding, 5px);
|
||||
background-color: #f8f8f8;
|
||||
@include DarkThemeOverride {
|
||||
background: rgba(0, 10, 20, 0.1);
|
||||
}
|
||||
|
||||
.title {
|
||||
@include Heading;
|
||||
}
|
||||
|
||||
.people {
|
||||
.entry {
|
||||
}
|
||||
|
||||
.flex-entry {
|
||||
display: flex;
|
||||
|
||||
.entry {
|
||||
display: inline-block;
|
||||
width: min-content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.people-flex {
|
||||
display: flex;
|
||||
|
||||
.entry {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -34,6 +34,7 @@ import { RestrictionManager } from "./core/restriction_manager";
|
||||
import { PuzzleMenuState } from "./states/puzzle_menu";
|
||||
import { ClientAPI } from "./platform/api";
|
||||
import { LoginState } from "./states/login";
|
||||
import { CreditsState } from "./states/credits";
|
||||
|
||||
/**
|
||||
* @typedef {import("./platform/achievement_provider").AchievementProviderInterface} AchievementProviderInterface
|
||||
@ -165,6 +166,7 @@ export class Application {
|
||||
ChangelogState,
|
||||
PuzzleMenuState,
|
||||
LoginState,
|
||||
CreditsState,
|
||||
];
|
||||
|
||||
for (let i = 0; i < states.length; ++i) {
|
||||
|
@ -35,6 +35,14 @@ export class AboutState extends TextualGameState {
|
||||
{ preventClick: true }
|
||||
);
|
||||
});
|
||||
|
||||
const stateChangers = this.htmlElement.querySelectorAll("a[state]");
|
||||
console.log(stateChangers);
|
||||
stateChangers.forEach(element => {
|
||||
this.trackClicks(element, () => this.moveToState(element.getAttribute("state")), {
|
||||
preventClick: true,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getDefaultPreviousState() {
|
||||
|
92
src/js/states/credits.js
Normal file
92
src/js/states/credits.js
Normal file
@ -0,0 +1,92 @@
|
||||
import { TextualGameState } from "../core/textual_game_state";
|
||||
import { contributors, translators } from "../../../contributors.json";
|
||||
const APILink = "https://api.github.com/repos/tobspr/shapez.io"; // Should use THRIDPARY_URLS, but it is really hard to read.
|
||||
const numOfReqPerPage = 100; // Max is 100, change to something lower if loads are too long
|
||||
|
||||
export class CreditsState extends TextualGameState {
|
||||
constructor() {
|
||||
super("CreditsState");
|
||||
this.state = "Credits";
|
||||
}
|
||||
|
||||
getStateHeaderTitle() {
|
||||
return this.state;
|
||||
}
|
||||
|
||||
getMainContentHTML() {
|
||||
return `
|
||||
<div class="section"><div class="title">Tobias Springer - Main Programer and Artist</div></div>
|
||||
<div class="special-shout-out section">
|
||||
<div class="title">A Special Thanks To:</div>
|
||||
<div class="people">
|
||||
<div class="entry">Pepsin - Created the soundtrack</div>
|
||||
<div class="entry">Sense 101 - Designed the Puzzle DLC's official puzzles</div>
|
||||
<div class="entry">SargeanTravis - Created an achievement by whining a lot</div>
|
||||
<div class="entry">Bagel03 - Was an absolute CHAD</div>
|
||||
<div class="entry">Dengr - Wouldn't tell me what to put here</div>
|
||||
<div class="entry">Block of Emerald - Also wouldn't tell me what to put here</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="translators section">
|
||||
<div class="title">Translators: </div>
|
||||
<div class="flex-people">
|
||||
${this.getTranslatorsHTML()}
|
||||
</div>
|
||||
</div>
|
||||
<div class="contributors section">
|
||||
<div class="title">Contributors: </div>
|
||||
<div id="loading">Loading... <div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
getTranslatorsHTML() {
|
||||
let html = "";
|
||||
for (let i = 0; i < translators.length; i++) {
|
||||
html += `
|
||||
<br>
|
||||
<div class="entry">
|
||||
<a href="${translators[i].username}" target="_blank">${
|
||||
translators[i].username
|
||||
}</a>: <br> ${translators[i].value
|
||||
.map(pr => {
|
||||
return `<a href=${pr.html_url} target="_blank">${this.getGoodTitle(pr.title)}</a>, `;
|
||||
})
|
||||
.reduce((p, c) => p + c)
|
||||
.slice(0, -2)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
getContributorsHTML() {
|
||||
let html = "";
|
||||
for (let i = 0; i < contributors.length; i++) {
|
||||
html += `
|
||||
<br>
|
||||
<div class="entry">
|
||||
<a href="${contributors[i].username}" target="_blank">${
|
||||
contributors[i].username
|
||||
}</a>: <br> ${contributors[i].value
|
||||
.map(pr => {
|
||||
return `<a href=${pr.html_url} target="_blank">${this.getGoodTitle(pr.title)}</a>, `;
|
||||
})
|
||||
.reduce((p, c) => p + c)
|
||||
.slice(0, -2)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
getGoodTitle(title) {
|
||||
if (title.endsWith(".")) return title.slice(0, -1);
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
onEnter() {
|
||||
// this.setPRInnerHTML();
|
||||
}
|
||||
}
|
@ -1368,6 +1368,8 @@ 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>
|
||||
|
||||
Have a look at the <a state="CreditsState">Credits</a> to see every who has helped out by translating or contributing.<br><br>
|
||||
|
||||
If you want to contribute, check out <a href="<githublink>" target="_blank">shapez.io 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…
Reference in New Issue
Block a user