forked from phenom/healer-man
Release Healer Man 0.1.14
This commit is contained in:
1 parent
67d7a8ebe7
commit
7b41b102fa
147 files changed
+169892
-1342
No files matched your search
@@ -1,6 +1,7 @@
|
||||
import { copyFile, mkdir, rm, writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import {
|
||||
BASIC_HUMAN_TEXTURE_PATHS,
|
||||
PROJECT_ROOT,
|
||||
createContentManifest,
|
||||
scanContentSources,
|
||||
@@ -12,8 +13,21 @@ if (path.dirname(outputDirectory) !== PROJECT_ROOT || path.basename(outputDirect
|
||||
}
|
||||
|
||||
const sources = [
|
||||
{ directory: path.join(PROJECT_ROOT, "public", "basis"), logicalPrefix: "basis" },
|
||||
{ directory: path.join(PROJECT_ROOT, "public", "assets", "ui"), logicalPrefix: "assets/ui" },
|
||||
{ directory: path.join(PROJECT_ROOT, "public", "assets", "equipment"), logicalPrefix: "assets/equipment" },
|
||||
{
|
||||
directory: path.join(PROJECT_ROOT, "public", "assets", "characters", "wow335a-v1", "manifest.json"),
|
||||
logicalPrefix: "assets/characters/wow335a-v1/manifest.json",
|
||||
},
|
||||
{
|
||||
directory: path.join(PROJECT_ROOT, "public", "assets", "characters", "wow335a-v1", "human"),
|
||||
logicalPrefix: "assets/characters/wow335a-v1/human",
|
||||
},
|
||||
...BASIC_HUMAN_TEXTURE_PATHS.map((logicalPath) => ({
|
||||
directory: path.join(PROJECT_ROOT, "public", ...logicalPath.split("/")),
|
||||
logicalPrefix: logicalPath,
|
||||
})),
|
||||
{
|
||||
directory: path.join(PROJECT_ROOT, "public", "assets", "creatures", "wailing-caverns"),
|
||||
logicalPrefix: "assets/creatures/wailing-caverns",
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import { copyFile, mkdir, rm, writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import {
|
||||
BASIC_HUMAN_TEXTURE_PATHS,
|
||||
PROJECT_ROOT,
|
||||
createContentManifest,
|
||||
scanContentSources,
|
||||
} from "./publish-content.mjs";
|
||||
|
||||
const outputDirectory = path.join(PROJECT_ROOT, ".web-public");
|
||||
if (path.dirname(outputDirectory) !== PROJECT_ROOT || path.basename(outputDirectory) !== ".web-public") {
|
||||
throw new Error(`Refusing to replace unexpected browser staging directory: ${outputDirectory}`);
|
||||
}
|
||||
|
||||
const sources = [
|
||||
{ directory: path.join(PROJECT_ROOT, "public", "basis"), logicalPrefix: "basis" },
|
||||
{ directory: path.join(PROJECT_ROOT, "public", "data"), logicalPrefix: "data" },
|
||||
{ directory: path.join(PROJECT_ROOT, "public", "assets", "ui"), logicalPrefix: "assets/ui" },
|
||||
{ directory: path.join(PROJECT_ROOT, "public", "assets", "equipment"), logicalPrefix: "assets/equipment" },
|
||||
{
|
||||
directory: path.join(PROJECT_ROOT, "public", "assets", "characters", "wow335a-v1", "manifest.json"),
|
||||
logicalPrefix: "assets/characters/wow335a-v1/manifest.json",
|
||||
},
|
||||
{
|
||||
directory: path.join(PROJECT_ROOT, "public", "assets", "characters", "wow335a-v1", "human"),
|
||||
logicalPrefix: "assets/characters/wow335a-v1/human",
|
||||
},
|
||||
...BASIC_HUMAN_TEXTURE_PATHS.map((logicalPath) => ({
|
||||
directory: path.join(PROJECT_ROOT, "public", ...logicalPath.split("/")),
|
||||
logicalPrefix: logicalPath,
|
||||
})),
|
||||
{
|
||||
directory: path.join(PROJECT_ROOT, "public", "assets", "creatures", "wailing-caverns"),
|
||||
logicalPrefix: "assets/creatures/wailing-caverns",
|
||||
},
|
||||
{
|
||||
directory: path.join(PROJECT_ROOT, "src", "assets", "game", "dungeons", "wailing-caverns"),
|
||||
logicalPrefix: "assets/game/dungeons/wailing-caverns",
|
||||
},
|
||||
];
|
||||
|
||||
await rm(outputDirectory, { recursive: true, force: true });
|
||||
await mkdir(outputDirectory, { recursive: true });
|
||||
|
||||
const files = await scanContentSources(sources);
|
||||
for (const file of files) {
|
||||
const destination = path.join(outputDirectory, ...file.logicalPath.split("/"));
|
||||
await mkdir(path.dirname(destination), { recursive: true });
|
||||
await copyFile(file.sourcePath, destination);
|
||||
}
|
||||
|
||||
await copyFile(
|
||||
path.join(PROJECT_ROOT, "public", "content-cache-sw.js"),
|
||||
path.join(outputDirectory, "content-cache-sw.js"),
|
||||
);
|
||||
|
||||
const generated = createContentManifest(files, { baseUrl: "/" });
|
||||
const bootstrapManifest = {
|
||||
...generated,
|
||||
files: Object.fromEntries(Object.entries(generated.files).map(([logicalPath, entry]) => [
|
||||
logicalPath,
|
||||
{ ...entry, url: logicalPath },
|
||||
])),
|
||||
};
|
||||
const manifestPath = path.join(outputDirectory, "content", "bootstrap-manifest.json");
|
||||
await mkdir(path.dirname(manifestPath), { recursive: true });
|
||||
await writeFile(manifestPath, `${JSON.stringify(bootstrapManifest, null, 2)}\n`, "utf8");
|
||||
|
||||
const bytes = files.reduce((total, file) => total + file.bytes, 0);
|
||||
console.log(`Prepared browser bootstrap content: ${files.length} files (${bytes} bytes)`);
|
||||
console.log(`Bootstrap version: ${bootstrapManifest.version}`);
|
||||
@@ -18,14 +18,34 @@ export const DEFAULT_MINIMUM_APP_VERSION = "0.1.0";
|
||||
export const BUNDLED_ANDROID_PACKS = Object.freeze([
|
||||
"core:ui",
|
||||
"core:equipment",
|
||||
"core:character-basic",
|
||||
"core:runtime",
|
||||
"dungeon:wailing-caverns",
|
||||
]);
|
||||
|
||||
const RUNTIME_EXTENSIONS = new Set([
|
||||
".avif", ".glb", ".json", ".ktx2", ".mp3", ".ogg", ".png",
|
||||
".svg", ".wav", ".webm", ".webp",
|
||||
".avif", ".glb", ".js", ".json", ".ktx2", ".mp3", ".ogg", ".png",
|
||||
".svg", ".wasm", ".wav", ".webm", ".webp",
|
||||
]);
|
||||
|
||||
export const BASIC_HUMAN_TEXTURE_PATHS = Object.freeze([
|
||||
"assets/characters/wow335a-v1/textures/character/human/faciallowerhair00_00.webp",
|
||||
"assets/characters/wow335a-v1/textures/character/human/facialupperhair00_00.webp",
|
||||
"assets/characters/wow335a-v1/textures/character/human/female/humanfemalefacelower00_00.webp",
|
||||
"assets/characters/wow335a-v1/textures/character/human/female/humanfemalefaceupper00_00.webp",
|
||||
"assets/characters/wow335a-v1/textures/character/human/female/humanfemalenakedpelvisskin00_00.webp",
|
||||
"assets/characters/wow335a-v1/textures/character/human/female/humanfemalenakedtorsoskin00_00.webp",
|
||||
"assets/characters/wow335a-v1/textures/character/human/female/humanfemaleskin00_00.webp",
|
||||
"assets/characters/wow335a-v1/textures/character/human/hair00_00.webp",
|
||||
"assets/characters/wow335a-v1/textures/character/human/male/humanmalefacelower00_00.webp",
|
||||
"assets/characters/wow335a-v1/textures/character/human/male/humanmalefaceupper00_00.webp",
|
||||
"assets/characters/wow335a-v1/textures/character/human/male/humanmalenakedpelvisskin00_00.webp",
|
||||
"assets/characters/wow335a-v1/textures/character/human/male/humanmaleskin00_00.webp",
|
||||
"assets/characters/wow335a-v1/textures/character/human/scalplowerhair00_00.webp",
|
||||
"assets/characters/wow335a-v1/textures/character/human/scalpupperhair00_00.webp",
|
||||
]);
|
||||
const BASIC_HUMAN_TEXTURE_PATH_SET = new Set(BASIC_HUMAN_TEXTURE_PATHS);
|
||||
|
||||
export const DEFAULT_CONTENT_SOURCES = Object.freeze([
|
||||
{
|
||||
directory: path.join(PROJECT_ROOT, "public", "assets"),
|
||||
@@ -49,18 +69,33 @@ function packLabel(packId) {
|
||||
const [category, id = category] = packId.split(":", 2);
|
||||
const name = id.split("-").map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`).join(" ");
|
||||
if (category === "dungeon") return name;
|
||||
if (category === "characters") return `${name} Characters`;
|
||||
if (packId === "characters:wow335a-v1") return "Extra Wrath Character Models & Skins";
|
||||
if (packId === "characters:ascension-v1") return "Ascension Character Models & Skins";
|
||||
if (packId === "characters:runewaker-v1") return "RuneWaker Character Models & Skins";
|
||||
if (category === "characters") return `${name} Character Models & Skins`;
|
||||
if (category === "manastorm") return `${name} Manastorm`;
|
||||
if (packId === "shared:creatures") return "Shared Creatures";
|
||||
if (packId === "core:ui") return "Interface";
|
||||
if (packId === "core:equipment") return "Starter Equipment";
|
||||
if (packId === "core:character-basic") return "Basic Human Character Model";
|
||||
if (packId === "core:runtime") return "Game Runtime";
|
||||
if (packId === "core:combat-media") return "Original Spell Effects & Sounds";
|
||||
return name;
|
||||
}
|
||||
|
||||
export function contentPackForPath(logicalPath) {
|
||||
const segments = normalizedRelativePath(logicalPath).split("/");
|
||||
const normalizedPath = normalizedRelativePath(logicalPath);
|
||||
const segments = normalizedPath.split("/");
|
||||
if (segments[0] === "basis" || segments[0] === "data") return "core:runtime";
|
||||
if (segments[0] !== "assets") return "core:misc";
|
||||
if (segments[1] === "characters" && segments[2]) return `characters:${segments[2]}`;
|
||||
if (segments[1] === "characters" && segments[2]) {
|
||||
const basicWrathHuman = segments[2] === "wow335a-v1" && (
|
||||
segments[3] === "manifest.json"
|
||||
|| segments[3] === "human"
|
||||
|| BASIC_HUMAN_TEXTURE_PATH_SET.has(normalizedPath)
|
||||
);
|
||||
return basicWrathHuman ? "core:character-basic" : `characters:${segments[2]}`;
|
||||
}
|
||||
if (segments[1] === "creatures" && segments[2]) {
|
||||
return segments[2] === "shared" ? "shared:creatures" : `dungeon:${segments[2]}`;
|
||||
}
|
||||
@@ -73,12 +108,17 @@ export function contentPackForPath(logicalPath) {
|
||||
if (segments[1] === "game" && segments[2] === "manastorm" && segments[3]) {
|
||||
return `manastorm:${segments[3]}`;
|
||||
}
|
||||
if (segments[1] === "game" && segments[2] === "combat-effects") return "core:combat-media";
|
||||
if (segments[1] === "ui") return "core:ui";
|
||||
if (segments[1] === "equipment") return "core:equipment";
|
||||
return "core:misc";
|
||||
}
|
||||
|
||||
async function walkFiles(directory) {
|
||||
const rootStats = await stat(directory);
|
||||
if (rootStats.isFile()) {
|
||||
return RUNTIME_EXTENSIONS.has(path.extname(directory).toLowerCase()) ? [directory] : [];
|
||||
}
|
||||
const files = [];
|
||||
async function visit(current) {
|
||||
const entries = await readdir(current, { withFileTypes: true });
|
||||
@@ -111,7 +151,9 @@ export async function scanContentSources(sources = DEFAULT_CONTENT_SOURCES) {
|
||||
const files = await walkFiles(sourceDirectory);
|
||||
for (const sourcePath of files) {
|
||||
const relative = normalizedRelativePath(path.relative(sourceDirectory, sourcePath));
|
||||
const logicalPath = normalizedRelativePath(`${source.logicalPrefix}/${relative}`);
|
||||
const logicalPath = normalizedRelativePath(relative
|
||||
? `${source.logicalPrefix}/${relative}`
|
||||
: source.logicalPrefix);
|
||||
const fileStats = await stat(sourcePath);
|
||||
const sha256 = await sha256File(sourcePath);
|
||||
const prior = discovered.get(logicalPath);
|
||||
@@ -170,11 +212,17 @@ export function createContentManifest(files, options = {}) {
|
||||
|
||||
if (packs.has("shared:creatures")) {
|
||||
for (const pack of packs.values()) {
|
||||
if (pack.id.startsWith("dungeon:") || pack.id.startsWith("manastorm:")) {
|
||||
if ((pack.id.startsWith("dungeon:") && pack.id !== "dungeon:wailing-caverns")
|
||||
|| pack.id.startsWith("manastorm:")) {
|
||||
pack.dependencies = ["shared:creatures"];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (packs.has("core:character-basic")) {
|
||||
for (const pack of packs.values()) {
|
||||
if (pack.id.startsWith("characters:")) pack.dependencies = ["core:character-basic"];
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
schemaVersion: CONTENT_SCHEMA_VERSION,
|
||||
|
||||
@@ -4,6 +4,7 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
import test from "node:test";
|
||||
import {
|
||||
BUNDLED_ANDROID_PACKS,
|
||||
contentPackForPath,
|
||||
createContentManifest,
|
||||
publishContent,
|
||||
@@ -13,8 +14,19 @@ import {
|
||||
test("assigns stable player-facing packs from logical asset paths", () => {
|
||||
assert.equal(contentPackForPath("assets/game/dungeons/wailing-caverns/visual.glb"), "dungeon:wailing-caverns");
|
||||
assert.equal(contentPackForPath("assets/creatures/wailing-caverns/mutanus.glb"), "dungeon:wailing-caverns");
|
||||
assert.equal(contentPackForPath("assets/characters/wow335a-v1/manifest.json"), "characters:wow335a-v1");
|
||||
assert.equal(contentPackForPath("assets/characters/wow335a-v1/manifest.json"), "core:character-basic");
|
||||
assert.equal(contentPackForPath("assets/characters/wow335a-v1/human/male/rig.glb"), "core:character-basic");
|
||||
assert.equal(contentPackForPath("assets/characters/wow335a-v1/textures/character/human/male/humanmaleskin00_00.webp"), "core:character-basic");
|
||||
assert.equal(contentPackForPath("assets/characters/wow335a-v1/textures/character/human/male/humanmaleskin00_01.webp"), "characters:wow335a-v1");
|
||||
assert.equal(contentPackForPath("assets/characters/wow335a-v1/orc/male/rig.glb"), "characters:wow335a-v1");
|
||||
assert.equal(contentPackForPath("assets/game/manastorm/stage-1/visual.glb"), "manastorm:stage-1");
|
||||
assert.equal(contentPackForPath("assets/game/combat-effects/audio/smite.wav"), "core:combat-media");
|
||||
assert.equal(
|
||||
contentPackForPath("assets/game/combat-effects/audio/manastorm/Manastorm_BossKill_GenericPortalsReady.wav"),
|
||||
"core:combat-media",
|
||||
);
|
||||
assert.equal(BUNDLED_ANDROID_PACKS.includes("core:combat-media"), false);
|
||||
assert.equal(contentPackForPath("basis/basis_transcoder.wasm"), "core:runtime");
|
||||
});
|
||||
|
||||
test("publishes objects before a deterministic latest manifest", async () => {
|
||||
|
||||
Reference in new issue
Block a user