full suport for models and hotloading Plugins
This commit is contained in:
@@ -16,6 +16,71 @@ PROBLEM_TAGS = {'broken', 'bugged', 'borked'}
|
||||
MODEL_FILE_EXTENSIONS = ('.bbmodel', '.geo.json', '.jem', '.gltf', '.glb')
|
||||
|
||||
|
||||
def _in_plugins_dir(name):
|
||||
"""True when a member lives inside a plugins/ folder (any nesting)."""
|
||||
parts = name.split('/')
|
||||
return 'plugins' in parts[:-1]
|
||||
|
||||
|
||||
def read_plugins_manifest(path):
|
||||
"""Scan the archive's plugins/ folder for Blockbench plugins.
|
||||
|
||||
A ``.js`` file under a ``plugins/`` directory that registers a Blockbench
|
||||
plugin (contains ``Plugin.register(`` or ``BBPlugin.register(``) is listed.
|
||||
``plugins/autoload.txt`` holds one plugin name per line; matching plugins
|
||||
are marked ``autoload`` so the viewer can hotload them when a model opens.
|
||||
Returns None when the pack has no plugins.
|
||||
"""
|
||||
zf = _open_zip(path)
|
||||
if zf is None:
|
||||
return None
|
||||
try:
|
||||
names = set(zf.namelist())
|
||||
autoload = set()
|
||||
for n in names:
|
||||
if _in_plugins_dir(n) and n.endswith('autoload.txt'):
|
||||
try:
|
||||
raw = zf.read(n).decode('utf-8', 'replace')
|
||||
except KeyError:
|
||||
continue
|
||||
for line in raw.splitlines():
|
||||
line = line.strip()
|
||||
if line and not line.startswith('#'):
|
||||
autoload.add(line)
|
||||
|
||||
plugins = []
|
||||
for n in sorted(names):
|
||||
if not _in_plugins_dir(n) or not n.endswith('.js'):
|
||||
continue
|
||||
try:
|
||||
content = zf.read(n)
|
||||
except KeyError:
|
||||
continue
|
||||
if len(content) > 8 * 1024 * 1024:
|
||||
continue
|
||||
text = content.decode('utf-8', 'replace')
|
||||
if 'Plugin.register(' not in text and 'BBPlugin.register(' not in text:
|
||||
continue
|
||||
base = n.rsplit('/', 1)[-1]
|
||||
stem = base[:-3] if base.lower().endswith('.js') else base
|
||||
plugins.append({
|
||||
'name': base,
|
||||
'member': n,
|
||||
'size': len(content),
|
||||
'autoload': base in autoload or stem in autoload,
|
||||
})
|
||||
|
||||
if not plugins:
|
||||
return None
|
||||
return {
|
||||
'plugins': plugins,
|
||||
'has_plugins': True,
|
||||
'autoload_count': sum(1 for p in plugins if p['autoload']),
|
||||
}
|
||||
finally:
|
||||
zf.close()
|
||||
|
||||
|
||||
def _decode(data):
|
||||
"""Decode JSON text, tolerating a UTF-8 BOM (common in hand-edited packs)."""
|
||||
return json.loads(data.decode('utf-8-sig', 'replace'))
|
||||
@@ -668,7 +733,9 @@ def parse_mods_manifest(path, filename=''):
|
||||
parts = name.split('/')
|
||||
if 'mods' not in parts or parts.index('mods') >= len(parts) - 1:
|
||||
continue
|
||||
if 'minecraft' in parts or '.minecraft' in parts:
|
||||
# Full instance = .minecraft/mods or a nested <instance>/minecraft/mods;
|
||||
# a root-level minecraft/mods folder is the classic folder layout.
|
||||
if '.minecraft' in parts or (('minecraft' in parts) and parts.index('minecraft') > 0):
|
||||
is_instance = True
|
||||
fname = parts[-1]
|
||||
if fname and fname not in seen:
|
||||
|
||||
Reference in New Issue
Block a user