1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-06-13 13:04:03 +00:00
This commit is contained in:
Bagel03 2021-07-07 13:31:45 +02:00 committed by GitHub
commit a1c62cc069
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 368 additions and 2 deletions

1
.gitignore vendored
View File

@ -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

180
gulp/contributors.js Normal file
View File

@ -0,0 +1,180 @@
const fs = require("fs");
const fsp = require("fs/promises");
const path = require("path");
const nodeFetch = 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 personalAccessToken = "PUT TOKEN HERE";
const JSONFileLocation = path.join(__dirname, "..", "contributors.json");
function fetch(url) {
return nodeFetch(url, {
headers: [["Authorization", "token " + personalAccessToken]],
});
}
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) {
// 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();
const clearLine = () => {
process.stdout.moveCursor(0, -1); // up one line
process.stdout.clearLine(1);
};
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]);
if (i !== 0) clearLine();
console.log(`PR's Downloaded: ${i} out of ${prs.length} (${prs.length - i} left)`);
}
clearLine();
console.log("Downloaded All PR's");
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; 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,
title: res[i].title,
};
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 = 1; i < numOfPageReqs + 1; 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 (personalAccessToken === "PUT TOKEN HERE") {
console.log("A github token was not provided, writing default contributors.json");
await writeJSONFile([], []);
return;
}
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(`Discovered ${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,
};

View File

@ -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

View File

@ -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",

View File

@ -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"

View File

@ -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";

View File

@ -0,0 +1,35 @@
#state_CreditsState {
.container .content {
text-align: center;
.tobspr .title {
}
.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;
color: #555;
@include DarkThemeOverride {
color: #eee;
}
}
.people {
max-height: 0;
overflow: hidden;
transition: max-height 0.5s ease-out;
}
.people > :first-child {
@include S(margin-top, 8px);
}
}
}
}

View File

@ -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) {

View File

@ -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() {

112
src/js/states/credits.js Normal file
View File

@ -0,0 +1,112 @@
import { TextualGameState } from "../core/textual_game_state";
import { contributors, translators } from "../../../contributors.json";
import { T } from "../translations";
export class CreditsState extends TextualGameState {
constructor() {
super("CreditsState");
}
getStateHeaderTitle() {
return T.credits.title;
}
getMainContentHTML() {
return `
<div class="section tobspr">
<button class="title">${T.credits.tobspr}</button>
<div class="people">
<div class="entry">${this.linkify("https://github.com/tobspr", "Tobias Springer")}</div>
</div>
</div>
<div class="special-shout-out section">
<button class="title">${T.credits.specialThanks.title}:</button>
<div class="people">
<div class="entry">${this.linkify(
"https://soundcloud.com/pettersumelius",
"Peppsen"
)} - ${T.credits.specialThanks.descriptions.peppsen}</div>
<div class="entry">Add some other people here (Whoever you think deserves it)</div>
</div>
</div>
<div class="translators section">
<button class="title">${T.credits.translators.title}:</button>
<div class="people">
${this.getGithubHTML(translators)}
</div>
</div>
<div class="contributors section">
<button class="title">${T.credits.contributors.title}:</button>
<div class="people">
${this.getGithubHTML(contributors)}
</div>
</div>
`;
}
linkify(href, text) {
return `<a href="${href}" target="_blank">${text}</a>`;
}
getGithubHTML(list) {
let html = "";
for (let i = 0; i < list.length; i++) {
html += `
${i === 0 ? "" : "<br>"}
<div class="entry">
${this.linkify(`https://github.com/${list[i].username}`, list[i].username)}: <br> ${list[
i
].value
.map(pr => {
return `${this.linkify(pr.html_url, this.getGoodTitle(pr.title))}, `;
})
.reduce((p, c) => p + c)
.slice(0, -2)}
</div>
`;
}
return html;
}
getGoodTitle(title) {
if (title.endsWith(".")) return title.slice(0, -1);
return title;
}
onEnter() {
// Allow the user to close any section by clicking on the title
const buttons = this.htmlElement.querySelectorAll("button.title");
buttons.forEach(button => {
/** @type {HTMLElement} */
//@ts-ignore
const people = button.nextElementSibling;
button.addEventListener("click", e => {
if (people.style.maxHeight) {
people.style.maxHeight = null;
} else {
people.style.maxHeight = people.scrollHeight + "px";
}
});
// Set them to open at the start
people.style.maxHeight = people.scrollHeight + "px";
});
// Link stuff
const links = this.htmlElement.querySelectorAll("a[href]");
links.forEach(link => {
this.trackClicks(
link,
() => this.app.platformWrapper.openExternalLink(link.getAttribute("href")),
{ preventClick: true }
);
});
}
getDefaultPreviousState() {
return "AboutState";
}
}

View File

@ -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>
@ -1379,6 +1381,18 @@ about:
changelog:
title: Changelog
credits:
title: Credits
tobspr: Main Programer and Artist
specialThanks:
title: Special Thanks To
descriptions:
peppsen: Created the awesome soundtrack
translators:
title: Translators
contributors:
title: Contributors
demo:
features:
restoringGames: Restoring savegames