From 8a6833def042924e05ed1bac43dbfccb6f10397f Mon Sep 17 00:00:00 2001 From: JakeBreath Date: Tue, 4 Aug 2026 19:05:38 -0500 Subject: [PATCH] finishing with Phase 2 --- .gitignore | 1 + .../commands/refresh_vanilla_index.py | 2 +- nonpacks/library/urls.py | 1 - nonpacks/library/vanilla_textures.py | 20 ++++++++++++------- nonpacks/library/views.py | 13 ------------ nonpacks/static/js/packs_preview.js | 12 +++++++++++ .../templates/library/project_detail.html | 2 +- requirements.txt | 7 +++++++ run_dev.sh | 4 ++++ run_prod.sh | 4 ++++ 10 files changed, 43 insertions(+), 23 deletions(-) create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore index 3ec8cfc..018ed8c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,6 @@ Packs_DB .migrations_done nonpacks/staticfiles/ nonpacks/media/ +nonpacks/static/vanilla/entity_index.json *.log AGENTS/ diff --git a/nonpacks/library/management/commands/refresh_vanilla_index.py b/nonpacks/library/management/commands/refresh_vanilla_index.py index 0053e01..6b8d4c0 100644 --- a/nonpacks/library/management/commands/refresh_vanilla_index.py +++ b/nonpacks/library/management/commands/refresh_vanilla_index.py @@ -4,7 +4,7 @@ from library.vanilla_textures import refresh_vanilla_index class Command(BaseCommand): - help = 'Rebuild the vanilla entity-texture index (media/vanilla/entity_index.json).' + help = 'Rebuild the vanilla entity-texture index (static/vanilla/entity_index.json).' def handle(self, *args, **options): index = refresh_vanilla_index() diff --git a/nonpacks/library/urls.py b/nonpacks/library/urls.py index d3abc59..6aaefe9 100644 --- a/nonpacks/library/urls.py +++ b/nonpacks/library/urls.py @@ -15,7 +15,6 @@ urlpatterns = [ path('packs//versions//download/', views.version_download, name='version_download'), path('packs//versions//files//download/', views.version_file_download, name='version_file_download'), path('packs//versions//asset/', views.pack_asset, name='pack_asset'), - path('vanilla/', views.vanilla_asset, name='vanilla_asset'), path('packs//guide///', views.guide_doc, name='guide_doc'), path('packs//gallery/upload/', views.asset_upload, name='asset_upload'), path('packs//gallery//thumb/', views.asset_thumbnail, name='asset_thumbnail'), diff --git a/nonpacks/library/vanilla_textures.py b/nonpacks/library/vanilla_textures.py index 735ca51..7629a39 100644 --- a/nonpacks/library/vanilla_textures.py +++ b/nonpacks/library/vanilla_textures.py @@ -1,9 +1,10 @@ """Build the vanilla entity-texture index for the model viewer. -Scans ``MEDIA_ROOT/vanilla/entity/`` (extracted from a Minecraft client jar) -and writes ``MEDIA_ROOT/vanilla/entity_index.json`` mapping each entity name to -the best "base skin" relative path, so the renderer can fall back to real -vanilla textures for models that have no pack textures of their own. +Scans ``static/vanilla/entity/`` (extracted from a Minecraft client jar, +tracked in git) and writes ``static/vanilla/entity_index.json`` mapping each +entity name to the best "base skin" relative path, so the renderer can fall +back to real vanilla textures for models that have no pack textures of their +own. """ import json import os @@ -13,6 +14,11 @@ from django.conf import settings ENTITY_ROOT = 'entity' INDEX_NAME = 'entity_index.json' + +def vanilla_dir(): + """The tracked static directory holding the vanilla texture set.""" + return os.path.join(settings.BASE_DIR, 'static', 'vanilla') + # Entity names whose vanilla layout differs from the model-name convention. ALIASES = { 'polar_bear': 'bear/polarbear.png', @@ -75,7 +81,7 @@ def _base_candidates(entity): """Return (entity, [candidate relative paths]) in priority order.""" if entity in ALIASES: return entity, [ALIASES[entity]] - root = os.path.join(settings.MEDIA_ROOT, 'vanilla', ENTITY_ROOT) + root = os.path.join(vanilla_dir(), ENTITY_ROOT) top = os.path.join(root, entity + '.png') if os.path.isfile(top): return entity, [entity + '.png'] @@ -98,7 +104,7 @@ def _base_candidates(entity): def refresh_vanilla_index(): """Rebuild the entity โ†’ {default, variants} skin map from the vanilla set.""" - root = os.path.join(settings.MEDIA_ROOT, 'vanilla', ENTITY_ROOT) + root = os.path.join(vanilla_dir(), ENTITY_ROOT) if not os.path.isdir(root): raise FileNotFoundError(f'{root} missing โ€” extract vanilla entity textures first') @@ -125,7 +131,7 @@ def refresh_vanilla_index(): index[entity]['default'] = variants[0] index[entity]['variants'] = variants - out = os.path.join(settings.MEDIA_ROOT, 'vanilla', INDEX_NAME) + out = os.path.join(vanilla_dir(), INDEX_NAME) with open(out, 'w') as f: json.dump(index, f, indent=1, sort_keys=True) return index diff --git a/nonpacks/library/views.py b/nonpacks/library/views.py index 5c17adf..5c6e9bc 100644 --- a/nonpacks/library/views.py +++ b/nonpacks/library/views.py @@ -423,19 +423,6 @@ def api_packs_latest(request, namespace, pack_id): }) -def vanilla_asset(request, asset_path): - """Serve a bundled Minecraft vanilla resource (e.g. entity textures) โ€” gated.""" - if not asset_path or asset_path.startswith('/') or '..' in asset_path.split('/'): - raise Http404 - full = Path(settings.MEDIA_ROOT) / 'vanilla' / asset_path - if not full.is_file(): - raise Http404 - content_type = mimetypes.guess_type(asset_path)[0] or 'application/octet-stream' - response = HttpResponse(full.read_bytes(), content_type=content_type) - response['Cache-Control'] = 'public, max-age=86400' - return response - - def guide_doc(request, slug, version_id, file_uuid): """Render one markdown guide document server-side (used by the switcher).""" version = get_object_or_404( diff --git a/nonpacks/static/js/packs_preview.js b/nonpacks/static/js/packs_preview.js index cf1168e..3cae9ce 100644 --- a/nonpacks/static/js/packs_preview.js +++ b/nonpacks/static/js/packs_preview.js @@ -147,6 +147,18 @@ } } // The vanilla/base texture must always be loaded first, then features. + // Wide player models additionally always load L1Z0's custom skins + // alongside the default Steve skin (tracked in static/player_skins/). + if (entity === 'player') { + for (const name of ['destroyed_skin.png', 'russian_goat.png']) { + try { + const img = await loadImage('/static/player_skins/' + name); + baseTextures.push({ name: name, dataUrl: imageDataUrl(img) }); + } catch (e) { + console.warn('packs: skip player skin', name, e); + } + } + } return baseTextures.concat(featureTextures); } diff --git a/nonpacks/templates/library/project_detail.html b/nonpacks/templates/library/project_detail.html index bfb7c75..f5d85bb 100644 --- a/nonpacks/templates/library/project_detail.html +++ b/nonpacks/templates/library/project_detail.html @@ -244,7 +244,7 @@ data-member="{{ model.member }}" data-name="{{ model.name }}" data-default-texture="{{ model.default_texture }}" data-base-url="{% url 'library:pack_asset' project.slug versions.0.pk 'ASSET' %}" - data-vanilla-base-url="{% url 'library:vanilla_asset' 'ASSET' %}"> Render + data-vanilla-base-url="/static/vanilla/ASSET"> Render

{{ model.bones }} bones ยท {{ model.cubes }} cubes

diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..df92643 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +# Packs site dependencies (Python 3.14) +Django==6.0.3 +gunicorn==25.1.0 +markdown-it-py==4.0.0 +pillow==12.2.0 +PyMySQL==1.1.2 +whitenoise==6.12.0 diff --git a/run_dev.sh b/run_dev.sh index ff657db..a061c57 100755 --- a/run_dev.sh +++ b/run_dev.sh @@ -28,5 +28,9 @@ else echo "To force re-run, use: $0 --force-setup" fi +# Rebuild the vanilla entity-texture index (gitignored generated file) +echo "Rebuilding vanilla entity-texture index..." +"$PYTHON_BIN" manage.py refresh_vanilla_index + echo "Starting Django development server on 0.0.0.0:8000..." exec "$PYTHON_BIN" manage.py runserver 0.0.0.0:8000 diff --git a/run_prod.sh b/run_prod.sh index edfffb3..fe57cfd 100755 --- a/run_prod.sh +++ b/run_prod.sh @@ -29,6 +29,10 @@ else echo "To force re-run, use: $0 --force-setup" fi +# Rebuild the vanilla entity-texture index (gitignored generated file) +echo "Rebuilding vanilla entity-texture index..." +"$PYTHON_BIN" manage.py refresh_vanilla_index + # Collect static files (served by gunicorn via whitenoise) "$PYTHON_BIN" manage.py collectstatic --noinput