diff --git a/dev/TODO.md b/dev/TODO.md index f01bd87af..64efdbed9 100644 --- a/dev/TODO.md +++ b/dev/TODO.md @@ -9,15 +9,33 @@ - [ ] ✏️ Buff advanced generators - [ ] 📖 remove Eldritch Knowledge Rewards since newbies eat them all at once - [ ] ✏️ adv Rock Crystals Beneficate In QMD -- [ ] ✏️ steel And Up Turbine Upgrades Not Working -- [ ] 📖 note That Mithminite Armor Simplify Bores - [ ] ⚡ Replace `utils.rh` with subcommands to improve performance. `IItemStack.ores` is slow. - [ ] ✏️ QMDs `Sodium Chlorde` and `Sodium Nitrate` unify -- [ ] 🌍 Fix Biome Tweaker block replacementsafter AdvRock filler block change +- [ ] 🌍 Fix Biome Tweaker block replacements after AdvRock filler block change - [ ] 🖼️ Optimize Bibliocraft textures -- [ ] ✏️ addVoidBeaconTab -- [ ] addSkyblockTpMechanic -- [ ] Fix Myrmexes have double JER trade tabs +- [ ] ✏️ add Void Beacon Tab +- [ ] 📖 add E2E book notes and quest about Skyblock Tp Mechanic +- [ ] fix Black Quartz And Certus Untransformable Portal Spread +- [ ] 🌍 Remove Poison Ivy from OTG generator +- [ ] 🧩 make Backpacks Unbreakable to prevent destroying for several reasons +- [ ] 📖 Getting Fire fix quest +- [ ] 📖 Touching portal not working on server +- [ ] 📖 15 health for portal should be shared for whole team +- [ ] 📖 Piston Rewards add amount of reward +- [ ] ✏️ Completely disable OreDict melting +- [ ] Install Pixel Reality - Luminance +- [ ] 📖 Add "Crafting" upgrade to Compressing robot +- [ ] 📖 Add note about Assembler+Battery+Bottle +- [ ] ✏️ Native Clusters should be made into Dirty Ores to prevent multiplication of recipes +- [ ] 🟢 Add https://legacy.curseforge.com/minecraft/mc-mods/advanced-smelter +- [ ] 💿 Add null check to fix Dynamism Table https://discord.com/channels/911676461050642432/1195650557004755056/1197080356604616704 +- [ ] 📖 Fix "Completed chapter" message +- [ ] 🦆 Rewrite Goose logic https://github.com/friendlyhj/ZenUtils/issues/33 +- [x] ✏️ TE Sawmill fix Spectre wood => Planks (remove Camuflaged Panelling from game) +- [ ] ✏️ Fix enderman still drop ghost Mysical Flesh +- [ ] 🌍 Terra Nova (112) regenerate JER +- [ ] 🟠 When update Precision Mining add `438451` to server-setup-config +- [ ] ✏️ Remove removed item from Overworld Explorer villager trades ```sh /bcore_ticktime diff --git a/dev/build/build_utils.ts b/dev/build/build_utils.ts new file mode 100644 index 000000000..54baa3a5e --- /dev/null +++ b/dev/build/build_utils.ts @@ -0,0 +1,142 @@ +import type { Options } from 'fast-glob' +import type { InputFieldOptions } from 'terminal-kit/Terminal.js' + +import { relative } from 'node:path' +import process from 'node:process' + +import boxen from 'boxen' +import chalk from 'chalk' +import fast_glob from 'fast-glob' +import fse from 'fs-extra' +import logUpdate from 'log-update' +import terminal_kit from 'terminal-kit' + +import { + end, + write, +} from '../lib/utils.js' + +const { rmSync } = fse + +const { terminal: term } = terminal_kit + +/** + * Globs with default options `dot: true, onlyFiles: false` + */ +export function globs(source: string | string[], options?: Options) { + return fast_glob.sync(source, { dot: true, onlyFiles: false, ...options }) +} + +export function getIgnoredFiles(ignored: any) { + return globs( + ignored._rules.filter((r: { negative: any }) => !r.negative).map((r: { pattern: any }) => r.pattern), + { ignore: ignored._rules.filter((r: { negative: any }) => r.negative).map((r: { pattern: any }) => r.pattern) } + ) +} + +/** + * @param {string | readonly string[]} fileArg list of globs of files to remove + */ +export function removeFiles(fileArg: readonly string[] | string) { + const files = [fileArg].flat(2) + /** @type {string[]} */ + const removed: string[] = [] + files.forEach((file) => { + try { + rmSync(file, { recursive: true, maxRetries: 1 }) + removed.push(file) + } + catch (error) { + process.stdout.write(`\n${chalk.red(`Cannot remove: ${chalk.blue(file)}`)}\n`) + } + }) + return `removed: ${removed.length}\n${ + removed.map(s => chalk.gray(relative(process.cwd(), s))).join('\n') + }` +} + +export const style = { + trace : chalk.hex('#7b4618'), + info : chalk.hex('#915c27'), + log : chalk.hex('#ad8042'), + label : chalk.hex('#bfab67'), + string: chalk.hex('#bfc882'), + number: chalk.hex('#a4b75c'), + status: chalk.hex('#647332'), + chose : chalk.hex('#3e4c22'), + end : chalk.hex('#2e401c'), +} + +/** + * Write task in log and execute it + * @param s Name of the tast would be printed in Log + * @param fn Function of task + * @param cwd Optional working path where task is executed + */ +export function doTask(s: string, fn: () => void, cwd?: string) { + const oldCwd = process.cwd() + if (cwd) process.chdir(cwd) + write(style.label(s)) + end(fn()) + if (cwd) process.chdir(oldCwd) +} + +/* + █████╗ ██╗ ██╗████████╗ ██████╗ ███╗ ███╗ █████╗ ████████╗██╗ ██████╗ ███╗ ██╗ +██╔══██╗██║ ██║╚══██╔══╝██╔═══██╗████╗ ████║██╔══██╗╚══██╔══╝██║██╔═══██╗████╗ ██║ +███████║██║ ██║ ██║ ██║ ██║██╔████╔██║███████║ ██║ ██║██║ ██║██╔██╗ ██║ +██╔══██║██║ ██║ ██║ ██║ ██║██║╚██╔╝██║██╔══██║ ██║ ██║██║ ██║██║╚██╗██║ +██║ ██║╚██████╔╝ ██║ ╚██████╔╝██║ ╚═╝ ██║██║ ██║ ██║ ██║╚██████╔╝██║ ╚████║ +╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ +*/ + +let STEP = 1 + +/** + * Prompt user to write something and press ENTER or ESC + * @param message message to show + * @param options message to show + * @returns inputted string or undefined + */ +export async function enterString(message: string, options?: InputFieldOptions) { + const msg = `[${STEP++}] ${message}` + term(style.trace(msg.replace(/(ENTER|ESC)/g, style.info('$1')))) + const result = await term.inputField({ + cancelable: true, + ...(options ?? {}), + }).promise + term('\n') + return result +} + +/** + * Prompt user to press ENTER or ESC + * @param message message to show + * @param condition repeat until true + * @returns `true` if ENTER pressed, `false` otherwise + */ +export async function pressEnterOrEsc(message: string, condition?: () => Promise) { + let oneTime = 0 + while (condition ? !(await condition()) : !oneTime++) + if ((await enterString(message)) === undefined) return false + + return true +} + +export function getBoxForLabel(label: string) { + logUpdate.done() + return function updateBox(...args: any[]) { + return logUpdate( + boxen( + args.map((v, i) => Object.values(style)[i](String(v))).join(' '), + { + borderStyle: 'round', + borderColor: '#22577a', + width : 50, + padding : { left: 1, right: 1 }, + title : style.info(label), + } + ) + ) + } +} diff --git a/dev/build/sftp.ts b/dev/build/sftp.ts new file mode 100644 index 000000000..2ad404967 --- /dev/null +++ b/dev/build/sftp.ts @@ -0,0 +1,122 @@ +import { join, parse } from 'node:path' +import process from 'node:process' + +import fse from 'fs-extra' +import git_describe from 'git-describe' +import replace_in_file from 'replace-in-file' +import Client from 'ssh2-sftp-client' + +import { + end, + loadJson, +} from '../lib/utils.js' +import { getBoxForLabel, globs, pressEnterOrEsc, style } from './build_utils' + +const { readFileSync, writeFileSync } = fse + +const { gitDescribeSync } = git_describe +export async function manageSFTP( + serverRemoveDirs: string[] = [ + 'bansoukou', + 'config', + 'mods', + 'patchouli_books', + 'resources', + 'schematics', + 'scripts', + 'structures', + ], + serverSetupConfig: string = 'server/server-setup-config.yaml' +) { + const sftpConfigs = globs('secrets/sftp_servers/*/sftp.json').map((filename) => { + const dir = parse(filename).dir + return { + dir, + label : dir.split('/').pop(), + config: loadJson(filename) as { [key: string]: string }, + } + }) + + const currentVersion = gitDescribeSync().tag + + const serverConfigTmp = '~tmp-server-setup-config.yaml' + const confText = readFileSync(serverSetupConfig, 'utf8') + .replace( + /(additionalFiles:\s*)\n {4}\S.*$\n {4}\S.*$/m, +`$1 + - url: https://mediafilez.forgecdn.net/files/4886/383/mc2discord-forge-1.12.2-3.3.1.jar + destination: mods/mc2discord-forge-1.12.2-3.3.1.jar` + ) + // .replace( + // /(localFiles:\s*)\n/m, + // `$1 + // - url: https://mediafilez.forgecdn.net/files/4886/383/mc2discord-forge-1.12.2-3.3.1.jar + // destination: mods/mc2discord-forge-1.12.2-3.3.1.jar` + // ) + writeFileSync(serverConfigTmp, confText) + + for (const conf of sftpConfigs) { + if (!(await pressEnterOrEsc( + `To upload SFTP ${style.string(conf.label)} press ENTER. Press ESC to skip.` + ))) + continue + + const sftp = new Client() + const updateBox = getBoxForLabel(conf.label) + + updateBox('Establishing connection') + try { + await sftp.connect(conf.config) + } + catch (error) { + end('Cant connect to SFTP') + continue + } + + updateBox('Removing folders') + const stillToRemove = serverRemoveDirs.slice(0) + await Promise.all( + serverRemoveDirs.map(async (dir) => { + try { + if (!(await sftp.stat(dir)).isDirectory) return + await sftp.rmdir(dir, true) + stillToRemove.splice(stillToRemove.indexOf(dir), 1) + updateBox('Still to remove:', stillToRemove.join(', ')) + } + catch (error) {} + }) + ) + + updateBox(`Copy ${serverConfigTmp}`) + await sftp.fastPut(serverConfigTmp, 'server-setup-config.yaml') + + updateBox('Change and copy server overrides') + const title = `+ Server Started! +` + const spaces = (' ').repeat(Math.max(1, (title.length - currentVersion.length) / 2) | 0) + const replaceResult = replace_in_file.sync({ + files : join(conf.dir, 'overrides/config/mc2discord.toml'), + from : /(start\s*=\s*")[^"]+"/, + to : `$1\`\`\`diff\\n${title}\\n${spaces}${currentVersion}\\n\`\`\`"`, + countMatches: true, + disableGlobs: true, + }) + + updateBox('Remove', 'serverstarter.lock') + sftp.delete('serverstarter.lock', true) + + if (replaceResult.length === 0) + throw new Error('Nothing replaced! Code failure') + + let fileCounter = 0 + sftp.on('upload', () => updateBox('Copy overrides', ++fileCounter)) + await sftp.uploadDir(join(conf.dir, 'overrides'), './') + + await sftp.end() + } +} + +// Launch file +if (import.meta.url === (await import('node:url')).pathToFileURL(process.argv[1]).href) { + await manageSFTP() + process.exit(0) +} diff --git a/dev/make_pack.js b/dev/make_pack.js index d2c218d90..a35459408 100644 --- a/dev/make_pack.js +++ b/dev/make_pack.js @@ -13,25 +13,21 @@ // @ts-check -import { join, parse, relative, resolve } from 'node:path' +import { join, resolve } from 'node:path' -import boxen from 'boxen' import chalk from 'chalk' import * as del from 'del' -import fast_glob from 'fast-glob' import fs_extra from 'fs-extra' import git_describe from 'git-describe' import ignore from 'ignore' -import logUpdate from 'log-update' import open from 'open' import replace_in_file from 'replace-in-file' import simpleGit from 'simple-git' -import Client from 'ssh2-sftp-client' -import terminal_kit from 'terminal-kit' import yargs from 'yargs' +import { doTask, enterString, getIgnoredFiles, globs, pressEnterOrEsc, removeFiles } from './build/build_utils.ts' +import { manageSFTP } from './build/sftp.ts' import { - end, execSyncInherit, loadJson, saveObjAsJson, @@ -39,43 +35,14 @@ import { write, } from './lib/utils.js' +const { lstatSync } + = fs_extra + const { gitDescribeSync } = git_describe -const { terminal: term } = terminal_kit -const { rmSync, mkdirSync, existsSync, renameSync, readFileSync, writeFileSync, lstatSync } +const { rmSync, mkdirSync, existsSync, renameSync, readFileSync, writeFileSync } = fs_extra const git = simpleGit() -const { sync: _globs } = fast_glob - -/** - * Globs with default options `dot: true, onlyFiles: false` - * @param {string | string[]} source - * @param {import('../node_modules/fast-glob/out/settings').Options} [options] - */ -function globs(source, options) { - return _globs(source, { dot: true, onlyFiles: false, ...options }) -} - -/** - * @param {any} ignored - */ -function getIgnoredFiles(ignored) { - return globs( - ignored._rules.filter(r => !r.negative).map(r => r.pattern), - { ignore: ignored._rules.filter(r => r.negative).map(r => r.pattern) } - ) -} - -/** - * @param {string | readonly string[]} files list of globs of files to remove - */ -function removeFiles(files) { - const removed = del.deleteSync(files, { dryRun: false }) - return `removed: ${removed.length}\n${ - removed.map(s => chalk.gray(relative(process.cwd(), s))).join('\n') - }` -} - const argv = yargs(process.argv.slice(2)) .alias('h', 'help') .option('dryRun', { @@ -90,18 +57,6 @@ const argv = yargs(process.argv.slice(2)) }) .parseSync() -const style = { - trace : chalk.hex('#7b4618'), - info : chalk.hex('#915c27'), - log : chalk.hex('#ad8042'), - label : chalk.hex('#bfab67'), - string: chalk.hex('#bfc882'), - number: chalk.hex('#a4b75c'), - status: chalk.hex('#647332'), - chose : chalk.hex('#3e4c22'), - end : chalk.hex('#2e401c'), -} - ;(async () => { const mcClientPath = process.cwd() const sZPath = 'D:/Program Files/7-Zip/7z.exe' @@ -110,63 +65,8 @@ const style = { const tmpDir = 'D:/mc_tmp/' const tmpOverrides = resolve(tmpDir, 'overrides/') - /** - * Write task in log and execute it - * @param {string} s Name of the tast would be printed in Log - * @param {()=>void} fn Function of task - * @param {string} [cwd] Optional working path where task is executed - */ - const doTask = (s, fn, cwd) => { - const oldCwd = process.cwd() - if (cwd) process.chdir(cwd) - write(style.label(s)) - end(fn()) - if (cwd) process.chdir(oldCwd) - } - - /* - █████╗ ██╗ ██╗████████╗ ██████╗ ███╗ ███╗ █████╗ ████████╗██╗ ██████╗ ███╗ ██╗ -██╔══██╗██║ ██║╚══██╔══╝██╔═══██╗████╗ ████║██╔══██╗╚══██╔══╝██║██╔═══██╗████╗ ██║ -███████║██║ ██║ ██║ ██║ ██║██╔████╔██║███████║ ██║ ██║██║ ██║██╔██╗ ██║ -██╔══██║██║ ██║ ██║ ██║ ██║██║╚██╔╝██║██╔══██║ ██║ ██║██║ ██║██║╚██╗██║ -██║ ██║╚██████╔╝ ██║ ╚██████╔╝██║ ╚═╝ ██║██║ ██║ ██║ ██║╚██████╔╝██║ ╚████║ -╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ -*/ write(`${chalk.gray('-'.repeat(20))}\n`) - let STEP = 1 - - /** - * Prompt user to write something and press ENTER or ESC - * @param {string} message message to show - * @param {terminal_kit.Terminal.InputFieldOptions} [options] message to show - * @returns {Promise} inputted string or undefined - */ - async function enterString(message, options) { - const msg = `[${STEP++}] ${message}` - term(style.trace(msg.replace(/(ENTER|ESC)/g, style.info('$1')))) - const result = await term.inputField({ - cancelable: true, - ...(options ?? {}), - }).promise - term('\n') - return result - } - - /** - * Prompt user to press ENTER or ESC - * @param {string} message message to show - * @param {()=>Promise} [condition] repeat until true - * @returns {Promise} `true` if ENTER pressed, `false` otherwise - */ - const pressEnterOrEsc = async (message, condition) => { - let oneTime = 0 - while (condition ? !(await condition()) : !oneTime++) - if ((await enterString(message)) === undefined) return false - - return true - } - if ( await pressEnterOrEsc( `Press ENTER to perform Automation. Press ESC to skip.` @@ -217,7 +117,7 @@ const style = { execSyncInherit(`npx conventional-changelog-cli --config dev/tools/changelog/config.cjs -o ${latestPath}`) // Iconize - execSyncInherit(`esno E:/dev/mc-icons/src/cli.ts "${latestPath}" --silent --no-short --modpack=e2ee --treshold=2`) + execSyncInherit(`ts-node E:/dev/mc-icons/src/cli.ts "${latestPath}" --silent --no-short --modpack=e2ee --treshold=2`) await open(latestPath, { wait: true }) } @@ -262,7 +162,9 @@ const style = { try { rmSync(tmpDir, { recursive: true }) } - catch (err) {} + catch (err) { + process.stdout.write(`\n${chalk.red(`Cannot remove TMP folder ${tmpDir}`)}\n${String(err)}\n`) + } mkdirSync(tmpOverrides, { recursive: true }) }) @@ -335,26 +237,14 @@ const style = { makeZips && doTask('📥 Create server zip ... \n', () => withZip(zipPath_server)('.'), serverRoot) /* -███████╗███████╗████████╗██████╗ -██╔════╝██╔════╝╚══██╔══╝██╔══██╗ -███████╗█████╗ ██║ ██████╔╝ -╚════██║██╔══╝ ██║ ██╔═══╝ -███████║██║ ██║ ██║ -╚══════╝╚═╝ ╚═╝ ╚═╝ + ███████╗███████╗████████╗██████╗ + ██╔════╝██╔════╝╚══██╔══╝██╔══██╗ + ███████╗█████╗ ██║ ██████╔╝ + ╚════██║██╔══╝ ██║ ██╔═══╝ + ███████║██║ ██║ ██║ + ╚══════╝╚═╝ ╚═╝ ╚═╝ */ - /** - * @type {{ dir:string, label:string, config: {[key:string]:string} }[]} - */ - const sftpConfigs = globs('secrets/sftp_servers/*/sftp.json').map((filename) => { - const dir = parse(filename).dir - return { - dir, - label : /** @type {string} */ (dir.split('/').pop()), - config: loadJson(filename), - } - }) - // Relative to overrides const serverAllOverrides = globs('./*', { cwd: tmpOverrides }) @@ -363,67 +253,7 @@ const style = { .filter(f => lstatSync(join(tmpOverrides, f)).isDirectory()) .concat('mods') - for (const conf of sftpConfigs) { - logUpdate.done() - - if (!(await pressEnterOrEsc( - `To upload SFTP ${style.string(conf.label)} press ENTER. Press ESC to skip.` - ))) - continue - - const sftp = new Client() - - const updateBox = (/** @type {any[]} */ ...args) => - logUpdate( - boxen( - args.map((v, i) => Object.values(style)[i](String(v))).join(' '), - { - borderStyle: 'round', - borderColor: '#22577a', - width : 50, - padding : { left: 1, right: 1 }, - title : style.info(conf.label), - } - ) - ) - - updateBox('Establishing connection') - try { - await sftp.connect(conf.config) - } - catch (error) { - end('Cant connect to SFTP') - continue - } - - updateBox('Removing folders') - await Promise.all( - serverRemoveDirs.map(async (dir) => { - try { - if (!(await sftp.stat(dir)).isDirectory) return - await sftp.rmdir(dir, true) - updateBox('Removed folder:', dir) - } - catch (error) {} - }) - ) - - updateBox(`Copy ${serverSetupConfig}`) - await sftp.fastPut(serverSetupConfig, serverSetupConfig) - - updateBox('Change and copy server overrides') - replace_in_file.sync({ - files: join(conf.dir, 'overrides/config/mc2discord.toml'), - from : /(start\s*=\s*")[^"]+"/, - to : `$1\`\`\`diff\\n+ Server Started! +\\n ${nextVersion}\\n\`\`\`"`, - }) - - let fileCounter = 0 - sftp.on('upload', () => updateBox('Copy overrides', ++fileCounter)) - await sftp.uploadDir(join(conf.dir, 'overrides'), './') - - await sftp.end() - } + manageSFTP(serverRemoveDirs, serverSetupConfig) /* ██████╗ ███████╗██╗ ███████╗ █████╗ ███████╗███████╗ diff --git a/eslint.config.js b/eslint.config.js index d0a8d820a..bb0148911 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -6,9 +6,11 @@ export default antfu({ rules : { // Override @antfu rules to my personal prefferences - 'style/key-spacing' : ['error', { align: 'colon' }], - 'antfu/if-newline' : 'off', - 'style/comma-dangle': [ + 'style/key-spacing' : ['error', { align: 'colon' }], + 'style/no-multi-spaces': 'off', + // 'style/type-annotation-spacing': 'off', + 'antfu/if-newline' : 'off', + 'style/comma-dangle' : [ 'error', { functions: 'never', diff --git a/package.json b/package.json index ef0d640bc..842bbdb81 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "@types/conventional-changelog-core": "^4.2.7", "@types/fs-extra": "^11.0.4", "@types/memoizee": "^0.4.11", + "@types/ssh2-sftp-client": "^9.0.3", "@types/terminal-kit": "^2.5.6", "adm-zip": "^0.5.10", "boxen": "^7.1.1",