Skip to content

feat(editor): Stage 4 - palette, object/NPC browsers, terrain, and placement - #109

Open
Shadow-MMN wants to merge 3 commits into
Bitcoindefi:mainfrom
Shadow-MMN:feat/editor-stage4-palette-browser
Open

feat(editor): Stage 4 - palette, object/NPC browsers, terrain, and placement#109
Shadow-MMN wants to merge 3 commits into
Bitcoindefi:mainfrom
Shadow-MMN:feat/editor-stage4-palette-browser

Conversation

@Shadow-MMN

Copy link
Copy Markdown

Closes #29

Implements the visual editor content browser: object catalog, NPC catalog, terrain palette with upload, and entity placement on the map.

What's included

API (api/)

  • New game_map_tile_entities table for objects/NPCs placed on tiles (draft/published lifecycle, same model as tile overrides)
  • Admin endpoints: PUT/DELETE /admin/game-data/maps/:n/entities, GET /admin/game-data/maps/:n/terrain, GET /admin/game-data/maps/:n/overrides (with drafts)
  • ?all=true query param on objects/NPCs for full catalog in one page (1,062 objects, 340 NPCs)
  • grhIndex added to object summaries
  • publish/discard/revert/status extended to include entities
  • Restored seed files (api/src/jsons/objs.json, api/src/jsons/npcs.json) — without them internal endpoints return 500
  • 5 integration tests in world-builder.integration.test.ts

Frontend (frontend/)

  • /construccion page with tabbed sidebar (Terreno / Objetos / NPCs)
  • VirtualizedList: dependency-free virtualized list (no react-window) for 1,000+ items — no DOM nodes created for off-screen rows
  • GraphicPreview: resolves grhIndex → cropped sprite via shared lib/graphicTextures.ts (extracted from CharacterSpritePreview)
  • ObjectsBrowser: searchable, filterable by type (30+ categories with counts), each item shows real sprite preview + type color dot
  • NpcsBrowser: searchable, preview via existing CharacterSpritePreview (body + head)
  • TerrainPalette: tile grid from terrain.json + uploaded PNGs, with PNG upload button
  • EditorCanvas: PixiJS canvas with zoom (scroll wheel, cursor-anchored), pan (Shift+click / middle-click), grid overlay, blocked-tile overlay, hover highlight, entity placement with bottom-anchor positioning, batch paint (up to 500 tiles per request)
  • EditorToolbar: map selector, tool buttons, publish/discard/revert actions, live status counters
  • RecentsStrip: recently used items persisted in localStorage, click to re-select
  • editorStore: React context providing all shared editor state
  • editorApi: typed API client via /api/editor/[...path] server-side proxy
  • data/objectTypes.ts: original type catalog with labels + colors (not copied from any reference repo)
  • /api/editor/[...path] proxy route: forwards to API with session cookie + admin proxy token (token never reaches the browser)

Documentation

  • docs/licensing-notes.md: AO-object-editor used as conceptual reference only; all code is original (no LICENSE declared in reference repo)
  • docs/modo-construccion-guide.md: step-by-step manual test guide

Acceptance criteria

  • Objects can be searched by name and filtered by type
  • Preview shows the real cropped graphic, not a placeholder
  • An object can be selected and placed on the map
  • Terrain palette includes user-uploaded graphics
  • Object list stays responsive with all 1,062 objects (virtualized — zero DOM for off-screen rows)
  • Licensing situation re: AO-object-editor documented in docs/licensing-notes.md

How to test

  1. Start API with GAME_DATA_ADMIN_* env vars, frontend with API_BASE_URL pointing to it
  2. Login as admin, navigate to /construccion
  3. Paint terrain, place an object and an NPC, publish
  4. Verify an anonymous player sees the published changes

Notes

Comment thread frontend/components/editor/EditorCanvas.tsx Outdated
Comment thread frontend/components/editor/EditorCanvas.tsx Outdated
…acement

Implements the visual editor content browser (issue Bitcoindefi#29):

API (api/):
- New game_map_tile_entities table for objects/NPCs placed on tiles
  (draft/published lifecycle, same model as tile overrides)
- Admin endpoints: PUT/DELETE entities, GET terrain palette, admin overrides
- all=true query param on objects/NPCs for full catalog in one page
- grhIndex added to object summaries
- Publish/discard/revert/status extended to include entities
- Restored seed files (objs.json, npcs.json) for internal endpoints
- 5 integration tests for admin endpoint access and entity data

Frontend (frontend/):
- /construccion page with tabbed sidebar (Terrain / Objects / NPCs)
- VirtualizedList: dependency-free virtualized list for 1000+ items
- GraphicPreview: resolves grhIndex to cropped sprite via shared
  lib/graphicTextures.ts (extracted from CharacterSpritePreview)
- ObjectsBrowser: searchable, filterable by type, with graphic preview
- NpcsBrowser: searchable, NPC sprite preview via CharacterSpritePreview
- TerrainPalette: tile palette from terrain.json + uploaded PNGs, upload
- EditorCanvas: PixiJS pan/zoom/grid, places objects/NPCs via entity API
- EditorToolbar: tool selection, publish/discard/revert actions, status
- RecentsStrip: recently used items persisted in localStorage
- editorStore: React context for shared editor state
- editorApi: typed API client via /api/editor proxy
- data/objectTypes: original type catalog (not copied from reference)
- /api/editor/[...path] proxy route (server-side auth + admin token)

Documentation:
- docs/licensing-notes.md: AO-object-editor used as reference only
- docs/modo-construccion-guide.md: manual test guide

No new dependencies added. Uses existing pixi.js + react + next stack.
* Use shared 2D canvas previews and virtualized lists to improve performance.
* Fix terrain brushes to paint full tiles and correctly handle empty layers.
* Optimize tile rendering with caching and selective updates.
* Fix deletion, drawing order, map switching, and pointer handling.
* Add admin session permission checks and confirmation dialogs.
* Add tests for terrain palette data, session permissions, and catalog flags.
Comment thread frontend/components/editor/NpcsBrowser.tsx Outdated
Comment thread api/src/repositories/gameNpcs.ts Outdated
* Treat invalid catalog flags as `false` instead of returning 500 errors.
* Wait for catalogs before saving NPC recents to prevent empty thumbnails.
* Avoid saving invalid graphics when catalog loading fails.
* Add regression test for invalid catalog flags.
@gitar-bot

gitar-bot Bot commented Aug 21, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 4 resolved / 4 findings

Implements the visual editor Stage 4 content browser with object/NPC catalogs, terrain palette, and map placement, addressing batch paint limits, flush race conditions, and NPC recent persistence issues.

✅ 4 resolved
Bug: Batch paint can exceed server's 500-tile cap and silently fail

📄 frontend/components/editor/EditorCanvas.tsx:551-565 📄 frontend/components/editor/EditorCanvas.tsx:611-625 📄 api/src/repositories/worldBuilder.ts:206-208
The terrain paint tool accumulates every dragged tile into pendingPaintRef.tiles with a 400ms debounce that resets on each move, so a long drag can gather far more than 500 unique tiles before flushing (maps are 100x100). flushPendingPaint then sends the whole set to paintTiles, but paintTilesSchema enforces .max(500), so the API responds 400 and the error is only console.error'd — the user's paint is silently dropped with no feedback. Chunk the tiles into batches of ≤500 (or flush eagerly once the pending set reaches 500) and surface failures in the UI.

Bug: Pending paint tiles can be lost if flush races with an in-flight apply

📄 frontend/components/editor/EditorCanvas.tsx:551-565
When the debounce timer fires (or pointerup flushes) while applyingRef.current is already true, flushPendingPaint returns early at line 561 before clearing pending.tiles, but it does not reschedule a flush. Those accumulated tiles then stay unsent until the user happens to paint again, so the last strokes of a fast drag may never reach the server. Reschedule a flush (e.g. setTimeout(flushPendingPaint, 400)) when bailing out because an apply is in progress.

Edge Case: NPC recents can persist grhIndex 0 if catalogs not loaded

📄 frontend/components/editor/NpcsBrowser.tsx:66-79
In NpcsBrowser.handleSelect, the recent entry's grhIndex is computed with resolveCharacterThumbnailGrh(bodiesDB, headsDB, ...). If a user clicks an NPC before getSharedBodiesDB()/getSharedHeadsDB() resolve, bodiesDB/headsDB are still null and the function returns 0, which gets written to localStorage recents permanently — leaving an empty thumbnail in RecentsStrip even after the catalogs load. Consider guarding the addRecent call until both DBs are loaded, or recomputing the recent's grhIndex when catalogs arrive.

Edge Case: Malformed catalog query flag throws instead of defaulting

📄 api/src/repositories/gameNpcs.ts:98-112
queryFlagSchema in gameNpcs.ts returns the raw value unchanged when it matches none of the true/false forms (e.g. ?hostileOnly=yes or ?all=maybe), which then fails z.boolean() and makes listFiltersSchema.parse throw. Unlike gameObjects.ts's all preprocess (which coerces anything unrecognized to false), this surfaces as an unhandled parse error rather than a benign default. Consider having the fallthrough return false (or undefined) so unexpected query strings degrade gracefully.

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Etapa 4: paleta y navegador de objetos, terreno y NPCs para el editor

1 participant