Skip to content

Commit

Permalink
Merge pull request #572 from ewanhowell5195/master
Browse files Browse the repository at this point in the history
Resource Pack Utilities - Missing Textures
  • Loading branch information
JannisX11 authored Jul 10, 2024
2 parents f4fe401 + d83468c commit 7f21e78
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 21 deletions.
2 changes: 1 addition & 1 deletion plugins.json
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@
"author": "Ewan Howell",
"description": "A collection of utilities to assist with resource pack creation.",
"tags": ["Minecraft: Java Edition", "Resource Packs", "Utilities"],
"version": "1.0.0",
"version": "1.1.0",
"min_version": "4.10.0",
"variant": "desktop",
"website": "https://ewanhowell.com/plugins/resource-pack-utilities/",
Expand Down
4 changes: 4 additions & 0 deletions plugins/resource_pack_utilities/about.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
<h3>Lang Stripper</h3>
<p>Lang Stripper is a tool that will go through all the language files in an resource pack and remove any entries that have not been modified.</p>
</li>
<li>
<h3>Missing Textures</h3>
<p>Missing Textures is a tool that will check what textures you have in a resource pack and tell you which ones the resource pack is missing.</p>
</li>
<li>
<h3>Pack Cleaner</h3>
<p>Pack Cleaner is a tool that will go through all the files in a resource pack and compare them against the vanilla assets, removing them if they are unmodified.</p>
Expand Down
20 changes: 20 additions & 0 deletions plugins/resource_pack_utilities/changelog.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,25 @@
]
}
]
},
"1.1.0": {
"title": "1.1.0",
"date": "2024-07-09",
"author": "Ewan Howell",
"categories": [
{
"title": "New Features",
"list": [
"Added Missing Textures utility",
"Added ability to export and load ignore lists"
]
},
{
"title": "Changes",
"list": [
"Changed logs to save as a \".log\" file"
]
}
]
}
}
216 changes: 196 additions & 20 deletions plugins/resource_pack_utilities/resource_pack_utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
author: "Ewan Howell",
description,
tags: ["Minecraft: Java Edition", "Resource Packs", "Utilities"],
version: "1.0.0",
version: "1.1.0",
min_version: "4.10.0",
variant: "desktop",
website: `https://ewanhowell.com/plugins/${id.replace(/_/g, "-")}/`,
Expand Down Expand Up @@ -322,6 +322,7 @@
display: flex;
gap: 40px;
flex-direction: row;
align-items: flex-start;
}
}
Expand Down Expand Up @@ -575,7 +576,7 @@
MenuBar.addAction(action2, "tools")
document.addEventListener("keydown", copyText)
// dialog.show()
// dialog.content_vue.utility = "batchExporter"
// dialog.content_vue.utility = "missingTextures"
},
onunload() {
document.removeEventListener("keydown", copyText)
Expand Down Expand Up @@ -910,8 +911,41 @@
})
}

function getDate() {
return new Date().toISOString().replace(/T/, "_").replace(/:/g, "-").split(".")[0]
}

function formatFilePaths(paths) {
const tree = {}
paths.map(filePath => filePath.split(/\/|\\/)).forEach(filePath => {
let branch = tree
filePath.slice(0, -1).forEach(directory => {
if (!branch[directory]) {
branch[directory] = {}
}
branch = branch[directory]
})
branch[filePath[filePath.length - 1]] = true
})

const lines = []
function branchToString(branch, indentLevel = 0) {
for (const [key, value] of Object.entries(branch)) {
lines.push(" ".repeat(indentLevel) + key)
if (typeof value === "object") {
branchToString(value, indentLevel + 1)
}
}
}
branchToString(tree)

return lines.join("\n")
}

// Constants

const header = `Generated by the Resource Pack Utilities plugin for Blockbench: https://ewanhowell.com/plugins/${id.replace(/_/g, "-")}/\n\n`

const releasePattern = new RegExp("^[\\d\\.]+$")
const invalidDirPattern = new RegExp('[\\\\/:*?"<>|`]')
const simpleFilePattern = new RegExp("\\.(fsh|vsh|glsl|txt|ogg|zip|icns)$")
Expand Down Expand Up @@ -947,7 +981,7 @@
props: {
value: {},
placeholder: {
default: "Select Folder"
default: "Folder"
}
},
data() {
Expand All @@ -963,7 +997,7 @@
methods: {
selectFolder(title = "folder") {
const dir = Blockbench.pickDirectory({
title: `Select ${title}`,
title: `Select the ${title}`,
startpath: this.folder || path.join(settings.minecraft_directory.value, "resourcepacks")
})
if (dir) {
Expand Down Expand Up @@ -997,7 +1031,7 @@
`,
template: `
<div class="folder-selector" @click="selectFolder(buttonText)">
<input disabled type="text" :value="formatPath(folder)" :placeholder="placeholder">
<input disabled type="text" :value="formatPath(folder)" :placeholder="'Select ' + placeholder">
<button class="material-icons">folder_open</button>
</div>
`
Expand Down Expand Up @@ -1112,6 +1146,35 @@
}
this.newWord = ""
setTimeout(() => this.$refs.input.focus(), 0)
},
load() {
Blockbench.import({
title: "Load Ignore List",
extensions: ["json"],
type: "JSON"
}, files => {
try {
const data = JSON.parse(files[0].content)
if (!Array.isArray(data) || data.some(e => typeof e !== "string")) {
throw new Error
}
this.ignoreList = Array.from(new Set(data.map(e => e.toLowerCase().trim())))
} catch {
Blockbench.showQuickMessage("Invalid ignore list")
}
})
},
save() {
if (!this.ignoreList.length) {
Blockbench.showQuickMessage("The ignore list is empty")
return
}
Blockbench.export({
extensions: ["json"],
type: "JSON",
name: "ignore_list",
content: JSON.stringify(this.ignoreList, null, 2)
}, () => Blockbench.showQuickMessage("Exported Ignore List"))
}
},
styles: `
Expand Down Expand Up @@ -1154,9 +1217,42 @@
opacity: 1;
}
}
.ignore-list-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: -8px;
h3 {
flex: 1;
margin: 0;
}
.tool {
margin: 0;
width: initial;
height: initial;
cursor: pointer;
}
i {
margin: 0;
}
}
`,
template: `
<h3>Ignore List</h3>
<div class="ignore-list-header">
<h3>Ignore List</h3>
<div class="tool" @click="load">
<div class="tooltip">Load Ignore List</div>
<i class="material-icons">upload</i>
</div>
<div class="tool" @click="save">
<div class="tooltip">Export Ignore List</div>
<i class="material-icons">save</i>
</div>
</div>
<p>Files and folders that include these terms will<br>be ignored</p>
<div>
<input type="text" placeholder="Enter term" v-model="newWord" ref="input" @keydown.enter="addWord">
Expand Down Expand Up @@ -1206,8 +1302,8 @@
},
save() {
Blockbench.export({
extensions: ["txt"],
type: "Text file",
extensions: ["log"],
type: "Log file",
name: "log",
content: this.value.map(e => e[1]).join("\n\n").replaceAll("`", "")
}, () => Blockbench.showQuickMessage("Saved log"))
Expand Down Expand Up @@ -1759,7 +1855,7 @@
<div class="row">
<div class="col spacer">
<h3>Folder to Optimise:</h3>
<folder-selector v-model="folder">the folder to optimise the JSON of</folder-selector>
<folder-selector v-model="folder">folder to optimise the JSON of</folder-selector>
<checkbox-row v-model="types.json">Optimise <code>.json</code> files</checkbox-row>
<checkbox-row v-model="types.mcmeta">Optimise <code>.mcmeta</code> files</checkbox-row>
<checkbox-row v-model="types.jem">Optimise <code>.jem</code> files</checkbox-row>
Expand Down Expand Up @@ -1868,7 +1964,7 @@
<div class="row">
<div class="col spacer">
<h3>Folder to Optimise:</h3>
<folder-selector v-model="folder">the folder to optimise the CIT properties files of</folder-selector>
<folder-selector v-model="folder">folder to optimise the CIT properties files of</folder-selector>
</div>
<ignore-list v-model="ignoreList" style="align-self: flex-start;" />
</div>
Expand Down Expand Up @@ -2147,7 +2243,7 @@
<div class="row">
<div class="col spacer">
<h3>Output Location:</h3>
<folder-selector v-model="folder">the folder to output the resource pack to</folder-selector>
<folder-selector v-model="folder">folder to output the resource pack to</folder-selector>
<input-row v-model="name" placeholder="Enter name…" width="120" :required="attemptedStart && !name.length">Pack Name</input-row>
<input-row v-model="description" placeholder="Enter description…" width="120">Pack Description</input-row>
<version-selector v-model="version" />
Expand Down Expand Up @@ -2394,7 +2490,7 @@
<div class="row">
<div class="col spacer">
<h3>Resource Pack to Clean:</h3>
<folder-selector v-model="folder" placeholder="Select Resource Pack">the resource pack to clean the contents of</folder-selector>
<folder-selector v-model="folder" placeholder="Resource Pack">resource pack to clean the contents of</folder-selector>
<version-selector v-model="version" />
<checkbox-row v-model="objects">Also clean objects (sounds, languages, panorama, etc…)</checkbox-row>
</div>
Expand Down Expand Up @@ -2575,7 +2671,7 @@
template: `
<div v-if="!status.processing && !status.finished">
<h3>Resource Pack to Strip:</h3>
<folder-selector v-model="folder" placeholder="Select Resource Pack">the resource pack to strip the language files of</folder-selector>
<folder-selector v-model="folder" placeholder="Resource Pack">resource pack to strip the language files of</folder-selector>
<version-selector v-model="version" />
<radio-row v-model="mode" :options="[['default', 'Strip default language file (en_us)'], ['all', 'Strip all language files']]"></radio-row>
<button :disabled="!folder" @click="execute">Strip</button>
Expand Down Expand Up @@ -2742,17 +2838,12 @@
this.status.processing = false
}
},
styles: `
.component-versionSelector {
align-self: flex-start;
}
`,
template: `
<div v-if="!status.processing && !status.finished">
<h3>Input Folder:</h3>
<folder-selector v-model="inputFolder" placeholder="Select Input Folder">the folder containing bbmodels</folder-selector>
<folder-selector v-model="inputFolder" placeholder="Input Folder">folder containing bbmodels</folder-selector>
<h3>Output Folder:</h3>
<folder-selector v-model="outputFolder" placeholder="Select Output Folder">the folder to export the bbmodels to</folder-selector>
<folder-selector v-model="outputFolder" placeholder="Output Folder">folder to export the bbmodels to</folder-selector>
<select-row v-model="format" :options="formats">Output format</select-row>
<checkbox-row v-model="subfolders">Export each model to its own subfolder</checkbox-row>
<checkbox-row v-model="textures">Export textures</checkbox-row>
Expand All @@ -2767,6 +2858,91 @@
</div>
`
}
},
missingTextures: {
name: "Missing Textures",
icon: "find_in_page",
tagline: "List the textures that are missing from a resource pack.",
description: "Missing Textures is a tool that will check what textures you have in a resource pack and tell you which ones the resource pack is missing.",
component: {
data: {
folder: "",
outputLog,
done: 0,
total: null,
cancelled: false,
version: "",
ignoreList: ["clouds", "color_palettes", "colormap", "debug", "font", "gui/title", "presets", "realms", "textures/effect"],
exportLog: false
},
methods: {
async execute() {
outputLog.length = 0
this.status.finished = false
this.status.processing = true
this.done = 0
this.total = null
this.cancelled = false

if (!await exists(this.folder)) {
this.status.finished = true
this.status.processing = false
this.total = 0
output.error(`The folder \`${formatPath(this.folder)}\` was not found`)
return
}

const jar = await getVersionJar(this.version)

const toCheck = Object.keys(jar.files).filter(e => e.endsWith(".png") && !this.ignoreList.some(item => e.toLowerCase().includes(item)))

this.total = toCheck.length

const files = (await Array.fromAsync(getFiles(this.folder))).map(e => formatPath(e.slice(this.folder.length + 1)))

const missing = []
for (const file of toCheck) {
if (!files.includes(file)) {
output.log(`Missing \`${file}\``)
missing.push(file)
}
this.done++
}

const str = `Missing ${missing.length} of ${this.total} textures (${!this.total ? 0 : ((missing.length / this.total) * 100).toFixed(2).replace(/\.?0+$/, "")}%)`

if (this.exportLog) {
const name = path.join(this.folder, `missing_textures_${getDate()}.txt`)
await fs.promises.writeFile(name, `${header}${str}\n\nMissing Textures\n――――――――――――――――\n${formatFilePaths(missing)}`, "utf-8")
output.log(`Saved missing textures list to \`${name}\``)
}

output.info(`Finished\n${str}`)
this.status.finished = true
this.status.processing = false
}
},
template: `
<div v-if="!status.processing && !status.finished">
<div class="row">
<div class="col spacer">
<h3>Resource Pack:</h3>
<folder-selector v-model="folder" placeholder="Resource Pack">resource pack to list missing textures of</folder-selector>
<version-selector v-model="version" />
<checkbox-row v-model="exportLog">Export missing texture list</checkbox-row>
</div>
<ignore-list v-model="ignoreList" />
</div>
<button :disabled="!folder" @click="execute">Find Missing Textures</button>
</div>
<div v-else>
<progress-bar :done="done" :total="total" />
<output-log v-model="outputLog" />
<button v-if="status.processing" @click="cancelled = true">Cancel</button>
<button v-else @click="status.finished = false">Done</button>
</div>
`
}
}
}

Expand Down

0 comments on commit 7f21e78

Please sign in to comment.