Allow for QB Translations of Normal and Expert Quest Books (#432)

[FEATURE]

/* Description: */
Also moves all nodejs stuff into `tools` package.

Also adds a basic `CONTRIBUTING.md` file. 

Superseeds https://github.com/Nomi-CEu/Nomi-CEu/pull/431, which was closed as the branch needed to be renamed, due to the last two items on this list.

TODO:
- [x] Make it only run on master repo.
- [x] Remove qb-lang-revamp branch from the events
- [x] Allow workflow to run on all branches starting with test_buildscript?

/* Commits: */

* Buildscript Changes

* Make Contributing Docs a .md file

* updateqb.yml v1

* Test QB change

* Add lang file to commit file list

* Another Test Change

* Test with only lang path, + `./` at beginning of path

* Test QB change

* updateqb.yml v3

* Test QB Change

* Add some debug settings

* Remove debug, branch input. Switch to auto-commit action

* Make activate: branches prefix `test_buildscript`, commit author GHA bot

* Add branch prefix note into CONTRIBUTING.md

* Make workflow only run if on master repo
This commit is contained in:
IntegerLimit 2023-09-17 22:44:48 +10:00 committed by GitHub
parent bcae6f4eeb
commit 5aecff8b4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 140239 additions and 53919 deletions

View File

@ -31,7 +31,7 @@ jobs:
path: |
~/.npm
./.cache
./buildtools/node_modules
./tools/node_modules
key: ${{ runner.os }}-bunny-${{ hashFiles('**/.cache', '**/package-lock.json', '**/manifest.json') }}
restore-keys: ${{ runner.os }}-bunny-
@ -42,11 +42,11 @@ jobs:
check-latest: true
- name: "Download NPM packages"
working-directory: ./buildtools
working-directory: ./tools
run: npm ci
- name: "Check environmental variables"
working-directory: ./buildtools
working-directory: ./tools
run: npx gulp check
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -55,24 +55,24 @@ jobs:
CFCORE_API_TOKEN: ${{ secrets.CFCORE_API_TOKEN }}
- name: "Build everything"
working-directory: ./buildtools
working-directory: ./tools
run: npx gulp buildAll
env:
CFCORE_API_TOKEN: ${{ secrets.CFCORE_API_TOKEN }}
- name: "Prune cache"
working-directory: ./buildtools
working-directory: ./tools
run: npx gulp pruneCache
env:
CFCORE_API_TOKEN: ${{ secrets.CFCORE_API_TOKEN }}
- name: "Zip everything"
working-directory: ./buildtools
working-directory: ./tools
run: npx gulp zipAll
- name: "Deploy to CurseForge"
env:
CURSEFORGE_PROJECT_ID: ${{ secrets.CURSEFORGE_PROJECT_ID }}
CURSEFORGE_API_TOKEN: ${{ secrets.CURSEFORGE_API_TOKEN }}
working-directory: ./buildtools
working-directory: ./tools
run: npx gulp deployCurseForge

View File

@ -36,7 +36,7 @@ jobs:
path: |
~/.npm
./.cache
./buildtools/node_modules
./tools/node_modules
key: ${{ runner.os }}-bunny-${{ hashFiles('**/.cache', '**/package-lock.json', '**/manifest.json') }}
restore-keys: ${{ runner.os }}-bunny-
@ -47,11 +47,11 @@ jobs:
check-latest: true
- name: "Download NPM packages"
working-directory: ./buildtools
working-directory: ./tools
run: npm ci
- name: "Check environmental variables"
working-directory: ./buildtools
working-directory: ./tools
run: npx gulp check
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -60,24 +60,24 @@ jobs:
CFCORE_API_TOKEN: ${{ secrets.CFCORE_API_TOKEN }}
- name: "Build everything"
working-directory: ./buildtools
working-directory: ./tools
run: npx gulp buildAll
env:
CFCORE_API_TOKEN: ${{ secrets.CFCORE_API_TOKEN }}
- name: "Prune cache"
working-directory: ./buildtools
working-directory: ./tools
run: npx gulp pruneCache
env:
CFCORE_API_TOKEN: ${{ secrets.CFCORE_API_TOKEN }}
- name: "Zip everything"
working-directory: ./buildtools
working-directory: ./tools
run: npx gulp zipAll
- name: "Deploy to CurseForge"
env:
CURSEFORGE_PROJECT_ID: ${{ secrets.CURSEFORGE_PROJECT_ID }}
CURSEFORGE_API_TOKEN: ${{ secrets.CURSEFORGE_API_TOKEN }}
working-directory: ./buildtools
working-directory: ./tools
run: npx gulp deployCurseForgeBeta

View File

@ -32,7 +32,7 @@ jobs:
path: |
~/.npm
./.cache
./buildtools/node_modules
./tools/node_modules
key: ${{ runner.os }}-bunny-${{ hashFiles('**/.cache', '**/package-lock.json', '**/manifest.json') }}
restore-keys: ${{ runner.os }}-bunny-
@ -43,11 +43,11 @@ jobs:
check-latest: true
- name: "Download NPM packages"
working-directory: ./buildtools
working-directory: ./tools
run: npm ci
- name: "Check environmental variables"
working-directory: ./buildtools
working-directory: ./tools
run: npx gulp check
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -56,19 +56,19 @@ jobs:
CFCORE_API_TOKEN: ${{ secrets.CFCORE_API_TOKEN }}
- name: "Build everything"
working-directory: ./buildtools
working-directory: ./tools
run: npx gulp buildAll
env:
CFCORE_API_TOKEN: ${{ secrets.CFCORE_API_TOKEN }}
- name: "Prune cache"
working-directory: ./buildtools
working-directory: ./tools
run: npx gulp pruneCache
env:
CFCORE_API_TOKEN: ${{ secrets.CFCORE_API_TOKEN }}
- name: "Zip everything"
working-directory: ./buildtools
working-directory: ./tools
run: npx gulp zipAll
- name: "Deploy to GitHub Releases"
@ -76,5 +76,5 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CURSEFORGE_PROJECT_ID: ${{ secrets.CURSEFORGE_PROJECT_ID }}
CURSEFORGE_API_TOKEN: ${{ secrets.CURSEFORGE_API_TOKEN }}
working-directory: ./buildtools
working-directory: ./tools
run: npx gulp deployReleases

View File

@ -27,7 +27,7 @@ jobs:
path: |
~/.npm
./.cache
./buildtools/node_modules
./tools/node_modules
key: ${{ runner.os }}-bunny-${{ hashFiles('**/.cache', '**/package-lock.json', '**/manifest.json') }}
restore-keys: ${{ runner.os }}-bunny-
@ -38,11 +38,11 @@ jobs:
check-latest: true
- name: "Download NPM packages"
working-directory: ./buildtools
working-directory: ./tools
run: npm ci
- name: "Check environmental variables"
working-directory: ./buildtools
working-directory: ./tools
run: npx gulp check
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -51,13 +51,13 @@ jobs:
CFCORE_API_TOKEN: ${{ secrets.CFCORE_API_TOKEN }}
- name: "Build everything"
working-directory: ./buildtools
working-directory: ./tools
run: npx gulp buildAll
env:
CFCORE_API_TOKEN: ${{ secrets.CFCORE_API_TOKEN }}
- name: "Prune cache"
working-directory: ./buildtools
working-directory: ./tools
run: npx gulp pruneCache
##
@ -65,7 +65,7 @@ jobs:
##
- name: "Make artifact names"
id: artifactNames
working-directory: ./buildtools
working-directory: ./tools
run: npx gulp makeArtifactNames
- name: "Upload client artifact"
@ -97,7 +97,7 @@ jobs:
build/shared/CHANGELOG.md
- name: "Fire Discord webhook"
working-directory: ./buildtools
working-directory: ./tools
run: npx gulp fireNightlyWebhook
env:
GITHUB_RUN_ID: ${{ github.run_id }}

67
.github/workflows/updateqb.yml vendored Normal file
View File

@ -0,0 +1,67 @@
# This workflow updates the main json files, and the en_us.lang, after a Quest Book change.
name: Update Quest Book Jsons and Lang
on:
workflow_dispatch:
push:
branches:
- main
- test_buildscript*
paths:
- "overrides/config/betterquesting/saved_quests/NormalQuestsDev.json" # Normal QB Developer Json
- "overrides/config/betterquesting/saved_quests/ExpertQuestsDev.json" # Expert QB Developer Json
jobs:
updateQB:
if: "${{ github.repository_owner == 'Nomi-CEu' }}"
name: Update Quest Book Jsons and Lang (${{ github.event.inputs.tag }})
runs-on: ubuntu-latest
env:
GITHUB_BRANCH: ${{ github.event.inputs.branch }}
permissions:
contents: write
steps:
- name: "Checkout code"
uses: actions/checkout@v3
with:
fetch-depth: 0
ref: ${{ github.event.inputs.tag }}
- name: "Restore cached files"
uses: actions/cache@v3
id: cache
with:
path: |
~/.npm
./.cache
./tools/node_modules
key: ${{ runner.os }}-bunny-${{ hashFiles('**/.cache', '**/package-lock.json', '**/manifest.json') }}
restore-keys: ${{ runner.os }}-bunny-
- name: "Setup NodeJS v16"
uses: actions/setup-node@v2
with:
node-version: "16"
check-latest: true
- name: "Download NPM packages"
working-directory: ./tools
run: npm ci
- name: "Transform QB"
working-directory: ./tools
run: npx gulp transformQB
- name: "Commit and push QB Changes"
uses: "stefanzweifel/git-auto-commit-action@v4"
id: "commit"
with:
commit_message: "Update Quest Book Jsons and Lang\n[SKIP]"
commit_author: "github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>"
- name: "Throw Error if No Changes were Detected"
if: steps.commit.outputs.changes_detected == 'false'
run: |
echo "No QB Changes Detected. This may be intended, or an error."
exit 1

34
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,34 @@
# Contributing
## More to come!
## How to contribute:
- Fork this repository
- Add commits
- PR it back!
## Quest Book Contributing
### Normal Mode:
Change the quest book `.json` located at `/overrides/config/betterquesting/saved_quests/NormalQuestsDev.json`.
### Expert Mode:
Change the quest book ``.json` located at `/overrides/config/betterquesting/saved_quests/ExpertQuestsDev.json`.
**Make sure when you change the files listed above! DO NOT change the lang file(s), or the main `.json` files, which contain translation keys.**
The only exception to this rule is with QB translations. Only change the lang file(s). Notes on how to do this are stated below.
Once the changes are committed to `main`, GH Actions will automatically update the main QB `.json` files, and the English QB lang file!
### Translations:
You can either make a resource pack, with a lang file located in `/questbook/lang`, or you can PR a lang file, placed in `/overrides/resources/questbook/lang`.
- Note that both Normal and Expert QB translations are located there.
- Each localization entry for each line or quest has a comment above, saying the quest/line id, and the mode it is related to.
- The localization entries for normal and expert are seperated by a comment.
- `line` notes above localization keys are for chapters.
- `db` notes above localization keys are for quests.
## Info for Maintainers:
Here are some basic notes. I hope that you were explained the basic gist of this project, but here are some finer details:
- If you want the QB Transform Workflow to run on your branch, that isn't `main`, prefix the branch with `test_buildscript`
- If you want the QB Transform Workflow to run on your fork, remove or comment out `if: "${{ github.repository_owner == 'Nomi-CEu' }}"` in `/.github/workflows/updateqb.yml` (in your fork)

View File

@ -1,5 +0,0 @@
import quest from "./quest_i18n";
import scannable from "./scannable";
import version from "./version";
export default [quest, scannable, version];

View File

@ -1,124 +0,0 @@
import fs from "fs";
import upath from "upath";
import { overridesFolder, sharedDestDirectory } from "../../../globals";
import { Quest, QuestBook, QuestLines as QuestLine } from "../../../types/bqQuestBook";
const questLocation = "config/betterquesting/DefaultQuests.json";
const langFileLocation = "resources/questbook/lang";
function escapeString(string: string) {
return string.replace(/%/g, "%%").replace(/\n/g, "%n");
}
function transformKeyPairs(
database: { [key: string]: Quest } | { [key: string]: QuestLine },
namespace: string,
lines: string[],
) {
Object.keys(database).forEach((key) => {
const storeKey = key.replace(/:10/g, "");
const item = database[key];
const properties = item["properties:10"]["betterquesting:10"];
if (properties["name:8"] !== "Gap") {
const titleKey = `nomifactory.quest.${namespace}.${storeKey}.title`;
const descKey = `nomifactory.quest.${namespace}.${storeKey}.desc`;
// Push lang file lines.
lines.push(
`# ${namespace} ${storeKey}`,
`${titleKey}=${escapeString(properties["name:8"])}`,
`${descKey}=${escapeString(properties["desc:8"])}`,
"",
);
properties["name:8"] = titleKey;
properties["desc:8"] = descKey;
} else {
properties["name:8"] = "";
properties["desc:8"] = "";
}
});
}
/**
* Trimming all that results in almost half the size of the original JSON file.
*
* Interesting, huh?
*/
const uselessProps = {
"simultaneous:1": 0,
"ismain:1": 0,
"repeat_relative:1": 1,
"globalshare:1": 0,
"repeattime:3": -1,
"issilent:1": 0,
"snd_complete:8": "minecraft:entity.player.levelup",
"snd_update:8": "minecraft:entity.player.levelup",
"tasklogic:8": "AND",
"questlogic:8": "AND",
"visibility:8": "NORMAL",
"partysinglereward:1": 0,
"lockedprogress:1": 0,
"OreDict:8": "",
"Damage:2": 0,
"Count:3": 0,
"autoclaim:1": 0,
"autoConsume:1": 0,
"consume:1": 0,
"groupDetect:1": 0,
"ignoreNBT:1": 0,
"index:3": 0,
"partialMatch:1": 1,
"ignoresview:1": 0,
};
function stripUselessMetadata(object: unknown) {
Object.keys(object).forEach((propName) => {
const prop = object[propName];
if (prop === uselessProps[propName]) {
return delete object[propName];
}
if (typeof prop === "object") {
if (Array.isArray(prop) && prop.length === 0) {
return delete object[propName];
}
stripUselessMetadata(prop);
if (Object.keys(prop).length === 0) {
return delete object[propName];
}
}
});
}
/**
* Extract lang entries from the quest book and transform the database.
*/
export default async function transformQuestBook(): Promise<void> {
const questDatabasePath = upath.join(sharedDestDirectory, overridesFolder, questLocation);
const questLangLocation = upath.join(sharedDestDirectory, overridesFolder, langFileLocation);
const questBook: QuestBook = JSON.parse((await fs.promises.readFile(questDatabasePath)).toString());
// Traverse through the quest book and rewrite titles/descriptions.
// Extract title/desc pairs into a lang file.
const lines: string[] = [];
// Quest lines.
transformKeyPairs(questBook["questLines:9"], "line", lines);
// Quests themselves.
transformKeyPairs(questBook["questDatabase:9"], "db", lines);
// Write lang file.
await fs.promises.mkdir(questLangLocation, { recursive: true });
await fs.promises.writeFile(upath.join(questLangLocation, "en_us.lang"), lines.join("\n"));
// Strip useless metadata.
stripUselessMetadata(questBook);
return fs.promises.writeFile(questDatabasePath, JSON.stringify(questBook));
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -11,3 +11,5 @@ export const langDestDirectory = upath.join(buildConfig.buildDestinationDirector
export const tempDirectory = upath.join(buildConfig.buildDestinationDirectory, "temp");
export const modpackManifest = manifest as ModpackManifest;
export const overridesFolder = modpackManifest.overrides || "overrides";
export const configFolder = upath.join(overridesFolder, "config");
export const configOverridesFolder = upath.join(overridesFolder, "config-overrides");

View File

@ -3,6 +3,9 @@ import * as gulp from "gulp";
import pruneCacheTask from "./tasks/misc/pruneCache";
export const pruneCache = pruneCacheTask;
import * as quest from "./tasks/github/quest";
export const transformQB = quest.transformQuestBook;
import sharedTasks from "./tasks/shared";
import clientTasks from "./tasks/client";
import serverTasks from "./tasks/server";

164
tools/tasks/github/quest.ts Normal file
View File

@ -0,0 +1,164 @@
import fs from "fs";
import gulp from "gulp";
import upath from "upath";
import { overridesFolder, configFolder, configOverridesFolder } from "../../globals";
import buildConfig from "../../buildConfig";
import { Quest, QuestBook, QuestLines as QuestLine } from "../../types/bqQuestBook";
const sharedQBDefaults = upath.join(buildConfig.buildSourceDirectory, configFolder, "betterquesting");
const sharedConfigOverrides = upath.join(buildConfig.buildSourceDirectory, configOverridesFolder);
const langFileLocation = "resources/questbook/lang";
function escapeString(string: string) {
return string.replace(/%/g, "%%").replace(/\n/g, "%n");
}
function transformKeyPairs(
database: { [key: string]: Quest } | { [key: string]: QuestLine },
mode: string,
namespace: string,
lines: string[],
) {
Object.keys(database).forEach((key) => {
const storeKey = key.replace(/:10/g, "");
const item = database[key];
const properties = item["properties:10"]["betterquesting:10"];
if (properties["name:8"] !== "Gap") {
const titleKey = `nomifactory.quest.${mode}.${namespace}.${storeKey}.title`;
const descKey = `nomifactory.quest.${mode}.${namespace}.${storeKey}.desc`;
// Push lang file lines.
lines.push(
`#${namespace} ${storeKey} of mode ${mode}`,
`${titleKey}=${escapeString(properties["name:8"])}`,
`${descKey}=${escapeString(properties["desc:8"])}`,
"",
);
properties["name:8"] = titleKey;
properties["desc:8"] = descKey;
} else {
properties["name:8"] = "";
properties["desc:8"] = "";
}
});
}
/**
* Trimming all that results in almost half the size of the original JSON file.
*
* Interesting, huh?
*/
const uselessProps = {
"simultaneous:1": 0,
"ismain:1": 0,
"repeat_relative:1": 1,
"globalshare:1": 0,
"repeattime:3": -1,
"issilent:1": 0,
"snd_complete:8": "minecraft:entity.player.levelup",
"snd_update:8": "minecraft:entity.player.levelup",
"tasklogic:8": "AND",
"questlogic:8": "AND",
"visibility:8": "NORMAL",
"partysinglereward:1": 0,
"lockedprogress:1": 0,
"OreDict:8": "",
"Damage:2": 0,
"Count:3": 0,
"autoclaim:1": 0,
"autoConsume:1": 0,
"consume:1": 0,
"groupDetect:1": 0,
"ignoreNBT:1": 0,
"index:3": 0,
"partialMatch:1": 1,
"ignoresview:1": 0,
};
function stripUselessMetadata(object: unknown) {
Object.keys(object).forEach((propName) => {
const prop = object[propName];
if (prop === uselessProps[propName]) {
return delete object[propName];
}
if (typeof prop === "object") {
if (Array.isArray(prop) && prop.length === 0) {
return delete object[propName];
}
stripUselessMetadata(prop);
if (Object.keys(prop).length === 0) {
return delete object[propName];
}
}
});
}
/**
* Extract lang entries from the quest book and transform the database.
*/
export async function transformQuestBook(): Promise<void> {
// Source Quest Book File Locations
const questPathNormalDev = upath.join(sharedQBDefaults, "saved_quests", "NormalQuestsDev.json");
const questPathExpertDev = upath.join(sharedQBDefaults, "saved_quests", "ExpertQuestsDev.json");
// Quest Book Objects
const questBookNormal: QuestBook = JSON.parse((await fs.promises.readFile(questPathNormalDev)).toString());
const questBookExpert: QuestBook = JSON.parse((await fs.promises.readFile(questPathExpertDev)).toString());
// Quest Book Paths
const questPathNormalDefault = upath.join(sharedQBDefaults, "DefaultQuests.json");
const questPathNormalOverride = upath.join(sharedConfigOverrides, "normal", "betterquesting", "DefaultQuests.json");
const questPathExpertDefault = upath.join(sharedQBDefaults, "saved_quests", "ExpertQuests.json");
const questPathExpertOverride = upath.join(sharedConfigOverrides, "expert", "betterquesting", "DefaultQuests.json");
// Quest Lang Location
const questLangLocation = upath.join(buildConfig.buildSourceDirectory, overridesFolder, langFileLocation);
// Traverse through the quest book and rewrite titles/descriptions.
// Extract title/desc pairs into a lang file.
const lines: string[] = [];
lines.push("Normal Quest Lang Entries:",
"",
);
// Normal Mode Quest lines.
transformKeyPairs(questBookNormal["questLines:9"], "normal", "line", lines);
// Normal Mode Quests themselves.
transformKeyPairs(questBookNormal["questDatabase:9"], "normal", "db", lines);
lines.push("Expert Quest Lang Entries:",
"",
);
// Expert Mode Quest lines.
transformKeyPairs(questBookExpert["questLines:9"], "expert", "line", lines);
// Expert Mode Quests themselves.
transformKeyPairs(questBookExpert["questDatabase:9"], "expert", "db", lines);
// Write lang file.
await fs.promises.mkdir(questLangLocation, { recursive: true });
await fs.promises.writeFile(upath.join(questLangLocation, "en_us.lang"), lines.join("\n"));
// Strip useless metadata.
stripUselessMetadata(questBookNormal);
stripUselessMetadata(questBookNormal);
// Write QB files.
fs.promises.writeFile(questPathNormalDefault, JSON.stringify(questBookNormal, null, 2));
fs.promises.writeFile(questPathNormalOverride, JSON.stringify(questBookNormal, null, 2));
fs.promises.writeFile(questPathExpertDefault, JSON.stringify(questBookExpert, null, 2));
fs.promises.writeFile(questPathExpertOverride, JSON.stringify(questBookExpert, null, 2));
}

View File

@ -0,0 +1,4 @@
import scannable from "./scannable";
import version from "./version";
export default [scannable, version];