resource extraction and reading
This commit is contained in:
@@ -5,6 +5,7 @@ import { ensureDir, ensureFile, exists } from "@std/fs";
|
||||
import { Router } from "./router.ts";
|
||||
import { getPackVersion } from "./util/packVersion.ts";
|
||||
import { createTagRoutes } from "./tags/routes.ts";
|
||||
import { createResourcesRoutes } from "./resources/routes.ts";
|
||||
|
||||
const installPath = Deno.env.get("BMP_INSTALL_DIR") || "./";
|
||||
|
||||
@@ -223,6 +224,7 @@ router.route("/api/versions")
|
||||
});
|
||||
|
||||
createTagRoutes(router);
|
||||
createResourcesRoutes(router);
|
||||
|
||||
sockpuppet.addHandler((req: Request) => {
|
||||
if (new URL(req.url).pathname.startsWith("/api")) return;
|
||||
|
80
server/resources/readers.ts
Normal file
80
server/resources/readers.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { encodeBase64 } from "@std/encoding/base64";
|
||||
import { readDirFiles } from "../util/readDir.ts";
|
||||
|
||||
interface BlockItem {
|
||||
name: string;
|
||||
resourceLocation: string;
|
||||
images: string[];
|
||||
}
|
||||
|
||||
export const readBlocks = async (path: string) => {
|
||||
const blocks: BlockItem[] =
|
||||
(await readDirFiles(path + "/assets/minecraft/blockstates"))
|
||||
.map((b) => ({
|
||||
name: b.replace(".json", ""),
|
||||
resourceLocation: "minecraft:" + b.replace(".json", ""),
|
||||
images: [],
|
||||
}));
|
||||
|
||||
const texPath = path + "/assets/minecraft/textures/block";
|
||||
for await (const image of Deno.readDir(texPath)) {
|
||||
for (const block of blocks) {
|
||||
if (image.name.startsWith(block.name)) {
|
||||
const data = await Deno.readFile(texPath + "/" + image.name);
|
||||
block.images.push("image/png;base64," + encodeBase64(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
return blocks;
|
||||
};
|
||||
|
||||
export const readItems = async (path: string) => {
|
||||
const items: BlockItem[] =
|
||||
(await readDirFiles(path + "/assets/minecraft/models/item")).map((i) => ({
|
||||
name: i.replace(".json", ""),
|
||||
resourceLocation: "minecraft:" + i.replace(".json", ""),
|
||||
images: [],
|
||||
})).slice(0, 10);
|
||||
|
||||
for (const item of items) {
|
||||
const data = await Deno.readFile(
|
||||
path + "/assets/minecraft/models/item/" + item.name + ".json",
|
||||
);
|
||||
const json = JSON.parse(new TextDecoder().decode(data));
|
||||
const texDir = path + "/assets/minecraft/textures/";
|
||||
if (json.textures) {
|
||||
const texLoc = json.textures.layer0;
|
||||
if (texLoc) {
|
||||
const data = await Deno.readFile(
|
||||
texDir + texLoc.replace("minecraft:", "") + ".png",
|
||||
);
|
||||
item.images.push("image/png;base64," + encodeBase64(data));
|
||||
}
|
||||
} else if (json.parent) {
|
||||
const parent = await Deno.readFile(
|
||||
path + "/assets/minecraft/models/" +
|
||||
json.parent.replace("minecraft:", "") + ".json",
|
||||
);
|
||||
const parentJson = JSON.parse(new TextDecoder().decode(parent));
|
||||
if (parentJson.textures) {
|
||||
let texLoc = parentJson.textures.all;
|
||||
if (!texLoc) texLoc = parentJson.textures.side;
|
||||
if (!texLoc) texLoc = parentJson.textures.top;
|
||||
if (!texLoc) texLoc = parentJson.textures.bottom;
|
||||
if (texLoc) {
|
||||
const data = await Deno.readFile(
|
||||
texDir + texLoc.replace("minecraft:", "") + ".png",
|
||||
);
|
||||
item.images.push("image/png;base64," + encodeBase64(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
};
|
||||
|
||||
if (import.meta.main) {
|
||||
const path = "./resources/1.21.1";
|
||||
console.log(await readItems(path));
|
||||
}
|
37
server/resources/routes.ts
Normal file
37
server/resources/routes.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import type { Router } from "../router.ts";
|
||||
import { readDirDirs } from "../util/readDir.ts";
|
||||
import { versionCompat } from "../util/versionCompat.ts";
|
||||
import { readBlocks } from "./readers.ts";
|
||||
|
||||
export const createResourcesRoutes = (router: Router) => {
|
||||
router.route("/api/resources/:path*")
|
||||
.get(async (req, ctx) => {
|
||||
const path = ctx.params.path;
|
||||
if (!path) {
|
||||
return new Response("no path provided", { status: 400 });
|
||||
}
|
||||
const format = ctx.url.searchParams.get("format");
|
||||
if (!format) {
|
||||
return new Response("no format provided", { status: 400 });
|
||||
}
|
||||
const packVersion = await Deno.readTextFile(
|
||||
"./pack_versions/" + format + ".json",
|
||||
);
|
||||
const packVersionJson = JSON.parse(packVersion);
|
||||
const mcVersion = packVersionJson.mcVersion;
|
||||
const resourceVersions = await readDirDirs("./resources");
|
||||
for (const resourceVersion of resourceVersions) {
|
||||
if (versionCompat(resourceVersion, mcVersion)) {
|
||||
const resourcePath = "./resources/" + resourceVersion;
|
||||
const splitPath = path.split("/");
|
||||
switch (splitPath[0]) {
|
||||
case "blocks": {
|
||||
return new Response(
|
||||
JSON.stringify(await readBlocks(resourcePath)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
58
server/resources/unzip.ts
Normal file
58
server/resources/unzip.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { BearMetalStore } from "@bearmetal/store";
|
||||
import { ZipReader } from "@zip.js/zip.js";
|
||||
import { ensureFile } from "@std/fs";
|
||||
|
||||
export async function unzipResources(mcVersion?: string) {
|
||||
using store = new BearMetalStore();
|
||||
mcVersion = mcVersion || await currentVersion(store);
|
||||
|
||||
console.log("mcVersion", mcVersion);
|
||||
|
||||
if (!mcVersion) return;
|
||||
|
||||
const blob = await Deno.open(
|
||||
store.get("mcPath") + "/versions/" + mcVersion + "/" + mcVersion + ".jar",
|
||||
);
|
||||
|
||||
const zip = new ZipReader(blob);
|
||||
|
||||
for (const entry of await zip.getEntries()) {
|
||||
if (
|
||||
entry.filename.startsWith("assets/") || entry.filename.startsWith("data/")
|
||||
) {
|
||||
// console.log("entry", entry);
|
||||
await ensureFile(`./resources/${mcVersion}/${entry.filename}`);
|
||||
const writer = await Deno.open(
|
||||
`./resources/${mcVersion}/${entry.filename}`,
|
||||
{ write: true },
|
||||
);
|
||||
await entry.getData?.(writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function currentVersion(store: BearMetalStore) {
|
||||
const mcPath = store.get("mcPath");
|
||||
if (!mcPath) return;
|
||||
|
||||
const versions = Array.from(Deno.readDirSync(mcPath + "/versions")).filter(
|
||||
(d) => d.isDirectory,
|
||||
).map((d) => d.name).sort();
|
||||
let version = versions.pop();
|
||||
let found = false;
|
||||
versionC:
|
||||
while (!found) {
|
||||
for await (const file of Deno.readDir(mcPath + "/versions/" + version)) {
|
||||
if (file.name.endsWith(".jar")) {
|
||||
found = true;
|
||||
break versionC;
|
||||
}
|
||||
}
|
||||
version = versions.pop();
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
if (import.meta.main) {
|
||||
unzipResources();
|
||||
}
|
@@ -70,12 +70,13 @@ export const createTagRoutes = (router: Router) => {
|
||||
const tagDir = await getTagDir(store, ctx.params.namespace, version);
|
||||
|
||||
const tag = ctx.params.tag;
|
||||
if (!tag) {
|
||||
const type = ctx.params.type;
|
||||
if (!tag || !type) {
|
||||
return new Response("no tag name provided", { status: 400 });
|
||||
}
|
||||
|
||||
try {
|
||||
const tagFile = Deno.readTextFileSync(tagDir + "/" + tag + ".json");
|
||||
const tagFile = Deno.readTextFileSync(`${tagDir}/${type}/${tag}.json`);
|
||||
return new Response(tagFile, { status: 200 });
|
||||
} catch {
|
||||
return new Response("no tag found", { status: 404 });
|
||||
|
20
server/util/readDir.ts
Normal file
20
server/util/readDir.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export const readDirFiles = async (path: string) => {
|
||||
return readDirFiltered(path, (file) => file.isFile);
|
||||
};
|
||||
|
||||
export const readDirDirs = async (path: string) => {
|
||||
return readDirFiltered(path, (file) => file.isDirectory);
|
||||
};
|
||||
|
||||
export const readDirFiltered = async (
|
||||
path: string,
|
||||
filter: (file: Deno.DirEntry) => boolean,
|
||||
) => {
|
||||
const files: string[] = [];
|
||||
for await (const file of Deno.readDir(path)) {
|
||||
if (filter(file)) {
|
||||
files.push(file.name);
|
||||
}
|
||||
}
|
||||
return files;
|
||||
};
|
14
server/util/versionCompat.ts
Normal file
14
server/util/versionCompat.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export const versionCompat = (version: string, targetVersion: string) => {
|
||||
if (targetVersion === "*") return true;
|
||||
if (targetVersion === version) return true;
|
||||
if (targetVersion.startsWith("^")) {
|
||||
const versionSplit = version.split(".");
|
||||
const targetVersionSplit = targetVersion.split(".");
|
||||
for (let i = 0; i < versionSplit.length; i++) {
|
||||
if (versionSplit[i] > targetVersionSplit[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
Reference in New Issue
Block a user