forked from mirror/ObjToSchematic
Updated atlas building tool, removed old files
This commit is contained in:
parent
4654ac90a0
commit
0ecedd4884
5233
package-lock.json
generated
5233
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,6 @@
|
||||
"start": "webpack serve --config ./webpack.dev.js",
|
||||
"dist": "webpack --config ./webpack.prod.js",
|
||||
"atlas": "ts-node ./tools/build-atlas.ts",
|
||||
"palette": "ts-node ./tools/build-palette.ts",
|
||||
"headless": "ts-node ./tools/run-headless.ts",
|
||||
"build": "tsc"
|
||||
},
|
||||
@ -39,11 +38,10 @@
|
||||
"@types/varint": "^6.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.9.1",
|
||||
"@typescript-eslint/parser": "^5.9.1",
|
||||
"adm-zip": "^0.5.9",
|
||||
"browserify-zlib": "^0.2.0",
|
||||
"bvh-tree": "^1.0.1",
|
||||
"chalk": "^4.1.2",
|
||||
"copy-dir": "^1.3.0",
|
||||
"commander": "^11.0.0",
|
||||
"css-loader": "^6.7.3",
|
||||
"eslint": "^8.7.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
@ -53,7 +51,6 @@
|
||||
"hsv-rgb": "^1.0.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"i18next": "^22.4.14",
|
||||
"images": "^3.2.3",
|
||||
"jest": "^27.5.1",
|
||||
"jpeg-js": "^0.4.4",
|
||||
"json-loader": "^0.5.7",
|
||||
@ -63,9 +60,8 @@
|
||||
"prismarine-nbt": "^2.2.1",
|
||||
"prompt": "^1.2.1",
|
||||
"raw-loader": "^4.0.2",
|
||||
"sharp": "^0.31.1",
|
||||
"sharp": "^0.31.3",
|
||||
"style-loader": "^3.3.1",
|
||||
"tga": "^1.0.7",
|
||||
"ts-jest": "^27.1.3",
|
||||
"ts-loader": "^9.4.2",
|
||||
"ts-node": "^10.1.0",
|
||||
|
@ -1,289 +0,0 @@
|
||||
acacia_log
|
||||
acacia_planks
|
||||
acacia_wood
|
||||
amethyst_block
|
||||
ancient_debris
|
||||
andesite
|
||||
azalea_leaves
|
||||
basalt
|
||||
bedrock
|
||||
birch_log
|
||||
birch_planks
|
||||
birch_wood
|
||||
blackstone
|
||||
black_concrete
|
||||
black_concrete_powder
|
||||
black_glazed_terracotta
|
||||
black_terracotta
|
||||
black_wool
|
||||
blue_concrete
|
||||
blue_concrete_powder
|
||||
blue_glazed_terracotta
|
||||
blue_ice
|
||||
blue_terracotta
|
||||
blue_wool
|
||||
bone_block
|
||||
bookshelf
|
||||
brain_coral_block
|
||||
bricks
|
||||
brown_concrete
|
||||
brown_concrete_powder
|
||||
brown_glazed_terracotta
|
||||
brown_mushroom_block
|
||||
brown_terracotta
|
||||
brown_wool
|
||||
bubble_coral_block
|
||||
budding_amethyst
|
||||
calcite
|
||||
cartography_table
|
||||
chiseled_deepslate
|
||||
chiseled_nether_bricks
|
||||
chiseled_polished_blackstone
|
||||
chiseled_quartz_block
|
||||
chiseled_red_sandstone
|
||||
chiseled_sandstone
|
||||
chiseled_stone_bricks
|
||||
clay
|
||||
coal_block
|
||||
coal_ore
|
||||
coarse_dirt
|
||||
cobbled_deepslate
|
||||
cobblestone
|
||||
copper_block
|
||||
copper_ore
|
||||
cracked_deepslate_bricks
|
||||
cracked_deepslate_tiles
|
||||
cracked_nether_bricks
|
||||
cracked_polished_blackstone_bricks
|
||||
cracked_stone_bricks
|
||||
crafting_table
|
||||
crimson_hyphae
|
||||
crimson_planks
|
||||
crimson_stem
|
||||
crying_obsidian
|
||||
cut_copper
|
||||
cut_red_sandstone
|
||||
cut_sandstone
|
||||
cyan_concrete
|
||||
cyan_concrete_powder
|
||||
cyan_glazed_terracotta
|
||||
cyan_terracotta
|
||||
cyan_wool
|
||||
dark_oak_log
|
||||
dark_oak_planks
|
||||
dark_oak_wood
|
||||
dark_prismarine
|
||||
dead_brain_coral_block
|
||||
dead_bubble_coral_block
|
||||
dead_fire_coral_block
|
||||
dead_horn_coral_block
|
||||
dead_tube_coral_block
|
||||
deepslate
|
||||
deepslate_bricks
|
||||
deepslate_coal_ore
|
||||
deepslate_copper_ore
|
||||
deepslate_diamond_ore
|
||||
deepslate_emerald_ore
|
||||
deepslate_gold_ore
|
||||
deepslate_iron_ore
|
||||
deepslate_lapis_ore
|
||||
deepslate_redstone_ore
|
||||
deepslate_tiles
|
||||
diamond_block
|
||||
diamond_ore
|
||||
diorite
|
||||
dirt
|
||||
dripstone_block
|
||||
emerald_block
|
||||
emerald_ore
|
||||
end_stone
|
||||
end_stone_bricks
|
||||
exposed_copper
|
||||
exposed_cut_copper
|
||||
fire_coral_block
|
||||
fletching_table
|
||||
flowering_azalea_leaves
|
||||
frosted_ice_0
|
||||
frosted_ice_1
|
||||
frosted_ice_2
|
||||
frosted_ice_3
|
||||
gilded_blackstone
|
||||
glowstone
|
||||
gold_block
|
||||
gold_ore
|
||||
granite
|
||||
gravel
|
||||
gray_concrete
|
||||
gray_concrete_powder
|
||||
gray_glazed_terracotta
|
||||
gray_terracotta
|
||||
gray_wool
|
||||
green_concrete
|
||||
green_concrete_powder
|
||||
green_glazed_terracotta
|
||||
green_terracotta
|
||||
green_wool
|
||||
hay_block
|
||||
honeycomb_block
|
||||
horn_coral_block
|
||||
ice
|
||||
iron_block
|
||||
iron_ore
|
||||
jungle_log
|
||||
jungle_planks
|
||||
jungle_wood
|
||||
lapis_block
|
||||
lapis_ore
|
||||
light_blue_concrete
|
||||
light_blue_concrete_powder
|
||||
light_blue_glazed_terracotta
|
||||
light_blue_terracotta
|
||||
light_blue_wool
|
||||
light_gray_concrete
|
||||
light_gray_concrete_powder
|
||||
light_gray_glazed_terracotta
|
||||
light_gray_terracotta
|
||||
light_gray_wool
|
||||
lime_concrete
|
||||
lime_concrete_powder
|
||||
lime_glazed_terracotta
|
||||
lime_terracotta
|
||||
lime_wool
|
||||
lodestone
|
||||
magenta_concrete
|
||||
magenta_concrete_powder
|
||||
magenta_glazed_terracotta
|
||||
magenta_terracotta
|
||||
magenta_wool
|
||||
magma_block
|
||||
mangrove_log
|
||||
mangrove_planks
|
||||
mangrove_wood
|
||||
melon
|
||||
mossy_cobblestone
|
||||
mossy_stone_bricks
|
||||
moss_block
|
||||
mud
|
||||
muddy_mangrove_roots
|
||||
mud_bricks
|
||||
mushroom_stem
|
||||
netherite_block
|
||||
netherrack
|
||||
nether_bricks
|
||||
nether_gold_ore
|
||||
nether_quartz_ore
|
||||
nether_wart_block
|
||||
note_block
|
||||
oak_log
|
||||
oak_planks
|
||||
oak_wood
|
||||
obsidian
|
||||
ochre_froglight
|
||||
orange_concrete
|
||||
orange_concrete_powder
|
||||
orange_glazed_terracotta
|
||||
orange_terracotta
|
||||
orange_wool
|
||||
oxidized_copper
|
||||
oxidized_cut_copper
|
||||
packed_ice
|
||||
packed_mud
|
||||
pearlescent_froglight
|
||||
pink_concrete
|
||||
pink_concrete_powder
|
||||
pink_glazed_terracotta
|
||||
pink_terracotta
|
||||
pink_wool
|
||||
polished_andesite
|
||||
polished_basalt
|
||||
polished_blackstone
|
||||
polished_blackstone_bricks
|
||||
polished_deepslate
|
||||
polished_diorite
|
||||
polished_granite
|
||||
prismarine
|
||||
prismarine_bricks
|
||||
purple_concrete
|
||||
purple_concrete_powder
|
||||
purple_glazed_terracotta
|
||||
purple_terracotta
|
||||
purple_wool
|
||||
purpur_block
|
||||
purpur_pillar
|
||||
quartz_block
|
||||
quartz_bricks
|
||||
quartz_pillar
|
||||
raw_copper_block
|
||||
raw_gold_block
|
||||
raw_iron_block
|
||||
redstone_block
|
||||
redstone_lamp
|
||||
redstone_ore
|
||||
red_concrete
|
||||
red_concrete_powder
|
||||
red_glazed_terracotta
|
||||
red_mushroom_block
|
||||
red_nether_bricks
|
||||
red_sand
|
||||
red_terracotta
|
||||
red_wool
|
||||
rooted_dirt
|
||||
sand
|
||||
sculk
|
||||
sea_lantern
|
||||
shroomlight
|
||||
smithing_table
|
||||
smooth_basalt
|
||||
smooth_quartz
|
||||
smooth_red_sandstone
|
||||
smooth_sandstone
|
||||
smooth_stone
|
||||
snow_block
|
||||
soul_sand
|
||||
soul_soil
|
||||
sponge
|
||||
spruce_log
|
||||
spruce_planks
|
||||
spruce_wood
|
||||
stone
|
||||
stone_bricks
|
||||
stripped_acacia_log
|
||||
stripped_acacia_wood
|
||||
stripped_birch_log
|
||||
stripped_birch_wood
|
||||
stripped_crimson_hyphae
|
||||
stripped_crimson_stem
|
||||
stripped_dark_oak_log
|
||||
stripped_dark_oak_wood
|
||||
stripped_jungle_log
|
||||
stripped_jungle_wood
|
||||
stripped_mangrove_log
|
||||
stripped_mangrove_wood
|
||||
stripped_oak_log
|
||||
stripped_oak_wood
|
||||
stripped_spruce_log
|
||||
stripped_spruce_wood
|
||||
stripped_warped_hyphae
|
||||
stripped_warped_stem
|
||||
target
|
||||
terracotta
|
||||
tube_coral_block
|
||||
tuff
|
||||
verdant_froglight
|
||||
warped_hyphae
|
||||
warped_planks
|
||||
warped_stem
|
||||
warped_wart_block
|
||||
weathered_copper
|
||||
weathered_cut_copper
|
||||
wet_sponge
|
||||
white_concrete
|
||||
white_concrete_powder
|
||||
white_glazed_terracotta
|
||||
white_terracotta
|
||||
white_wool
|
||||
yellow_concrete
|
||||
yellow_concrete_powder
|
||||
yellow_glazed_terracotta
|
||||
yellow_terracotta
|
||||
yellow_wool
|
@ -1,419 +1,288 @@
|
||||
// import images from 'images';
|
||||
// import path from 'path';
|
||||
// import prompt from 'prompt';
|
||||
import { program } from 'commander';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import sharp from 'sharp';
|
||||
import { getAverageColour, getStandardDeviation } from './misc';
|
||||
import { ASSERT } from '../src/util/error_util';
|
||||
import { RGBAUtil } from '../src/colour';
|
||||
|
||||
// import { RGBA, RGBAUtil } from '../src/colour';
|
||||
// import { AppUtil } from '../src/util';
|
||||
// import { LOG, LOG_WARN, Logger } from '../src/util/log_util';
|
||||
program
|
||||
.argument('<textures_directory>', 'The directory to load the blocks texture files from (assets/minecraft/textures/block)')
|
||||
.argument('<models_directory>', 'The directory to load the blocks model files from (assets/minecraft/models/block)')
|
||||
.argument('<output_directory>', 'The directory to write the texture atlas files to')
|
||||
.argument('<ignore_file_path>', 'Ignore file path')
|
||||
|
||||
// const AdmZip = require('adm-zip');
|
||||
// const copydir = require('copy-dir');
|
||||
program.parse();
|
||||
|
||||
// import { AppPaths, PathUtil } from '../src/util/path_util';
|
||||
// import { log } from './logging';
|
||||
// import { ASSERT_EXISTS, getAverageColour, getMinecraftDir, getStandardDeviation } from './misc';
|
||||
const paths = {
|
||||
textures: program.args[0],
|
||||
models: program.args[1],
|
||||
output: program.args[2],
|
||||
ignore: program.args[3],
|
||||
}
|
||||
|
||||
// const BLOCKS_DIR = PathUtil.join(AppPaths.Get.tools, '/blocks');
|
||||
// const MODELS_DIR = PathUtil.join(AppPaths.Get.tools, '/models');
|
||||
type FaceData<T> = {
|
||||
up: T,
|
||||
down: T,
|
||||
north: T,
|
||||
south: T,
|
||||
east: T,
|
||||
west: T,
|
||||
}
|
||||
|
||||
// type TFaceData<T> = {
|
||||
// up: T,
|
||||
// down: T,
|
||||
// north: T,
|
||||
// south: T,
|
||||
// east: T,
|
||||
// west: T,
|
||||
// }
|
||||
// GO!
|
||||
|
||||
// export type TAtlasVersion = {
|
||||
// formatVersion: 3,
|
||||
// atlasSize: number,
|
||||
// blocks: Array<{ name: string, faces: TFaceData<string>, colour: RGBA }>,
|
||||
// textures: { [texture: string]: { atlasColumn: number, atlasRow: number, colour: RGBA, std: number } },
|
||||
// supportedBlockNames: string[],
|
||||
// };
|
||||
const ignoreList = new Set(fs.readFileSync(paths.ignore, 'utf8').split(/\r?\n/));
|
||||
|
||||
// void async function main() {
|
||||
// AppPaths.Get.setBaseDir(PathUtil.join(__dirname, '../..'));
|
||||
// Logger.Get.enableLogToFile();
|
||||
// Logger.Get.initLogFile('atlas');
|
||||
// Load all models to use
|
||||
const allModels = fs.readdirSync(paths.models);
|
||||
const loadedModels = allModels
|
||||
// Remove ignored models
|
||||
.filter((modelFileName) => {
|
||||
return !ignoreList.has(modelFileName);
|
||||
})
|
||||
// Get each models content
|
||||
.map((modelFileName) => {
|
||||
const modelFilePath = path.join(paths.models, modelFileName);
|
||||
const fileContents = fs.readFileSync(modelFilePath, 'utf8');
|
||||
const model = JSON.parse(fileContents);
|
||||
|
||||
// const minecraftDir = getMinecraftDir();
|
||||
switch (model.parent) {
|
||||
case 'minecraft:block/cube_column_horizontal':
|
||||
return {
|
||||
modelFileName: modelFileName,
|
||||
up: model.textures.side,
|
||||
down: model.textures.side,
|
||||
north: model.textures.end,
|
||||
south: model.textures.end,
|
||||
east: model.textures.side,
|
||||
west: model.textures.side,
|
||||
};
|
||||
case 'minecraft:block/cube_all':
|
||||
return {
|
||||
modelFileName: modelFileName,
|
||||
up: model.textures.all,
|
||||
down: model.textures.all,
|
||||
north: model.textures.all,
|
||||
south: model.textures.all,
|
||||
east: model.textures.all,
|
||||
west: model.textures.all,
|
||||
};
|
||||
case 'minecraft:block/cube_column':
|
||||
return {
|
||||
modelFileName: modelFileName,
|
||||
up: model.textures.end,
|
||||
down: model.textures.end,
|
||||
north: model.textures.side,
|
||||
south: model.textures.side,
|
||||
east: model.textures.side,
|
||||
west: model.textures.side,
|
||||
};
|
||||
case 'minecraft:block/cube_bottom_top':
|
||||
return {
|
||||
modelFileName: modelFileName,
|
||||
up: model.textures.top,
|
||||
down: model.textures.bottom,
|
||||
north: model.textures.side,
|
||||
south: model.textures.side,
|
||||
east: model.textures.side,
|
||||
west: model.textures.side,
|
||||
};
|
||||
case 'minecraft:block/cube':
|
||||
return {
|
||||
modelFileName: modelFileName,
|
||||
up: model.textures.up,
|
||||
down: model.textures.down,
|
||||
north: model.textures.north,
|
||||
south: model.textures.south,
|
||||
east: model.textures.east,
|
||||
west: model.textures.west,
|
||||
};
|
||||
case 'minecraft:block/template_single_face':
|
||||
return {
|
||||
modelFileName: modelFileName,
|
||||
up: model.textures.texture,
|
||||
down: model.textures.texture,
|
||||
north: model.textures.texture,
|
||||
south: model.textures.texture,
|
||||
east: model.textures.texture,
|
||||
west: model.textures.texture,
|
||||
};
|
||||
case 'minecraft:block/template_glazed_terracotta':
|
||||
return {
|
||||
modelFileName: modelFileName,
|
||||
up: model.textures.pattern,
|
||||
down: model.textures.pattern,
|
||||
north: model.textures.pattern,
|
||||
south: model.textures.pattern,
|
||||
east: model.textures.pattern,
|
||||
west: model.textures.pattern,
|
||||
};
|
||||
case 'minecraft:block/leaves':
|
||||
return {
|
||||
modelFileName: modelFileName,
|
||||
up: model.textures.all,
|
||||
down: model.textures.all,
|
||||
north: model.textures.all,
|
||||
south: model.textures.all,
|
||||
east: model.textures.all,
|
||||
west: model.textures.all,
|
||||
};
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}, [])
|
||||
.filter((entry) => {
|
||||
return entry !== null;
|
||||
});
|
||||
|
||||
// // Clean up temporary data from previous use
|
||||
// {
|
||||
// fs.rmSync(BLOCKS_DIR, { recursive: true, force: true });
|
||||
// fs.rmSync(MODELS_DIR, { recursive: true, force: true });
|
||||
// }
|
||||
const allTextures = new Set<string>();
|
||||
loadedModels.forEach((model) => {
|
||||
allTextures.add(model?.up);
|
||||
allTextures.add(model?.down);
|
||||
allTextures.add(model?.east);
|
||||
allTextures.add(model?.west);
|
||||
allTextures.add(model?.north);
|
||||
allTextures.add(model?.south);
|
||||
});
|
||||
|
||||
// // Ask for permission to access Minecraft dir
|
||||
// {
|
||||
// log('Prompt', `This script requires files inside '${minecraftDir}'`);
|
||||
|
||||
// const { permission } = await prompt.get({
|
||||
// properties: {
|
||||
// permission: {
|
||||
// pattern: /^[YyNn]$/,
|
||||
// description: 'Do you give permission to access these files? (y/n)',
|
||||
// message: 'Response must be Y or N',
|
||||
// required: true,
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
const atlasSize = Math.ceil(Math.sqrt(allTextures.size));
|
||||
let nextAtlasColumn = 0;
|
||||
let nextAtlasRow = 0;
|
||||
|
||||
// const isResponseYes = ['Y', 'y'].includes(permission as string);
|
||||
// if (!isResponseYes) {
|
||||
// process.exit(0);
|
||||
// }
|
||||
// }
|
||||
const textureData = Array.from(allTextures)
|
||||
.map(async (texture) => {
|
||||
const shortName = texture.split('/')[1]; // Eww
|
||||
const texturePath = path.join(paths.textures, shortName + '.png');
|
||||
|
||||
// ASSERT_EXISTS(minecraftDir);
|
||||
const image = sharp(texturePath);
|
||||
const imageData = Uint8ClampedArray.from(await image.raw().ensureAlpha(1.0).toBuffer()); // 16 x 16 x 4
|
||||
|
||||
// // Prompt user to pick a version
|
||||
// let chosenVersionName: string;
|
||||
// let chosenVersionDir: string;
|
||||
// {
|
||||
// const versionsDir = PathUtil.join(minecraftDir, '/versions');
|
||||
// ASSERT_EXISTS(versionsDir);
|
||||
return {
|
||||
textureName: texture,
|
||||
texturePath: texturePath,
|
||||
image: image,
|
||||
imageData: imageData,
|
||||
}
|
||||
});
|
||||
|
||||
// const versions = fs.readdirSync(versionsDir)
|
||||
// .filter((file) => fs.lstatSync(PathUtil.join(versionsDir, file)).isDirectory())
|
||||
// .map((file) => ({ file, birthtime: fs.lstatSync(PathUtil.join(versionsDir, file)).birthtime }))
|
||||
// .sort((a, b) => b.birthtime.getTime() - a.birthtime.getTime());
|
||||
// {
|
||||
// versions.forEach((version, index) => {
|
||||
// log('Option', `${index + 1}) ${version.file}`);
|
||||
// });
|
||||
// }
|
||||
type ArrayElement<ArrayType extends readonly unknown[]> =
|
||||
ArrayType extends readonly (infer ElementType)[] ? ElementType : never;
|
||||
|
||||
// // Prompt user to pick a version
|
||||
// const { packChoice } = await prompt.get({
|
||||
// properties: {
|
||||
// packChoice: {
|
||||
// description: `Which version do you want to build an atlas for? (1-${versions.length})`,
|
||||
// message: `Response must be between 1 and ${versions.length}`,
|
||||
// required: true,
|
||||
// conform: (value) => {
|
||||
// return value >= 1 && value <= versions.length;
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
Promise.all(textureData)
|
||||
.then(async (res) => {
|
||||
const tmp = res
|
||||
.sort((a, b) => {
|
||||
return a.textureName < b.textureName ? -1 : 1;
|
||||
})
|
||||
.map(async (texture) => {
|
||||
const averageColour = getAverageColour(texture.imageData);
|
||||
const standardDeviation = getStandardDeviation(texture.imageData, averageColour);
|
||||
|
||||
// chosenVersionName = versions[(<number>packChoice) - 1].file;
|
||||
// chosenVersionDir = PathUtil.join(versionsDir, chosenVersionName);
|
||||
// }
|
||||
const atlasColumn = nextAtlasColumn;
|
||||
const atlasRow = nextAtlasRow;
|
||||
|
||||
// // Get vanilla models and textures
|
||||
// {
|
||||
// const jarName = `${chosenVersionName}.jar`;
|
||||
// const jarDir = PathUtil.join(chosenVersionDir, jarName);
|
||||
// ASSERT_EXISTS(jarDir);
|
||||
++nextAtlasColumn;
|
||||
if (nextAtlasColumn >= atlasSize) {
|
||||
++nextAtlasRow;
|
||||
nextAtlasColumn = 0;
|
||||
}
|
||||
|
||||
// log('Info', `Upzipping '${jarDir}'...`);
|
||||
// {
|
||||
// const zip = new AdmZip(jarDir);
|
||||
// const zipEntries = zip.getEntries();
|
||||
// zipEntries.forEach((zipEntry: any) => {
|
||||
// if (zipEntry.entryName.startsWith('assets/minecraft/textures/block')) {
|
||||
// zip.extractEntryTo(zipEntry.entryName, BLOCKS_DIR, false, true);
|
||||
// } else if (zipEntry.entryName.startsWith('assets/minecraft/models/block')) {
|
||||
// zip.extractEntryTo(zipEntry.entryName, MODELS_DIR, false, true);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// log('Success', `Extracted Vanilla models to '${MODELS_DIR}'`);
|
||||
// log('Success', `Extracted Vanilla textures to '${BLOCKS_DIR}'`);
|
||||
// }
|
||||
return {
|
||||
textureName: texture.textureName,
|
||||
texturePath: texture.texturePath,
|
||||
atlasColumn: atlasColumn,
|
||||
atlasRow: atlasRow,
|
||||
colour: averageColour,
|
||||
std: standardDeviation,
|
||||
};
|
||||
});
|
||||
|
||||
// // Prompt user to pick a resource pack
|
||||
// let chosenResourcePackDir: string | undefined;
|
||||
// {
|
||||
// const resourcePacksDir = PathUtil.join(minecraftDir, '/resourcepacks');
|
||||
// ASSERT_EXISTS(resourcePacksDir);
|
||||
Promise.all(tmp)
|
||||
.then(async (data) => {
|
||||
const textureMap = new Map<string, Omit<ArrayElement<typeof data>, 'textureName' | 'texturePath'>>();
|
||||
data.forEach((texture) => {
|
||||
textureMap.set(texture.textureName, {
|
||||
atlasColumn: texture.atlasColumn,
|
||||
atlasRow: texture.atlasRow,
|
||||
colour: texture.colour,
|
||||
std: texture.std,
|
||||
});
|
||||
});
|
||||
|
||||
// const resourcePacks = fs.readdirSync(resourcePacksDir);
|
||||
// {
|
||||
// log('Option', `1) Vanilla`);
|
||||
// resourcePacks.forEach((resourcePack, index) => {
|
||||
// log('Option', `${index + 2}) ${resourcePack}`);
|
||||
// });
|
||||
// }
|
||||
const baseImage = await sharp({
|
||||
create: {
|
||||
width: atlasSize * 16 * 3,
|
||||
height: atlasSize * 16 * 3,
|
||||
channels: 4,
|
||||
background: { r: 255, g: 255, b: 255, alpha: 0.0 },
|
||||
}
|
||||
});
|
||||
|
||||
// const { resourcePackChoiceIndex } = await prompt.get({
|
||||
// properties: {
|
||||
// packChoice: {
|
||||
// description: `Which resource pack do you want to build an atlas for? (1-${resourcePacks.length + 1})`,
|
||||
// message: `Response must be between 1 and ${resourcePacks.length + 1}`,
|
||||
// required: true,
|
||||
// conform: (value) => {
|
||||
// return value >= 1 && value <= resourcePacks.length + 1;
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
const compositeData: sharp.OverlayOptions[] = [];
|
||||
data.forEach((x) => {
|
||||
for (let i = 0; i < 3; ++i) {
|
||||
for (let j = 0; j < 3; ++j) {
|
||||
compositeData.push({
|
||||
input: x.texturePath,
|
||||
blend: 'over',
|
||||
left: (x.atlasColumn * 16) * 3 + 16 * i,
|
||||
top: (x.atlasRow * 16) * 3 + 16 * j,
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// chosenResourcePackDir = (<number>resourcePackChoiceIndex) === 1 ? undefined : resourcePacks[(<number>resourcePackChoiceIndex) - 2];
|
||||
// }
|
||||
baseImage.composite(compositeData)
|
||||
.toFile(path.join(paths.output, 'atlas.png'))
|
||||
.then((res) => {
|
||||
console.log('Done!');
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
|
||||
// // Get resource pack textures
|
||||
// if (chosenResourcePackDir !== undefined) {
|
||||
// log('Warning', 'Using non-16x16 texture packs is not supported and will result in undefined behaviour');
|
||||
// {
|
||||
// if (fs.lstatSync(chosenResourcePackDir).isDirectory()) {
|
||||
// log('Info', `Resource pack '${chosenResourcePackDir}' is a directory`);
|
||||
const blocks = loadedModels.map((model) => {
|
||||
ASSERT(model !== null);
|
||||
|
||||
// const blockTexturesSrc = PathUtil.join(chosenResourcePackDir, 'assets/minecraft/textures/block');
|
||||
// const blockTexturesDst = BLOCKS_DIR;
|
||||
const faces = {
|
||||
up: model.up,
|
||||
down: model.down,
|
||||
north: model.north,
|
||||
south: model.south,
|
||||
east: model.east,
|
||||
west: model.west,
|
||||
};
|
||||
|
||||
// log('Info', `Copying ${blockTexturesSrc} to ${blockTexturesDst}...`);
|
||||
// copydir(blockTexturesSrc, blockTexturesDst, {
|
||||
// utimes: true,
|
||||
// mode: true,
|
||||
// cover: true,
|
||||
// });
|
||||
// } else {
|
||||
// log('Info', `Resource pack '${chosenResourcePackDir}' is not a directory, expecting to be a .zip`);
|
||||
const faceColours = Object.values(faces).map((texture) => {
|
||||
const textureData = textureMap.get(texture);
|
||||
ASSERT(textureData !== undefined);
|
||||
return textureData.colour;
|
||||
});
|
||||
|
||||
// const zip = new AdmZip(chosenResourcePackDir);
|
||||
// const zipEntries = zip.getEntries();
|
||||
// zipEntries.forEach((zipEntry: any) => {
|
||||
// if (zipEntry.entryName.startsWith('assets/minecraft/textures/block')) {
|
||||
// zip.extractEntryTo(zipEntry.entryName, BLOCKS_DIR, false, true);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// log('Success', `Copied block textures successfully`);
|
||||
// }
|
||||
return {
|
||||
name: 'minecraft:' + model.modelFileName.split('.')[0],
|
||||
faces: faces,
|
||||
colour: RGBAUtil.average(...faceColours),
|
||||
}
|
||||
});
|
||||
|
||||
// // Load the ignore list
|
||||
// let ignoreList: Array<string> = [];
|
||||
// {
|
||||
// log('Info', 'Loading ignore list...');
|
||||
// {
|
||||
// const ignoreListPath = PathUtil.join(AppPaths.Get.tools, './models-ignore-list.txt');
|
||||
// if (fs.existsSync(ignoreListPath)) {
|
||||
// log('Success', `Found ignore list in '${ignoreListPath}'`);
|
||||
// ignoreList = fs.readFileSync(ignoreListPath, 'utf-8').replace(/\r/g, '').split('\n');
|
||||
// log('Info', `Found ${ignoreList.length} blocks in ignore list`);
|
||||
// } else {
|
||||
// log('Warning', `Could not find ignore list '${ignoreListPath}'`);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
console.log(textureMap);
|
||||
|
||||
// const usedTextures = new Set<string>();
|
||||
// const usedModels: Array<{ name: string, faces: TFaceData<string> }> = [];
|
||||
const textures: Record<any, any> = {};
|
||||
textureMap.forEach((value, key) => {
|
||||
textures[key] = value;
|
||||
});
|
||||
|
||||
// // Load all models to use
|
||||
// {
|
||||
// const allModels = fs.readdirSync(MODELS_DIR);
|
||||
// log('Info', `Found ${allModels.length} models in '${MODELS_DIR}'`);
|
||||
const atlasFile = {
|
||||
formatVersion: 3,
|
||||
atlasSize: atlasSize,
|
||||
blocks: blocks,
|
||||
textures: textures,
|
||||
}
|
||||
|
||||
// allModels.forEach((modelRelDir, index) => {
|
||||
// const modelAbsDir = PathUtil.join(MODELS_DIR, modelRelDir);
|
||||
// const parsed = path.parse(modelAbsDir);
|
||||
fs.writeFileSync(path.join(paths.output, 'atlas.atlas'), JSON.stringify(atlasFile, null, 4));
|
||||
});
|
||||
|
||||
// if (parsed.ext !== '.json' || ignoreList.includes(parsed.base)) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const fileData = fs.readFileSync(modelAbsDir, 'utf8');
|
||||
// const modelData = JSON.parse(fileData);
|
||||
|
||||
// const faceData: TFaceData<string> | undefined = (() => {
|
||||
// switch (modelData.parent) {
|
||||
// case 'minecraft:block/cube_column_horizontal':
|
||||
// return {
|
||||
// up: modelData.textures.side,
|
||||
// down: modelData.textures.side,
|
||||
// north: modelData.textures.end,
|
||||
// south: modelData.textures.end,
|
||||
// east: modelData.textures.side,
|
||||
// west: modelData.textures.side,
|
||||
// };
|
||||
// case 'minecraft:block/cube_all':
|
||||
// return {
|
||||
// up: modelData.textures.all,
|
||||
// down: modelData.textures.all,
|
||||
// north: modelData.textures.all,
|
||||
// south: modelData.textures.all,
|
||||
// east: modelData.textures.all,
|
||||
// west: modelData.textures.all,
|
||||
// };
|
||||
// case 'minecraft:block/cube_column':
|
||||
// return {
|
||||
// up: modelData.textures.end,
|
||||
// down: modelData.textures.end,
|
||||
// north: modelData.textures.side,
|
||||
// south: modelData.textures.side,
|
||||
// east: modelData.textures.side,
|
||||
// west: modelData.textures.side,
|
||||
// };
|
||||
// case 'minecraft:block/cube_bottom_top':
|
||||
// return {
|
||||
// up: modelData.textures.top,
|
||||
// down: modelData.textures.bottom,
|
||||
// north: modelData.textures.side,
|
||||
// south: modelData.textures.side,
|
||||
// east: modelData.textures.side,
|
||||
// west: modelData.textures.side,
|
||||
// };
|
||||
// case 'minecraft:block/cube':
|
||||
// return {
|
||||
// up: modelData.textures.up,
|
||||
// down: modelData.textures.down,
|
||||
// north: modelData.textures.north,
|
||||
// south: modelData.textures.south,
|
||||
// east: modelData.textures.east,
|
||||
// west: modelData.textures.west,
|
||||
// };
|
||||
// case 'minecraft:block/template_single_face':
|
||||
// return {
|
||||
// up: modelData.textures.texture,
|
||||
// down: modelData.textures.texture,
|
||||
// north: modelData.textures.texture,
|
||||
// south: modelData.textures.texture,
|
||||
// east: modelData.textures.texture,
|
||||
// west: modelData.textures.texture,
|
||||
// };
|
||||
// case 'minecraft:block/template_glazed_terracotta':
|
||||
// return {
|
||||
// up: modelData.textures.pattern,
|
||||
// down: modelData.textures.pattern,
|
||||
// north: modelData.textures.pattern,
|
||||
// south: modelData.textures.pattern,
|
||||
// east: modelData.textures.pattern,
|
||||
// west: modelData.textures.pattern,
|
||||
// };
|
||||
// case 'minecraft:block/leaves':
|
||||
// return {
|
||||
// up: modelData.textures.all,
|
||||
// down: modelData.textures.all,
|
||||
// north: modelData.textures.all,
|
||||
// south: modelData.textures.all,
|
||||
// east: modelData.textures.all,
|
||||
// west: modelData.textures.all,
|
||||
// };
|
||||
// }
|
||||
// })();
|
||||
|
||||
// // Debug logging to file
|
||||
// if (faceData === undefined) {
|
||||
// LOG_WARN(`Could not parse '${parsed.base}'`);
|
||||
// return;
|
||||
// } else {
|
||||
// LOG(`Parsed '${parsed.base}'`);
|
||||
// }
|
||||
|
||||
// // Check that the textures that this model uses can be found
|
||||
// Object.values(faceData).forEach((texture) => {
|
||||
// const textureBaseName = texture.split('/')[1] + '.png';
|
||||
// const textureAbsDir = PathUtil.join(BLOCKS_DIR, textureBaseName);
|
||||
|
||||
// if (fs.existsSync(textureAbsDir)) {
|
||||
// LOG(`Found '${textureAbsDir}'`);
|
||||
// } else {
|
||||
// log('Warning', `'${parsed.base}' uses texture '${texture}' but the texture file could not be found at '${textureAbsDir}'`);
|
||||
// return;
|
||||
// }
|
||||
// });
|
||||
|
||||
// // Update usedTextures and usedModels
|
||||
// Object.values(faceData).forEach((texture) => {
|
||||
// usedTextures.add(texture);
|
||||
// });
|
||||
// usedModels.push({
|
||||
// name: parsed.name,
|
||||
// faces: faceData,
|
||||
// });
|
||||
// });
|
||||
|
||||
// LOG('All Textures', usedTextures);
|
||||
// LOG('All Models', usedModels);
|
||||
// log('Info', `Found ${usedModels.length} models to use`);
|
||||
|
||||
// // Prompt user for an atlas name
|
||||
// const { atlasName } = await prompt.get({
|
||||
// properties: {
|
||||
// atlasName: {
|
||||
// pattern: /^[a-zA-Z\-]+$/,
|
||||
// description: 'What do you want to call this texture atlas?',
|
||||
// message: 'Name must only be letters or dash',
|
||||
// required: true,
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
|
||||
// // Create atlas texture file
|
||||
// const textureDetails: { [texture: string]: { atlasColumn: number, atlasRow: number, colour: RGBA, std: number } } = {};
|
||||
// const atlasSize = Math.ceil(Math.sqrt(usedTextures.size));
|
||||
// {
|
||||
// const atlasWidth = atlasSize * 16;
|
||||
|
||||
// let offsetX = 0;
|
||||
// let offsetY = 0;
|
||||
// const outputImage = images(atlasWidth * 3, atlasWidth * 3);
|
||||
|
||||
// usedTextures.forEach((texture) => {
|
||||
// const shortName = texture.split('/')[1]; // Eww
|
||||
// const absolutePath = path.join(BLOCKS_DIR, shortName + '.png');
|
||||
// const fileData = fs.readFileSync(absolutePath);
|
||||
// //const pngData = PNG.sync.read(fileData);
|
||||
// const image = images(absolutePath);
|
||||
|
||||
// for (let x = 0; x < 3; ++x) {
|
||||
// for (let y = 0; y < 3; ++y) {
|
||||
// outputImage.draw(image, 16 * (3 * offsetX + x), 16 * (3 * offsetY + y));
|
||||
// }
|
||||
// }
|
||||
|
||||
// // TODO Unimplemented
|
||||
// /*
|
||||
// const average = getAverageColour(pngData);
|
||||
// textureDetails[texture] = {
|
||||
// atlasColumn: offsetX,
|
||||
// atlasRow: offsetY,
|
||||
// colour: average,
|
||||
// std: getStandardDeviation(pngData, average),
|
||||
// };
|
||||
// */
|
||||
|
||||
// ++offsetX;
|
||||
// if (offsetX >= atlasSize) {
|
||||
// ++offsetY;
|
||||
// offsetX = 0;
|
||||
// }
|
||||
// });
|
||||
|
||||
// const atlasDir = PathUtil.join(AppPaths.Get.atlases, `./${atlasName}.png`);
|
||||
// outputImage.save(atlasDir);
|
||||
// }
|
||||
|
||||
// const modelDetails = new Array<{ name: string, faces: TFaceData<string>, colour: RGBA }>();
|
||||
// {
|
||||
// usedModels.forEach((model) => {
|
||||
// const faceColours = Object.values(model.faces)
|
||||
// .map((face) => textureDetails[face]!.colour);
|
||||
|
||||
// modelDetails.push({
|
||||
// name: AppUtil.Text.namespaceBlock(model.name),
|
||||
// faces: model.faces,
|
||||
// colour: RGBAUtil.average(...faceColours),
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
|
||||
// const toExport: TAtlasVersion = {
|
||||
// formatVersion: 3,
|
||||
// atlasSize: atlasSize,
|
||||
// blocks: modelDetails,
|
||||
// textures: textureDetails,
|
||||
// supportedBlockNames: modelDetails.map((model) => model.name),
|
||||
// };
|
||||
|
||||
// fs.writeFileSync(path.join(AppPaths.Get.atlases, `./${atlasName}.atlas`), JSON.stringify(toExport, null, 4));
|
||||
// }
|
||||
// }();
|
||||
console.log('Unimplemented');
|
||||
});
|
@ -1,68 +0,0 @@
|
||||
// import fs from 'fs';
|
||||
// import path from 'path';
|
||||
// import prompt from 'prompt';
|
||||
|
||||
// import { Palette } from '../src/palette';
|
||||
// import { AppPaths, PathUtil } from '../src/util/path_util';
|
||||
// import { log } from './logging';
|
||||
|
||||
// const PALETTE_NAME_REGEX = /^[a-zA-Z\-]+$/;
|
||||
|
||||
// void async function main() {
|
||||
// AppPaths.Get.setBaseDir(PathUtil.join(__dirname, '../..'));
|
||||
|
||||
// log('Info', 'Creating a new palette...');
|
||||
|
||||
// const paletteBlocksDir = path.join(AppPaths.Get.tools, './new-palette-blocks.txt');
|
||||
// if (!fs.existsSync(paletteBlocksDir)) {
|
||||
// log('Failure', 'Could not find /tools/new-palette-blocks.txt');
|
||||
// return;
|
||||
// }
|
||||
// log('Success', 'Found list of blocks to use in /tools/new-palette-blocks.txt');
|
||||
|
||||
// let blocksToUse: string[] = fs.readFileSync(paletteBlocksDir, 'utf8').replace(/\r/g, '').split('\n');
|
||||
// blocksToUse = blocksToUse.filter((block) => {
|
||||
// return block.length !== 0;
|
||||
// });
|
||||
// if (blocksToUse.length === 0) {
|
||||
// log('Failure', 'No blocks listed for palette');
|
||||
// log('Info', 'List the blocks you want from /tools/all-supported-blocks.txt ');
|
||||
// return;
|
||||
// }
|
||||
// log('Info', `Found ${blocksToUse.length} blocks to use`);
|
||||
|
||||
// const schema: prompt.Schema = {
|
||||
// properties: {
|
||||
// paletteName: {
|
||||
// pattern: PALETTE_NAME_REGEX,
|
||||
// description: 'What do you want to call this block palette? (e.g. my-block-palette)',
|
||||
// message: 'Must be only letters or dash',
|
||||
// required: true,
|
||||
// },
|
||||
// },
|
||||
// };
|
||||
|
||||
// const promptUser = await prompt.get(schema);
|
||||
|
||||
// log('Info', 'Creating palette...');
|
||||
// const palette = Palette.create();
|
||||
// if (palette === undefined) {
|
||||
// log('Failure', 'Invalid palette name');
|
||||
// return;
|
||||
// }
|
||||
|
||||
// log('Info', 'Adding blocks to palette...');
|
||||
// for (const blockNames of blocksToUse) {
|
||||
// palette.add(blockNames);
|
||||
// }
|
||||
|
||||
// log('Info', 'Saving palette...');
|
||||
// const success = palette.save(promptUser.paletteName as string);
|
||||
|
||||
// if (success) {
|
||||
// log('Success', 'Palette saved.');
|
||||
// } else {
|
||||
// log('Failure', 'Could not save palette.');
|
||||
// }
|
||||
// }();
|
||||
console.log('Unimplemented');
|
@ -1,61 +0,0 @@
|
||||
import chalk from 'chalk';
|
||||
|
||||
export type TLogStyle = 'None' | 'Option' | 'Prompt' | 'Info' | 'Warning' | 'Failure' | 'Success';
|
||||
|
||||
export function log(style: TLogStyle, message: string) {
|
||||
switch (style) {
|
||||
case 'None': {
|
||||
/* eslint-disable-next-line no-console */
|
||||
console.log(` ${chalk.whiteBright(message)}`);
|
||||
break;
|
||||
}
|
||||
case 'Prompt': {
|
||||
/* eslint-disable-next-line no-console */
|
||||
console.log(`${chalk.blue.inverse('INFO')} ${chalk.blue(message)}`);
|
||||
break;
|
||||
}
|
||||
case 'Option': {
|
||||
/* eslint-disable-next-line no-console */
|
||||
console.log(`${chalk.magenta(message)}`);
|
||||
break;
|
||||
}
|
||||
case 'Info': {
|
||||
/* eslint-disable-next-line no-console */
|
||||
console.log(`${chalk.white(message)}`);
|
||||
break;
|
||||
}
|
||||
case 'Warning': {
|
||||
/* eslint-disable-next-line no-console */
|
||||
console.log(`${chalk.yellow.inverse('WARN')} ${chalk.yellow(message)}`);
|
||||
break;
|
||||
}
|
||||
case 'Failure': {
|
||||
/* eslint-disable-next-line no-console */
|
||||
console.log(`${chalk.red.inverse('UHOH')} ${chalk.red(message)}`);
|
||||
break;
|
||||
}
|
||||
case 'Success': {
|
||||
/* eslint-disable-next-line no-console */
|
||||
console.log(`${chalk.green.inverse(' OK ')} ${chalk.green(message)}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Conditionally log to the console
|
||||
* @param condition The condition to evaluate
|
||||
* @param trueMessage The message to print if the condition is true
|
||||
* @param falseMessage The message to print if the condition is false
|
||||
* @param exitOnFalse Should the process exit if the condition is false
|
||||
*/
|
||||
export function clog(condition: boolean, trueMessage: string, falseMessage: string, exitOnFalse: boolean = true) {
|
||||
if (condition) {
|
||||
log('Success', trueMessage);
|
||||
} else {
|
||||
log('Failure', falseMessage);
|
||||
if (exitOnFalse) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +1,15 @@
|
||||
import path from 'path';
|
||||
import prompt from 'prompt';
|
||||
|
||||
import { RGBA } from '../src/colour';
|
||||
import { clog, log } from './logging';
|
||||
|
||||
export const ASSERT = (condition: boolean, onFailMessage: string) => {
|
||||
if (!condition) {
|
||||
log('Failure', onFailMessage);
|
||||
process.exit(0);
|
||||
}
|
||||
};
|
||||
|
||||
type PNG = {};
|
||||
|
||||
export function getAverageColour(image: PNG): RGBA {
|
||||
/*
|
||||
export function getAverageColour(image: Uint8ClampedArray): RGBA {
|
||||
let r = 0;
|
||||
let g = 0;
|
||||
let b = 0;
|
||||
let a = 0;
|
||||
let weight = 0;
|
||||
for (let x = 0; x < image.width; ++x) {
|
||||
for (let y = 0; y < image.height; ++y) {
|
||||
const index = 4 * (image.width * y + x);
|
||||
const rgba = image.data.slice(index, index + 4);
|
||||
for (let x = 0; x < 16; ++x) {
|
||||
for (let y = 0; y < 16; ++y) {
|
||||
const index = 4 * (16 * y + x);
|
||||
const rgba = image.slice(index, index + 4);
|
||||
const alpha = rgba[3] / 255;
|
||||
r += (rgba[0] / 255) * alpha;
|
||||
g += (rgba[1] / 255) * alpha;
|
||||
@ -32,27 +18,22 @@ export function getAverageColour(image: PNG): RGBA {
|
||||
weight += alpha;
|
||||
}
|
||||
}
|
||||
const numPixels = image.width * image.height;
|
||||
const numPixels = 16 * 16;
|
||||
return {
|
||||
r: r / weight,
|
||||
g: g / weight,
|
||||
b: b / weight,
|
||||
a: a / numPixels,
|
||||
};
|
||||
*/
|
||||
return { r: 0, g: 0, b: 0, a: 0 };
|
||||
}
|
||||
|
||||
|
||||
export function getStandardDeviation(image: PNG, average: RGBA): number {
|
||||
return 0; // TODO Unimplemented
|
||||
/*
|
||||
export function getStandardDeviation(image: Uint8ClampedArray, average: RGBA): number {
|
||||
let squaredDist = 0.0;
|
||||
let weight = 0.0;
|
||||
for (let x = 0; x < image.width; ++x) {
|
||||
for (let y = 0; y < image.height; ++y) {
|
||||
const index = 4 * (image.width * y + x);
|
||||
const rgba = image.data.slice(index, index + 4);
|
||||
for (let x = 0; x < 16; ++x) {
|
||||
for (let y = 0; y < 16; ++y) {
|
||||
const index = 4 * (16 * y + x);
|
||||
const rgba = image.slice(index, index + 4);
|
||||
const alpha = rgba[3] / 255;
|
||||
weight += alpha;
|
||||
const r = (rgba[0] / 255) * alpha;
|
||||
@ -62,35 +43,4 @@ export function getStandardDeviation(image: PNG, average: RGBA): number {
|
||||
}
|
||||
}
|
||||
return Math.sqrt(squaredDist / weight);
|
||||
*/
|
||||
}
|
||||
|
||||
export async function getPermission() {
|
||||
const directory = getMinecraftDir();
|
||||
log('Prompt', `This script requires files inside of ${directory}`);
|
||||
const { permission } = await prompt.get({
|
||||
properties: {
|
||||
permission: {
|
||||
pattern: /^[YyNn]$/,
|
||||
description: 'Do you give permission to access these files? (y/n)',
|
||||
message: 'Response must be Y or N',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
const responseYes = ['Y', 'y'].includes(permission as string);
|
||||
if (!responseYes) {
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
export function getMinecraftDir(): string {
|
||||
switch (process.platform) {
|
||||
case 'darwin': // MacOS
|
||||
return path.join(process.env.HOME!, './Library/Application Support/minecraft');
|
||||
case 'win32': // Windows
|
||||
return path.join(process.env.APPDATA!, './.minecraft');
|
||||
default:
|
||||
return path.join(require('os').homedir(), '/.minecraft');
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user