Files
NoN-Site/nonpacks/static/vendor/blockbench/plugins/packs_bootstrap.js
T
2026-08-04 21:47:37 -05:00

137 lines
5.7 KiB
JavaScript

// packs_bootstrap.js — injected into the vendored Blockbench app (same origin).
// Loads the GeckoLib + Multi Actor Animator plugins and bridges model/texture
// loading from the parent page via postMessage.
(function () {
if (window.PacksBB) return;
function waitFor(fn, timeout) {
return new Promise((resolve) => {
const start = Date.now();
const iv = setInterval(() => {
if (fn()) { clearInterval(iv); resolve(true); }
else if (Date.now() - start > timeout) { clearInterval(iv); resolve(false); }
}, 100);
});
}
function notify(type, payload) {
if (window.parent && window.parent !== window) {
window.parent.postMessage(Object.assign({ type: 'packs-' + type }, payload || {}), window.location.origin);
}
}
async function loadPlugin(path, id) {
if (Plugins.registered[id]) {
// already registered (persisted from a previous session) — ensure active
const existing = Plugins.registered[id];
if (existing.installed && !existing.disabled && typeof existing.onload === 'function' && existing.source === 'file') {
return existing;
}
}
const code = await (await fetch(path, { cache: 'no-store' })).text();
if (!code || code.length < 20) throw new Error('empty plugin: ' + path);
const inst = new Plugin();
await inst.loadFromFile({ path: path, content: code }, true);
return inst;
}
function filenameOf(resource) {
const s = String(resource || '').trim();
const parts = s.split('/');
return parts[parts.length - 1];
}
// Set the Multi Actor Animator per-bone texture overrides from the geo's
// afw_bone_textures. Only the feature bones get special treatment; the
// vanilla skin stays the model's base (just loaded, Blockbench default).
function applyAfwTextureOverrides(geo) {
const afw = (geo && geo['afw_bone_textures']) || {};
const overrides = {};
for (const bone in afw) {
const texName = filenameOf(afw[bone]);
if (texName) overrides[bone] = { name: bone, texture: texName };
}
if (!Object.keys(overrides).length || !Project) return;
try {
Project['multiactor_bone_texture_overrides'] = JSON.stringify(overrides);
} catch (e) {
console.error('packs override set failed', e);
}
}
async function openModel(msg) {
await waitFor(() => typeof loadModelFile === 'function', 15000);
const name = msg.name || 'model';
if (msg.modelFile) {
// Standalone Blockbench model (bbmodel) with baked textures.
// glTF/GLB is not sent here — those are marked "Incompatible" by the
// parent page (import needs the desktop-only glTF Importer plugin).
if (msg.modelFile.content) {
await loadModelFile({ content: msg.modelFile.content, name: name, path: name });
}
await new Promise((r) => setTimeout(r, 400));
notify('model-open', { name: name });
return;
}
await loadModelFile({ content: JSON.stringify(msg.geo), name: name, path: name });
// Give the format importer a beat, then set the feature-bone overrides
// (before adding textures, so the plugin's add_texture listener applies
// them) and load the base texture first, then the feature overlays.
await new Promise((r) => setTimeout(r, 800));
applyAfwTextureOverrides(msg.geo);
const afwFiles = new Set();
for (const k in ((msg.geo && msg.geo['afw_bone_textures']) || {})) {
afwFiles.add(filenameOf(msg.geo['afw_bone_textures'][k]));
}
const baseTextures = (msg.textures || []).filter((t) => !afwFiles.has(t.name));
const featureTextures = (msg.textures || []).filter((t) => afwFiles.has(t.name));
for (const t of baseTextures.concat(featureTextures)) {
try {
const tex = new Texture({ name: t.name || 'texture', folder: 'entity' });
if (t.dataUrl) {
await tex.fromDataURL(t.dataUrl);
} else if (t.url) {
await tex.fromPath(t.url);
}
tex.add(false);
} catch (e) {
console.error('packs texture load failed', t && t.name, e);
}
}
await new Promise((r) => setTimeout(r, 400));
notify('model-open', { name: name });
}
window.addEventListener('message', (e) => {
if (!e.data || e.data.type !== 'packs-open-model') return;
openModel(e.data).then(
() => {},
(err) => {
console.error('packs-open-model failed', err);
notify('model-error', { name: e.data.name || '', error: String(err) });
}
);
});
async function boot() {
window.confirm = () => true;
const ok = await waitFor(() => window.Blockbench && window.Blockbench.setup_successful && window.Plugin && typeof loadModelFile === 'function', 30000);
if (!ok) { notify('error', { error: 'Blockbench did not initialize' }); return; }
try {
await loadPlugin('plugins/geckolib/geckolib.js', 'geckolib');
await loadPlugin('plugins/MultiactorEditor/MultiactorEditor.js', 'MultiactorEditor');
} catch (e) {
console.error('packs plugin load failed', e);
notify('plugin-error', { error: String(e) });
}
window.PacksBB = { ready: true, plugins: ['geckolib', 'MultiactorEditor'] };
notify('bb-ready', {});
}
boot();
})();