Skip to content

feat: Grid-Town 64x64 map rebuild with Scalar API docs#56

Merged
ComBba merged 24 commits intomainfrom
feature/world-zones
Feb 11, 2026
Merged

feat: Grid-Town 64x64 map rebuild with Scalar API docs#56
ComBba merged 24 commits intomainfrom
feature/world-zones

Conversation

@ComBba
Copy link
Copy Markdown
Contributor

@ComBba ComBba commented Feb 11, 2026

Summary

  • Rebuild the world map from 64x52 Work & Life Village to 64x64 Grid-Town layout
  • Add Scalar-powered interactive API documentation at /docs
  • Fix zone.exit event not displaying in client

Changes

World Map

  • New 64x64 tile map (2048x2048 pixels)
  • 6 zones: plaza, north-block, west-block, east-block, south-block, lake
  • 6 NPCs: greeter, office-pm, meeting-host, barista, arcade-host, ranger
  • 10 interactive facilities distributed across zones

API Documentation

  • Integrated Scalar for interactive API docs at /docs
  • OpenAPI 3.1 specification at /openapi.json
  • Covers all 8 AIC endpoints with examples and schemas

Bug Fixes

  • Fixed zone.exit event not showing in client notification panel

Documentation

  • Updated README.md with current project state
  • Added Grid-Town map specification

Testing

  • pnpm typecheck
  • pnpm build
  • Manual testing: map renders, player moves, zone transitions work

New Endpoints

Endpoint Description
GET /docs Interactive Scalar API documentation
GET /openapi.json OpenAPI 3.1 specification

Summary by CodeRabbit

릴리스 노트

  • New Features

    • Grid-Town 64x64 맵과 6개 지역 시스템 추가
    • 지역 진입/퇴출 시 화면 상단에 알림 배너 표시
    • 대화형 API 문서 통합 (Scalar)
    • 향상된 충돌 감지 및 타일 기반 네비게이션
  • Documentation

    • Grid-Town 맵 사양 및 지역 상세 문서 추가
  • Chores

    • NPC 위치 및 일정 재배치
    • 게임 세계 자산 재구성

…rpretation

- Add TileInterpreter for parsing map colors to tile types
- Add CollisionSystem for client-side movement prediction
- Add ZoneBanner UI for zone enter/leave notifications
- Add WanderBot for server-side AI wandering with collision respect
- Add world.test.ts with 17 test scenarios covering:
  - Zone enter/exit events
  - Wall collision rejection
  - Bot zone traversal
  - Door passability
- Update GameScene with zone tracking and banner display
- Extend shared types with TileType, TileInfo, WorldGrid types
- Update tileset configuration with door tile support
- Replace 64x52 Work & Life Village with 64x64 Grid-Town layout
- Update zone system: plaza, north-block, west-block, east-block, south-block, lake
- Add 6 NPCs: greeter, office-pm, meeting-host, barista, arcade-host, ranger
- Add 10 interactive facilities across all zones
- Integrate Scalar for interactive API documentation at /docs
- Add OpenAPI 3.1 spec covering all 8 AIC endpoints
- Fix zone.exit event not showing in client notification panel
- Update README with current project state and documentation links
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 11, 2026

Caution

Review failed

The pull request is closed.

워크스루

Grid-Town 64x64 맵으로 월드 시스템을 전면 재설계하고, 6개 존으로 구성된 새로운 존 시스템을 도입했습니다. 클라이언트의 TileInterpreter와 ClientCollisionSystem으로 타일 기반 충돌 처리를 구현하고, 서버의 WanderBot과 OpenAPI 문서화를 추가했으며, UI 컴포넌트(ZoneBanner)와 자산 추출 도구들을 신규 추가했습니다.

변경 사항

Cohort / File(s) 요약
맵 및 존 시스템 재설계
README.md, docs/reference/map_spec_grid_town.md, packages/shared/src/schemas.ts, packages/shared/src/types.ts
Grid-Town 64x64 레이아웃 도입, 존 ID를 lobby/office/meeting-center/lounge-cafe/arcade에서 plaza/north-block/west-block/east-block/south-block/lake로 변경, 새로운 타일 타입 및 WorldGrid 추가
클라이언트 장면 및 UI 컴포넌트
packages/client/src/game/scenes/GameScene.ts, packages/client/src/game/scenes/BootScene.ts, packages/client/src/ui/ZoneBanner.ts, packages/client/src/ui/Minimap.ts
GameScene에 ZoneBanner, TileInterpreter, ClientCollisionSystem 통합, 존 진입/퇴출 알림 구현, 미니맵 존 색상 및 표시 방식 업데이트
타일 해석 및 충돌 시스템
packages/client/src/world/TileInterpreter.ts, packages/client/src/systems/CollisionSystem.ts
새로운 TileInterpreter 클래스로 Tiled 맵 데이터를 WorldGrid로 변환, 새로운 ClientCollisionSystem으로 타일 그리드 기반 이동 및 충돌 검사 구현
서버 API 및 문서화
packages/server/src/openapi.ts, packages/server/src/app.config.ts, packages/server/package.json, packages/server/src/index.ts
OpenAPI 3.1 스펙 정의, Scalar 기반 API 문서 UI 추가(/docs), 새로운 /openapi.json 엔드포인트 제공
서버 봇 및 스키마
packages/server/src/bots/WanderBot.ts, packages/server/src/schemas/EntitySchema.ts, packages/server/src/schemas/FacilitySchema.ts, packages/server/src/schemas/NPCSchema.ts, packages/server/src/schemas/ZoneSchema.ts
WanderBot 클래스로 결정론적 배회 AI 구현, 모든 스키마 기본 존을 plaza로 업데이트
월드 팩 및 존 시스템
packages/server/src/world/WorldPackLoader.ts, packages/server/src/zone/ZoneSystem.ts, packages/server/src/rooms/GameRoom.ts
DEFAULT_ZONE_BOUNDS를 64x64 그리드로 업데이트, NPC/시설 존 매핑 재구성(plaza, north-block, east-block, west-block, south-block, lake), 기본 맵을 village에서 grid_town_outdoor로 변경
맵 자산 및 NPC 데이터
packages/client/public/assets/maps/village.json, world/packs/base/maps/grid_town_outdoor.json, packages/server/assets/maps/village.json, world/packs/base/manifest.json
새로운 64x64 village/grid_town 맵 JSON 자산, 충돌 및 객체 레이어 포함, NPC 위치 및 존 재할당
NPC 정의 재구성
world/packs/base/npcs/{greeter, office-pm, ranger}.json (신규), world/packs/base/npcs/{event-host, hr, it-help, pm, quest-giver, receptionist, sales-rep, security-guard, tutorial-guide}.json (삭제), world/packs/base/npcs/{arcade-host, barista, meeting-host}.json (위치 업데이트)
기존 NPC 대부분 제거, 새로운 NPC(greeter, office-pm, ranger) 추가, 남은 NPC들의 존 및 위치 업데이트
자산 추출 도구 및 구성
tools/extract_assets/extract.py, tools/extract_assets/config/{schema, terrain, props, npc, interactables}.json, tools/extract_assets/README.md
Phaser 3 스프라이트 아틀라스 자동 생성 시스템 구현, 그리드/직사각형 모드 지원, 결정론적 패킹
맵 생성 유틸리티
tools/generate_road_map.py, tools/convert_to_tiled.py, tools/render_map_with_tiles.py, tools/visualize_roads.py
마을 타일맵 프로시저 생성, 충돌 맵 렌더링, Tiled 형식 변환, 로드맵 시각화
테스트 업데이트
tests/unit/zone-system-64x64.test.ts, tests/unit/zone-system-plaza.test.ts, tests/unit/zone-transition.test.ts, tests/unit/world.test.ts
64x64 Grid-Town 레이아웃에 맞게 존 시스템 테스트 업데이트, 세계 시스템 통합 테스트 추가
테스트 제거
tests/unit/worldpack-unified-map.test.ts, tests/unit/zone-system-64x52.test.ts, tests/unit/npc-unified-map.test.ts
기존 64x52 레이아웃 및 통합 맵 테스트 제거
시설 및 월드팩 테스트
tests/unit/facility-handlers-coverage.test.ts, tests/unit/facility.test.ts, tests/unit/world-pack.test.ts
새로운 존 ID로 테스트 픽스처 및 기대값 업데이트
GitHub 워크플로우 및 설정
.github/workflows/{ci, codeql, labeler}.yml, .github/{dependabot, labeler}.yml, .gitignore, packages/client/vite.config.ts
멀티 스테이지 CI/CD 파이프라인, CodeQL 분석, PR 자동 라벨링, 청크 분할 및 캐싱 설정
스크립트 및 기타
package.json
extract:assets npm 스크립트 추가

예상 코드 리뷰 노력

🎯 4 (복잡함) | ⏱️ ~45분

관련될 수 있는 PR

🐰 Grid-Town이 피어나고,
여섯 존이 무지개를 이루며,
타일들이 춤을 추고,
봇들이 배회하며 노래하네! 🌆✨
새 세계가 태어났어, 친구들!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 풀 리퀘스트의 주요 변경사항을 명확하게 요약합니다. Grid-Town 64x64 맵 재구축과 Scalar API 문서화는 변경사항의 핵심 내용을 정확히 반영합니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/world-zones

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @ComBba, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the OpenClawWorld experience by introducing a brand new, larger game map and improving the developer experience with interactive API documentation. The new 'Grid-Town' map offers a more expansive and structured environment for players and AI agents, while the integrated Scalar documentation streamlines understanding and interaction with the AI Agent Interface. Additionally, a client-side bug related to zone exit notifications has been resolved, improving user feedback.

Highlights

  • World Map Rebuild: The game world has been completely rebuilt from the 64x52 'Work & Life Village' to a larger 64x64 'Grid-Town' layout, featuring 6 distinct zones, 6 new NPCs, and 10 interactive facilities.
  • Interactive API Documentation: Scalar-powered interactive API documentation has been integrated and is now available at the /docs endpoint, providing a comprehensive OpenAPI 3.1 specification for all AIC endpoints.
  • Bug Fix: Zone Exit Event: A bug preventing the zone.exit event from displaying correctly in the client's notification panel has been identified and fixed.
Changelog
  • README.md
    • Updated features list to reflect the new 64x64 Grid-Town map and interactive API docs.
    • Added Scalar to the list of technologies used.
    • Included a new 'World Map' section detailing the Grid-Town layout and zones.
    • Updated the project structure to reflect new world/ and docs/ directories.
    • Revised server endpoints to include /docs and /openapi.json.
    • Expanded AIC API section with interactive documentation link and updated quick start example.
    • Added a new 'Documentation' section with links to PRD, demo runbook, map spec, and AIC schema.
  • docs/reference/map_spec_grid_town.md
    • Added a new document detailing the Grid-Town map specification, including dimensions, zone boundaries, layout description, collision rules, interactive objects, NPC spawn positions, and layers.
  • package.json
    • Added a new script extract:assets for Python-based asset extraction.
  • packages/client/public/assets/maps/village.json
    • Added the new 64x64 Grid-Town map data in Tiled JSON format, replacing the previous map.
  • packages/client/src/game/config.ts
    • Imported AssetGalleryScene to the game configuration.
    • Added AssetGalleryScene to the list of available scenes.
  • packages/client/src/game/scenes/BootScene.ts
    • Updated ZONE_IDS to use a single 'village' map instead of multiple distinct zones.
    • Modified scene start logic to conditionally launch AssetGalleryScene based on a URL parameter, otherwise defaults to GameScene.
  • packages/client/src/game/scenes/GameScene.ts
    • Imported ZoneBanner, TileInterpreter, and ClientCollisionSystem.
    • Added new private properties for zoneBanner, tileInterpreter, and clientCollision.
    • Implemented initializeWorldGrid method to load and interpret map data for client-side collision.
    • Destroyed zoneBanner on scene shutdown.
    • Updated createMap to load the 'village' map instead of 'lobby'.
    • Modified checkZoneChange to use ZoneBanner for displaying zone transitions and correctly log zone.exit events.
  • packages/client/src/systems/CollisionSystem.ts
    • Added a new client-side collision system to manage world grid, tile blocking, and movement prediction.
  • packages/client/src/ui/Minimap.ts
    • Updated default minimap dimensions (height, mapWidth, mapHeight) to match the new Grid-Town map.
    • Revised ZONE_COLORS to reflect the new zone IDs (plaza, north-block, west-block, east-block, south-block, lake) and added colors for water and road.
    • Modified drawZones to include new zone boundaries and road/water elements for the Grid-Town layout.
  • packages/client/src/ui/ZoneBanner.ts
    • Added a new UI component for displaying zone entry and exit banners with animated transitions.
  • packages/client/src/world/TileInterpreter.ts
    • Added a new utility class for interpreting Tiled map data and color information into a structured world grid with tile types, collision, and zone IDs.
  • packages/server/package.json
    • Added @scalar/express-api-reference as a development dependency.
  • packages/server/src/app.config.ts
    • Imported apiReference from @scalar/express-api-reference and openApiSpec.
    • Added a new GET endpoint /openapi.json to serve the OpenAPI specification.
    • Integrated Scalar API reference at /docs endpoint for interactive API documentation.
  • packages/server/src/bots/WanderBot.ts
    • Added a new AI bot implementation that enables entities to wander randomly around the map, respecting collision and triggering zone events.
  • packages/server/src/index.ts
    • Added a console log for the new API Docs endpoint on server startup.
  • packages/server/src/openapi.ts
    • Added a new file defining the OpenAPI 3.1 specification for the AIC (Agent Interface Contract) API, including schemas and paths for all endpoints.
  • packages/server/src/rooms/GameRoom.ts
    • Updated the default mapId from 'lobby' to 'village' to reflect the new unified map.
  • packages/server/src/schemas/EntitySchema.ts
    • Changed the default currentZone for entities from 'lobby' to 'plaza'.
  • packages/server/src/schemas/FacilitySchema.ts
    • Changed the default zoneId for facilities from 'lobby' to 'plaza'.
  • packages/server/src/schemas/NPCSchema.ts
    • Changed the default zone for NPCs from 'lobby' to 'plaza'.
  • packages/server/src/schemas/ZoneSchema.ts
    • Changed the default id for zones from 'lobby' to 'plaza'.
  • packages/server/src/world/WorldPackLoader.ts
    • Updated the unified map filename from village_outdoor.json to grid_town_outdoor.json.
    • Revised getNpcZone and getFacilityZone mappings to align with the new Grid-Town zones and NPCs/facilities.
    • Updated getDefaultZoneBounds to reflect the new zone boundaries for Grid-Town.
    • Adjusted default NPC role from 'receptionist' to 'greeter'.
  • packages/shared/src/schemas.ts
    • Updated the ZoneIdSchema enum to include the new Grid-Town zone IDs: 'plaza', 'north-block', 'west-block', 'east-block', 'south-block', 'lake'.
  • packages/shared/src/types.ts
    • Added new type definitions for TileType, TileColorMapping, TileInfo, and WorldGrid for map interpretation.
    • Updated the ZoneId type to reflect the new Grid-Town zone IDs.
  • pnpm-lock.yaml
    • Updated lockfile to include new Scalar dependencies and nanoid version update.
  • tests/unit/facility-handlers-coverage.test.ts
    • Updated default RoomState zone from 'lobby' to 'plaza' in test setup.
    • Adjusted facility zone IDs in tests to match the new Grid-Town zones.
  • tests/unit/facility.test.ts
    • Updated default FacilitySchema zone IDs from 'lobby'/'office' to 'plaza'/'north-block' in tests.
  • tests/unit/world-pack.test.ts
    • Updated default zone IDs in test pack creation and assertions from 'lobby'/'office' to 'plaza'/'north-block'.
  • tests/unit/world.test.ts
    • Added new comprehensive integration tests for world systems, covering zone transitions, collision, and bot wandering behavior.
  • tests/unit/zone-system-64x52.test.ts
    • Removed old zone system tests specific to the 64x52 map layout.
  • tests/unit/zone-system-64x64.test.ts
    • Added new zone system tests specifically for the 64x64 Grid-Town map layout, verifying zone bounds, detection, and transitions.
  • tests/unit/zone-system-plaza.test.ts
    • Updated zone system tests for the plaza zone to reflect the new 64x64 Grid-Town layout and its coordinates.
  • tests/unit/zone-transition.test.ts
    • Updated zone transition tests to align with the new 64x64 Grid-Town map layout and its zone IDs.
  • world/packs/base/assets/tilesets/village_tileset.json
    • Updated tile definitions to include new types like 'grass', 'road', and 'door', and adjusted tile IDs and counts.
  • world/packs/base/manifest.json
    • Updated world pack description to 'Grid-Town World - Base Pack'.
    • Revised zones list and entryZone to reflect the new Grid-Town zones.
  • world/packs/base/maps/grid_town_outdoor.json
    • Added the new 64x64 Grid-Town outdoor map data, including ground, collision, and objects layers.
  • world/packs/base/npcs/arcade-host.json
    • Updated NPC's default zone to 'south-block' and adjusted default position and schedule coordinates.
  • world/packs/base/npcs/barista.json
    • Updated NPC's default zone to 'west-block' and adjusted default position and schedule coordinates.
  • world/packs/base/npcs/event-host.json
    • Removed NPC definition file.
  • world/packs/base/npcs/greeter.json
    • Added new NPC definition file for 'Sam the Greeter' in the 'plaza' zone.
  • world/packs/base/npcs/hr.json
    • Removed NPC definition file.
  • world/packs/base/npcs/index.json
    • Updated the list of NPCs to reflect additions and removals for the Grid-Town map.
  • world/packs/base/npcs/it-help.json
    • Removed NPC definition file.
  • world/packs/base/npcs/meeting-host.json
    • Updated NPC's default zone to 'east-block' and adjusted default position and schedule coordinates.
  • world/packs/base/npcs/office-pm.json
    • Added new NPC definition file for 'Jordan the PM' in the 'north-block' zone.
  • world/packs/base/npcs/pm.json
    • Removed NPC definition file.
  • world/packs/base/npcs/quest-giver.json
    • Removed NPC definition file.
  • world/packs/base/npcs/ranger.json
    • Added new NPC definition file for 'River the Ranger' in the 'lake' zone.
  • world/packs/base/npcs/receptionist.json
    • Removed NPC definition file.
  • world/packs/base/npcs/sales-rep.json
    • Removed NPC definition file.
  • world/packs/base/npcs/security-guard.json
    • Removed NPC definition file.
  • world/packs/base/npcs/tutorial-guide.json
    • Removed NPC definition file.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request is a significant feature update, rebuilding the world map to a new 64x64 "Grid-Town" layout and adding interactive API documentation using Scalar. The changes are extensive, touching client, server, shared code, and documentation. The new map, zone system, and API docs integration are well-implemented. I've found a couple of areas for improvement related to consistency and correctness. One is a minor naming inconsistency in the client's map assets that could affect maintainability. The other is a more significant issue on the server where outdated default zone boundaries could lead to incorrect behavior. Overall, this is a great contribution that modernizes the project's world and developer experience.

Comment on lines +535 to +540
plaza: { x: 192, y: 96, width: 736, height: 416 },
'north-block': { x: 1024, y: 192, width: 448, height: 448 },
'east-block': { x: 96, y: 928, width: 512, height: 576 },
'west-block': { x: 704, y: 928, width: 512, height: 320 },
'south-block': { x: 1344, y: 736, width: 608, height: 416 },
lake: { x: 1344, y: 1152, width: 608, height: 416 },
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The default zone bounds in this method appear to be from the old 64x52 map layout and do not match the new 64x64 Grid-Town specification. This could lead to incorrect zone detection if a world pack doesn't provide its own bounds. These should be updated to match the new layout defined in docs/reference/map_spec_grid_town.md and packages/server/src/zone/ZoneSystem.ts.

      plaza: { x: 768, y: 768, width: 512, height: 512 },
      'north-block': { x: 576, y: 64, width: 768, height: 384 },
      'east-block': { x: 1472, y: 704, width: 576, height: 640 },
      'west-block': { x: 64, y: 704, width: 640, height: 640 },
      'south-block': { x: 576, y: 1472, width: 768, height: 384 },
      lake: { x: 1408, y: 1408, width: 640, height: 640 },


const ZONE_IDS = ['lobby', 'office', 'meeting-center', 'lounge-cafe', 'arcade', 'plaza'] as const;
// Single unified map for the entire world
const ZONE_IDS = ['village'] as const;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using 'village' as the map key is a bit confusing since the new map is "Grid-Town" and its internal mapId is grid_town. For better clarity and consistency, I suggest using 'grid_town' as the key. This would involve:

  1. Renaming packages/client/public/assets/maps/village.json to grid_town.json.
  2. Updating this line to use 'grid_town'.
  3. Updating GameScene.ts to use this.make.tilemap({ key: 'grid_town' }).
Suggested change
const ZONE_IDS = ['village'] as const;
const ZONE_IDS = ['grid_town'] as const;

- Update main CI with parallel jobs (typecheck, lint, build, test)
- Add E2E testing workflow with Playwright
- Add Docker build and push workflow with GHCR
- Add CodeQL security scanning (weekly + on PR)
- Add Dependabot for automated dependency updates
- Add PR auto-labeling by files changed and size
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
tests/unit/worldpack-unified-map.test.ts (3)

7-7: ⚠️ Potential issue | 🔴 Critical

테스트 설명과 기대값이 새 64×64 맵과 불일치합니다.

테스트 describe 블록이 "64x52"로 명시되어 있지만, 실제 grid_town_outdoor.json은 64×64 맵입니다. 이 파일 전반에 걸쳐 height: 52 기대값(Line 24, 46, 207, 210)이 모두 실패할 것입니다. 64x64로 수정해야 합니다.


51-98: ⚠️ Potential issue | 🔴 Critical

존 경계 기대값이 이전 64×52 맵 기준이며, plaza 경계 테스트가 자기모순입니다.

  1. Line 57에서 plaza 경계를 { x: 192, y: 96, width: 736, height: 416 }로 검증하고, Line 97에서는 같은 plaza{ x: 1344, y: 1152, width: 608, height: 416 }로 검증합니다. 동일한 존에 대해 서로 다른 기대값을 가지므로 둘 중 하나는 반드시 실패합니다.
  2. 모든 경계 기대값이 이전 64×52 맵의 값으로, 새 Grid-Town 맵 스펙(docs/reference/map_spec_grid_town.md)의 경계와 일치하지 않습니다.

존 경계 기대값을 새 64×64 Grid-Town 맵 스펙에 맞춰 전면 업데이트해야 합니다.


215-243: ⚠️ Potential issue | 🟠 Major

NPC 및 시설물 이름 기대값이 이전 데이터 기준입니다.

  • Line 221-222: 'receptionist', 'security-guard'는 이전 NPC ID입니다. PR에서 추가된 새 NPC는 'greeter', 'office-pm' 등입니다.
  • Line 225: 'pm' → 새 ID는 'office-pm'입니다.
  • Line 236: 'reception_desk'가 plaza에 존재하는지 확인하지만, 새 맵 스펙에는 plaza에 notice_boardonboarding_signpost만 있습니다.

테스트 기대값을 새 Grid-Town NPC 및 시설물 데이터에 맞춰 업데이트해 주세요.

packages/server/src/world/WorldPackLoader.ts (2)

309-310: ⚠️ Potential issue | 🟡 Minor

기본 높이값 52가 이전 맵 크기의 잔여값입니다.

raw.height ?? 52에서 fallback 값이 이전 64×52 맵 기준입니다. 새로운 64×64 Grid-Town 맵에 맞게 64로 변경해야 합니다.

🐛 수정 제안
         width: raw.width ?? 64,
-        height: raw.height ?? 52,
+        height: raw.height ?? 64,

533-544: ⚠️ Potential issue | 🔴 Critical

getDefaultZoneBounds 값이 ZoneSystem.DEFAULT_ZONE_BOUNDS와 완전히 불일치합니다.

이 메서드의 존 경계값이 ZoneSystem(테스트에서 검증됨)과 Minimap.ts에 정의된 값과 전혀 다릅니다:

Zone WorldPackLoader ZoneSystem/Minimap/Tests
plaza {192, 96, 736, 416} {768, 768, 512, 512}
north-block {1024, 192, 448, 448} {576, 64, 768, 384}
east-block {96, 928, 512, 576} {1472, 704, 576, 640}
west-block {704, 928, 512, 320} {64, 704, 640, 640}
south-block {1344, 736, 608, 416} {576, 1472, 768, 384}
lake {1344, 1152, 608, 416} {1408, 1408, 640, 640}

또한 east-blockwest-block의 경계가 논리적으로 뒤바뀐 것처럼 보입니다 — east-block이 x=96(왼쪽)에, west-block이 x=704(오른쪽)에 위치합니다.

통합 맵 로딩 시 이 경계값으로 NPC/시설물이 잘못된 존에 배치되며, filterObjectsByZone도 잘못된 결과를 반환합니다. ZoneSystem.DEFAULT_ZONE_BOUNDS의 값과 통일해 주세요.

tests/unit/zone-system-plaza.test.ts (1)

105-111: ⚠️ Potential issue | 🔴 Critical

WorldPackLoader의 존 경계값이 ZoneSystem 정의와 일치하지 않습니다.

이 테스트가 기대하는 plaza 경계값은 { x: 768, y: 768, width: 512, height: 512 }이며, 이는 packages/server/src/zone/ZoneSystem.tsDEFAULT_ZONE_BOUNDS와 일치합니다. 그러나 packages/server/src/world/WorldPackLoader.tsgetDefaultZoneBounds() 메서드는 모든 존에 대해 완전히 다른 좌표를 사용합니다:

  • ZoneSystem: plaza { x: 768, y: 768, width: 512, height: 512 }
  • WorldPackLoader: plaza { x: 192, y: 96, width: 736, height: 416 }

이는 서버 측 존 감지 로직(ZoneSystem)과 맵 로딩(WorldPackLoader)이 서로 다른 좌표계를 사용한다는 뜻입니다. 결과적으로 NPC/시설물 배치와 런타임 존 감지 사이에 심각한 불일치가 발생할 수 있습니다.

🤖 Fix all issues with AI agents
In `@packages/client/src/game/config.ts`:
- Line 4: The import of AssetGalleryScene in config.ts (AssetGalleryScene)
causes CI to fail because the module file is missing; either add the missing
module file packages/client/src/game/scenes/AssetGalleryScene.ts implementing
and exporting the AssetGalleryScene class, or remove the import and any
reference to AssetGalleryScene in the scenes registration array (where scenes
are assembled) until the file is added; update any exports or scene lists (in
config.ts) accordingly so the project typechecks and builds.

In `@packages/client/src/systems/CollisionSystem.ts`:
- Around line 39-53: The server's MovementSystem.setDestination currently only
checks whether the target tile is blocked and lacks the diagonal-corner rule
present in client CollisionSystem.canMoveTo, causing desyncs; update
MovementSystem.setDestination to replicate the client's checks: compute dx/dy
between from and to, reject moves with dx>1 or dy>1, and for dx===1 && dy===1
additionally reject when both adjacent orthogonal tiles (isBlocked(fromTx, toTy)
&& isBlocked(toTx, fromTy)) are blocked, using the same isBlocked helper used on
the client so server-side validation mirrors canMoveTo exactly.

In `@packages/client/src/ui/Minimap.ts`:
- Around line 17-19: The mapHeight constant in Minimap.ts is still 1664 while
mapWidth is updated to 2048, causing scale/clip math and region rendering (e.g.,
south-block and lake) to fall outside the minimap; update mapHeight to 2048 to
match a 64×64 tile (2048×2048px) map, then re-run or adjust any scaling/viewport
calculations in the Minimap component that reference mapHeight so the computed
scales, bounds and clipping for regions like "south-block" and "lake" correctly
fall inside the minimap area.

In `@packages/client/src/world/TileInterpreter.ts`:
- Around line 12-24: COLOR_RANGES contains overlapping RGB intervals causing
ambiguous classification (e.g., floor_plaza vs floor_south, road vs floor_plaza,
floor_north vs floor_west); update the ranges in the COLOR_RANGES array so each
ColorRange is mutually exclusive (adjust endpoints to non-overlapping intervals
or tighten channel bounds for 'floor_plaza', 'floor_south', 'road',
'floor_north', 'floor_west'), or if intentional, add a clear comment near the
COLOR_RANGES declaration documenting the priority ordering and why overlaps are
acceptable.
- Around line 112-134: The tileTypes array in interpretTileId is misordered and
missing a type: reorder the floor entries to match the shared types sequence
(ensure 'floor_north' → 'floor_west' → 'floor_east' at indexes 4–6) and add the
missing 'decoration' entry in the array at the position defined by the shared
types so tileId lookups map correctly; update the tileTypes definition inside
interpretTileId (and keep usage of ZONE_FLOOR_TYPES, isDoor, and collision logic
unchanged).

In `@packages/server/package.json`:
- Line 36: packages/server currently lists "@scalar/express-api-reference" in
devDependencies causing runtime module-not-found when app.config.ts
unconditionally imports it (see import at top of app.config.ts) and always
registers the /docs endpoint; move "@scalar/express-api-reference" from
devDependencies into dependencies in packages/server/package.json so it is
installed in production, then verify app.config.ts still imports the module and
the /docs route registers correctly.

In `@packages/server/src/bots/WanderBot.ts`:
- Around line 143-152: The code currently mixes injected simulation time with
wall-clock time: update(currentTimeMs) uses the injected currentTimeMs but
scheduleNextMove(), tryMove(), and moveTo() call Date.now() directly, causing
inconsistent nextMoveTime semantics; modify scheduleNextMove(nextTimeBaseMs),
tryMove(currentTimeMs) and moveTo(target, currentTimeMs) (or otherwise accept a
currentTimeMs parameter) so all time calculations use the injected
currentTimeMs, update assignments to this.nextMoveTime from the passed-in
currentTimeMs (not Date.now()), and thread currentTimeMs through update ->
tryMove -> moveTo to keep deterministic timing for the RNG and
movementSystem.isMoving(this.entityId) checks.
- Around line 92-102: random() can return exactly 1.0 and randomInt(min,max)
then yields an off-by-one; also the 32-bit LCG multiplication can lose precision
using JS Numbers. Fix by making random() use 32-bit integer arithmetic (e.g. use
Math.imul and >>>0 to keep randomState as uint32 after updating with multiplier
and increment) and divide by 0x80000000 (2**31) or otherwise ensure the returned
value is strictly less than 1.0; update references in the WanderBot class
methods random() and randomInt(min,max) so randomState is updated with
Math.imul(this.randomState, 1103515245) + 12345 masked to 32 bits and random()
returns randomState / 0x80000000 to prevent returning 1.0.

In `@packages/server/src/openapi.ts`:
- Around line 412-429: The OpenAPI schemas ProfileUpdateRequest and
ProfileUpdateResponseData must be made consistent with the TypeScript types in
types.ts: update ProfileUpdateRequest to include the fields used in types.ts
(status, statusMessage, title, department) instead of/alongside name and meta
(or vice‑versa if types.ts is canonical), and update ProfileUpdateResponseData
to use the same result shape (rename updated -> applied if types.ts uses
"applied", and add the missing profile object and any server timestamp field
that types.ts expects). Locate the ProfileUpdateRequest and
ProfileUpdateResponseData components in this file and change their properties
and required arrays to exactly match the field names/types in types.ts so
OpenAPI and TypeScript stay in sync.
- Around line 203-220: The OpenAPI schemas RegisterRequest and
RegisterResponseData are out of sync with the TypeScript types in
packages/shared/src/types.ts (the RegisterRequest type with { name, roomId } and
the response type with { agentId, roomId, sessionToken }). Reconcile them by
either: (A) updating openapi.ts RegisterRequest to remove agentId and only
require name and roomId, and updating RegisterResponseData to use the same field
names (agentId, roomId, sessionToken) and types/descriptions; or (B) updating
the TypeScript types in packages/shared/src/types.ts to include agentId and to
rename sessionToken → token if the OpenAPI shape is the source of truth. Update
examples/descriptions in openapi.ts accordingly and ensure $ref schema names
(RegisterRequest, RegisterResponseData) match the adjusted TypeScript shapes so
clients and runtime agree.

In `@tests/unit/world.test.ts`:
- Around line 53-73: The test uses coordinates (640, 300) which fall into
north-block based on DEFAULT_ZONE_BOUNDS, so update the test expectations or
coordinates: either change the asserted current zone from 'plaza' to
'north-block' for this test and all other specs reusing (640, 300) (Scenario A,
Scenario B beforeEach, Scenario D beforeEach), or replace the coordinate with a
plaza-inside point such as (1024, 1024) and keep expectations as 'plaza'; adjust
the calls to zoneSystem.updateEntityZone and subsequent expects
(result.currentZone, eventLog checks for 'zone.enter', and previousZone
assertions) accordingly.
- Around line 115-136: The test assumes the entity starts in 'plaza' but
beforeEach places it at (640,300) which is already in 'north-block', so
zoneSystem.updateEntityZone('player_1', 1200, 400, ...) will not produce the
expected change; fix by ensuring the entity's initial position is inside 'plaza'
(e.g., set entity.setPosition to plaza coordinates in this test or adjust the
beforeEach setup) before calling zoneSystem.updateEntityZone, so the call
triggers a transition from previousZone 'plaza' to currentZone 'north-block' and
the assertions on result.changed, previousZone, currentZone and emitted
zone.exit/zone.enter events become valid.
- Around line 240-257: The test uses incorrect coordinates for east-block in the
zonePositions array: { zone: 'east-block', x: 300, y: 1100 } actually falls in
west-block; update the test by either changing that entry to the correct
east-block coordinates (e.g., x ~1600, y ~1024) or replace the expectation
expect(visited).toContain('east-block') with
expect(visited).toContain('west-block'); ensure you adjust the matching zone
name in the zonePositions array and keep the calls to entity.setPosition(...)
and wanderBot.update(Date.now()) as-is so visited reflects the corrected
position.
- Around line 10-12: Tests use TILE_SIZE, MAP_WIDTH and MAP_HEIGHT to build the
test map but MAP_HEIGHT is set to 52 which misaligns with the intended 64×64
Grid-Town; update MAP_HEIGHT to 64 (and any derived pixel calculations that use
TILE_SIZE * MAP_HEIGHT) so bottom zones like "south-block" (y:1472-1856) and
"lake" (y:1408-2048) fall inside the test map; also sweep the test assertions in
world.test.ts that reference hard-coded pixel bounds and adjust them to use
TILE_SIZE * MAP_HEIGHT or the updated expected values so collision/movement
checks remain correct.

In `@tests/unit/worldpack-unified-map.test.ts`:
- Around line 33-40: The zones test array contains a duplicated 'plaza' and is
missing 'lake'; update the const zones declaration(s) in
worldpack-unified-map.test.ts (the arrays named zones) to replace the second
'plaza' with 'lake' so the list becomes
['plaza','north-block','east-block','west-block','south-block','lake'] and make
the same change for the other identical zones array later in the file.
🟡 Minor comments (9)
packages/client/public/assets/maps/village.json-11-11 (1)

11-11: ⚠️ Potential issue | 🟡 Minor

nextobjectid가 잘못되었습니다 — 객체 ID 충돌 가능성.

현재 nextobjectid1로 설정되어 있지만, objects 레이어에 이미 ID 1~10인 객체가 10개 존재합니다. Tiled에서 이 맵을 다시 편집할 경우 ID 충돌이 발생합니다. 11로 수정해야 합니다.

🐛 수정 제안
-  "nextobjectid": 1,
+  "nextobjectid": 11,
docs/reference/map_spec_grid_town.md-77-81 (1)

77-81: ⚠️ Potential issue | 🟡 Minor

lake.viewpoint가 Z_LAKE 존 경계 밖에 위치합니다.

lake.viewpoint의 타일 좌표 (42, 42)는 Z_LAKE 존 경계(시작 타일: (44, 44)) 밖에 있습니다. 이 시설물은 플레이어가 lake 존에 진입하기 전에 접근 가능하게 되며, 존 기반 시설물 조회 로직에서 누락될 수 있습니다.

타일 좌표를 (44, 44) 이상으로 조정하거나, 의도적으로 존 외부에 배치한 것이라면 문서에 명시해 주세요.

docs/reference/map_spec_grid_town.md-48-53 (1)

48-53: ⚠️ Potential issue | 🟡 Minor

plaza.signpost의 타입이 맵 JSON과 불일치합니다.

문서에서는 plaza.signpost의 타입을 onboarding_signpost로 명시하고 있지만, grid_town_outdoor.json(Line 328)에서는 notice_board로 정의되어 있습니다. 문서와 실제 데이터 중 하나를 수정하여 일관성을 맞춰야 합니다.

world/packs/base/maps/grid_town_outdoor.json-10-11 (1)

10-11: ⚠️ Potential issue | 🟡 Minor

nextobjectid가 잘못되었습니다.

오브젝트 레이어에 ID 1~10까지 10개의 오브젝트가 정의되어 있으므로, nextobjectid11이어야 합니다. 현재 값 1은 Tiled 에디터에서 이 맵을 다시 열어 편집할 때 오브젝트 ID 충돌을 유발할 수 있습니다.

🔧 수정 제안
-  "nextobjectid": 1,
+  "nextobjectid": 11,
packages/server/src/openapi.ts-124-127 (1)

124-127: ⚠️ Potential issue | 🟡 Minor

ChatChannel enum이 TypeScript 타입의 부분집합입니다.

OpenAPI에서는 ['proximity', 'global']만 정의하고 있지만, types.tsChatChannel'proximity' | 'global' | 'team' | 'meeting' | 'dm'을 포함합니다. AIC v0.1에서 의도적으로 제한한 것이라면 스키마 설명에 명시해 주세요. 그렇지 않으면 team/meeting/dm 채널 사용 시 유효성 검증 실패가 발생합니다.

world/packs/base/npcs/ranger.json-10-29 (1)

10-29: ⚠️ Potential issue | 🟡 Minor

대화 트리 구조가 로드 시스템에서 손실됩니다.

JSON 파일의 dialogue 필드는 중첩된 객체 구조(greeting/swimming/fishing 노드)로 정의되어 있지만, WorldPackLoader.parseNpcDefinition(라인 630)에서 Array.isArray(raw.dialogue) 검사 시 배열이 아니므로 빈 배열 []로 변환됩니다. 결과적으로 이 대화 트리 데이터는 로드된 NpcDefinition 객체에서 사라집니다.

테스트는 원본 JSON 파일을 직접 읽어 대화 트리 구조를 검증하지만, 실제 로드되는 NPC 시스템에서는 이 데이터가 사용되지 않습니다. 대화 트리를 실제로 소비하는 별도의 시스템이 있는지, 아니면 NpcDefinition 타입을 업데이트해야 하는지 확인해 주세요.

packages/client/src/ui/ZoneBanner.ts-60-68 (1)

60-68: ⚠️ Potential issue | 🟡 Minor

showLeaveshowEnter가 연속 호출되면 leave 배너가 보이지 않습니다.

GameScene.ts Line 498-499에서 showLeaveshowEnter가 동기적으로 연속 호출됩니다. showBanner는 이전 배너를 즉시 교체하므로 사용자는 leave 배너를 볼 수 없습니다.

의도적이라면 무시해도 되지만, 두 알림을 모두 표시하려면 큐 메커니즘이나 결합 메시지(예: "Plaza → North Block")를 고려해 보세요.

packages/client/src/game/scenes/GameScene.ts-91-118 (1)

91-118: ⚠️ Potential issue | 🟡 Minor

forEach 콜백이 값을 반환하여 린트 경고가 발생합니다.

tile => groundData.push(tile.index)Array.push()가 배열 길이를 반환하므로 forEach 콜백에서 값을 반환하는 것으로 간주됩니다. Biome 린터가 이를 경고합니다.

🔧 중괄호를 추가하여 암시적 반환 제거
       groundLayer.data.forEach(row => {
-        row.forEach(tile => groundData.push(tile.index));
+        row.forEach(tile => { groundData.push(tile.index); });
       });

       collisionLayer.data.forEach(row => {
-        row.forEach(tile => collisionData.push(tile.index));
+        row.forEach(tile => { collisionData.push(tile.index); });
       });
packages/client/src/ui/ZoneBanner.ts-123-131 (1)

123-131: ⚠️ Potential issue | 🟡 Minor

destroy()에서 resize 리스너가 제거되지 않아 메모리 누수가 발생할 수 있습니다.

Line 51에서 scene.scale.on('resize', ...) 리스너를 등록하지만, destroy()에서 제거하지 않습니다. Scene이 재시작되거나 ZoneBanner가 교체될 경우 이전 콜백이 계속 유지됩니다.

🐛 리스너 정리 제안
+  private resizeHandler: () => void;
+
   constructor(scene: Phaser.Scene) {
     this.scene = scene;
     // ...
-    scene.scale.on('resize', () => this.updatePosition());
+    this.resizeHandler = () => this.updatePosition();
+    scene.scale.on('resize', this.resizeHandler);
   }

   destroy(): void {
     if (this.hideTimeout) {
       clearTimeout(this.hideTimeout);
     }
     if (this.currentTween) {
       this.currentTween.stop();
     }
+    this.scene.scale.off('resize', this.resizeHandler);
     this.container.destroy();
   }
🧹 Nitpick comments (11)
packages/server/src/rooms/GameRoom.ts (1)

87-87: mapId 매개변수와 ZoneId 타입 간의 불일치를 해결하세요.

기본 mapId 값인 'village'는 정의된 ZoneId 타입('plaza' | 'north-block' | 'west-block' | 'east-block' | 'south-block' | 'lake')에 포함되지 않습니다. 106번 줄에서 mapId as ZoneId로 캐스팅되어 findAndSetSpawnPoint에 전달되지만, worldPack.maps.get(zoneId)에서 undefined를 반환한 후 parsedMap.objects에서 spawn 포인트를 찾는 fallback 경로로 처리됩니다. 현재 fallback 로직이 있어 런타임 오류는 발생하지 않지만, as ZoneId 캐스트가 타입 안전성을 우회하고 있습니다.

findAndSetSpawnPoint의 매개변수를 ZoneId에서 string으로 변경하거나, mapIdZoneId 타입과 구분하여 정의하는 것을 권장합니다.

package.json (1)

24-24: python 명령어의 이식성 문제

일부 시스템(특히 macOS, 최신 Linux 배포판)에서는 python이 존재하지 않고 python3만 사용 가능합니다. python3으로 변경하거나 프로젝트 규약에 맞게 명시하는 것을 권장합니다.

제안
-    "extract:assets": "python tools/extract_assets/extract.py --all"
+    "extract:assets": "python3 tools/extract_assets/extract.py --all"
packages/shared/src/schemas.ts (1)

414-427: 섹션 주석이 이전 명칭을 사용하고 있습니다.

Line 415의 Work-Life World Schemas 주석이 새로운 "Grid-Town" 레이아웃 명칭과 불일치합니다. ZoneIdSchema 값 자체는 ZoneId 타입과 정확히 일치하며 문제 없습니다.

제안
-// Work-Life World Schemas
+// Grid-Town World Schemas
world/packs/base/npcs/meeting-host.json (1)

15-15: 대화 텍스트에 "Meeting Center" 참조가 남아있습니다.

존 이름이 east-block으로 변경되었지만, 대화 텍스트에서 여전히 "Welcome to the Meeting Center!"라고 표시됩니다. 이것이 NPC의 인게임 대화로서 의도된 것이라면 문제없지만, 존 이름과 일치시키려면 업데이트를 고려해 보세요.

packages/client/src/ui/Minimap.ts (2)

87-124: 워터/로드 프로시저럴 렌더링의 매직 넘버가 64×52 레이아웃 기준입니다.

워터 배경의 y < 20 루프(Line 96)와 로드 좌표들(Lines 100-124)이 이전 레이아웃의 하드코딩된 타일 좌표를 사용하고 있습니다. mapHeight가 2048로 수정되면 이 값들도 64×64 맵에 맞게 재검토가 필요합니다.

또한 이 매직 넘버들(17, 23, 37, 41 등)을 상수로 추출하거나 주석을 추가하면 향후 맵 변경 시 유지보수가 용이해집니다.


126-133: 존 좌표가 하드코딩되어 있으며 서버 DEFAULT_ZONE_BOUNDS와 동기화 필요.

이 존 정의는 테스트의 기대값(plaza: {768,768,512,512})과는 일치하지만, WorldPackLoader.getDefaultZoneBounds와는 불일치합니다. 존 경계가 3곳(ZoneSystem, WorldPackLoader, Minimap)에 개별적으로 하드코딩되어 있어 동기화 관리가 어렵습니다.

공유 패키지(@openclawworld/shared)에 존 경계를 정의하고 재사용하는 것을 권장합니다.

packages/server/src/openapi.ts (1)

8-787: OpenAPI 스키마와 TypeScript 타입 간의 동기화를 자동화하는 것을 권장합니다.

현재 OpenAPI 스펙이 TypeScript 타입과 수동으로 별도 관리되어 여러 곳에서 스키마 드리프트가 발생하고 있습니다(RegisterRequest, ProfileUpdateRequest, ChatChannel 등). zod-to-openapi 또는 typescript-json-schema 같은 도구를 사용하여 TypeScript 타입으로부터 OpenAPI 스키마를 자동 생성하면 일관성을 보장할 수 있습니다.

packages/client/src/game/scenes/GameScene.ts (1)

493-516: entity에서 currentZone을 추출할 때 as unknown as ... 이중 캐스트가 사용됩니다.

Line 494의 (entity as unknown as { currentZone?: ZoneId }).currentZoneEntity 타입이 currentZone을 선언하지 않기 때문에 필요한 것으로 보입니다. 이 패턴은 타입 변경 시 컴파일 오류 없이 런타임 버그로 이어질 수 있습니다.

가능하면 Entity 타입/인터페이스에 currentZone?: ZoneId를 추가하여 타입 안전성을 확보하는 것을 권장합니다.

packages/client/src/world/TileInterpreter.ts (2)

95-106: loadFromImage에서 getImageData를 타일마다 개별 호출하고 있어 비효율적입니다.

64×64 맵 기준 4,096회의 getImageData 호출이 발생합니다. 전체 이미지 데이터를 한 번에 가져온 후 오프셋으로 접근하면 성능이 크게 개선됩니다.

♻️ 리팩터링 제안
+    const imageData = ctx.getImageData(0, 0, source.width, source.height);
+    const pixels = imageData.data;
+
     for (let y = 0; y < tilesY; y++) {
       const row: TileInfo[] = [];
       for (let x = 0; x < tilesX; x++) {
         const sampleX = x * this.tileSize + this.tileSize / 2;
         const sampleY = y * this.tileSize + this.tileSize / 2;
-        const pixel = ctx.getImageData(sampleX, sampleY, 1, 1).data;
+        const offset = (sampleY * source.width + sampleX) * 4;
 
-        const tileInfo = this.interpretColor(pixel[0], pixel[1], pixel[2]);
+        const tileInfo = this.interpretColor(pixels[offset], pixels[offset + 1], pixels[offset + 2]);
         row.push(tileInfo);
       }
       grid.push(row);
     }

155-157: interpretColor의 기본 폴백값에 zoneId가 빠져있습니다.

매칭되는 색상 범위가 없을 때 { type: 'grass', collision: false, isDoor: false } 를 반환하지만 zoneId 속성이 누락되어 있습니다. TileInfo 타입에서 zoneId는 optional이므로 런타임 에러는 아니지만, 명시적으로 zoneId: undefined를 추가하면 일관성이 높아집니다.

packages/server/src/bots/WanderBot.ts (1)

206-213: DIRECTION_DX/DIRECTION_DY 배열을 tryMove 호출마다 재생성하고 있습니다.

이 상수 배열들은 모듈 레벨이나 클래스 static 필드로 한 번만 정의하면 됩니다.

♻️ 리팩터링 제안

클래스 외부 모듈 스코프에 정의:

+const DIRECTION_DX = [0, 1, 1, 1, 0, -1, -1, -1] as const;
+const DIRECTION_DY = [-1, -1, 0, 1, 1, 1, 0, -1] as const;
+
 export class WanderBot {

tryMove 내부에서 제거:

-      const DIRECTION_DX = [0, 1, 1, 1, 0, -1, -1, -1];
-      const DIRECTION_DY = [-1, -1, 0, 1, 1, 1, 0, -1];
-      const dx = DIRECTION_DX[direction];
+      const dx = DIRECTION_DX[direction]!;

Comment on lines +39 to +53
canMoveTo(fromTx: number, fromTy: number, toTx: number, toTy: number): boolean {
if (this.isBlocked(toTx, toTy)) return false;

const dx = Math.abs(toTx - fromTx);
const dy = Math.abs(toTy - fromTy);
if (dx > 1 || dy > 1) return false;

if (dx === 1 && dy === 1) {
if (this.isBlocked(fromTx, toTy) && this.isBlocked(toTx, fromTy)) {
return false;
}
}

return true;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "*.ts" -not -path "*/client/*" -not -path "*/test/*" -not -path "*/node_modules/*" | head -20

Repository: Two-Weeks-Team/openClawWorld

Length of output: 890


🏁 Script executed:

rg -n "canMoveTo|CollisionSystem" --type=ts -g '!**/client/**' -g '!**/node_modules/**' -A 5

Repository: Two-Weeks-Team/openClawWorld

Length of output: 9497


🏁 Script executed:

cat -n packages/server/src/collision/CollisionSystem.ts

Repository: Two-Weeks-Team/openClawWorld

Length of output: 3110


🏁 Script executed:

cat -n packages/server/src/movement/MovementSystem.ts

Repository: Two-Weeks-Team/openClawWorld

Length of output: 4340


서버의 이동 검증 로직에 대각선 이동 정책이 없습니다 — 클라이언트와 서버 간 desync 위험이 있습니다.

클라이언트의 canMoveTo는 대각선 이동 시 양쪽 인접 타일이 모두 막혔을 때만 이동을 차단하는 permissive 정책을 구현합니다. 반면 서버의 MovementSystem.setDestination은 목표 타일이 막혀 있는지만 확인하고, 대각선 이동 검증을 전혀 수행하지 않습니다 (lines 28-34).

서버가 대각선 코너 타일 검사를 구현하지 않으면, 특정 대각선 이동 시나리오에서 클라이언트가 허용하지만 서버가 거부하거나 그 반대의 상황이 발생할 수 있습니다. 서버도 클라이언트와 동일한 대각선 이동 정책을 구현해야 합니다.

🤖 Prompt for AI Agents
In `@packages/client/src/systems/CollisionSystem.ts` around lines 39 - 53, The
server's MovementSystem.setDestination currently only checks whether the target
tile is blocked and lacks the diagonal-corner rule present in client
CollisionSystem.canMoveTo, causing desyncs; update MovementSystem.setDestination
to replicate the client's checks: compute dx/dy between from and to, reject
moves with dx>1 or dy>1, and for dx===1 && dy===1 additionally reject when both
adjacent orthogonal tiles (isBlocked(fromTx, toTy) && isBlocked(toTx, fromTy))
are blocked, using the same isBlocked helper used on the client so server-side
validation mirrors canMoveTo exactly.

Comment on lines +12 to +24
const COLOR_RANGES: ColorRange[] = [
{ r: [0, 50], g: [100, 200], b: [200, 255], type: 'water', collision: true },
{ r: [0, 100], g: [150, 255], b: [0, 100], type: 'grass', collision: false },
{ r: [200, 255], g: [200, 255], b: [0, 100], type: 'door', collision: false, isDoor: true },
{ r: [60, 100], g: [50, 80], b: [40, 70], type: 'wall', collision: true },
{ r: [180, 220], g: [180, 220], b: [180, 220], type: 'road', collision: false },
{ r: [150, 180], g: [150, 180], b: [150, 180], type: 'floor_plaza', collision: false },
{ r: [170, 200], g: [200, 230], b: [210, 240], type: 'floor_north', collision: false },
{ r: [190, 220], g: [200, 230], b: [190, 220], type: 'floor_west', collision: false },
{ r: [200, 230], g: [180, 210], b: [150, 180], type: 'floor_east', collision: false },
{ r: [140, 170], g: [140, 170], b: [140, 170], type: 'floor_south', collision: false },
{ r: [100, 130], g: [140, 170], b: [200, 230], type: 'floor_lake', collision: false },
];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

COLOR_RANGES에서 floor_plazafloor_south의 RGB 범위가 겹칩니다.

  • floor_plaza: r[150,180], g[150,180], b[150,180]
  • floor_south: r[140,170], g[140,170], b[140,170]

세 채널 모두 [150,170] 구간에서 겹치며, COLOR_RANGES 배열에서 floor_plaza가 먼저 정의되어 있으므로 해당 범위의 색상은 항상 floor_plaza로 분류됩니다. 의도한 floor_south 타일이 floor_plaza로 잘못 해석될 수 있습니다.

마찬가지로 road(r[180,220])와 floor_plaza(r[150,180])도 경계값 180에서 겹치고, floor_northfloor_west도 일부 겹칩니다. 범위를 상호 배타적으로 조정하거나, 범위 우선순위가 의도된 것이라면 주석으로 명시해주세요.

🤖 Prompt for AI Agents
In `@packages/client/src/world/TileInterpreter.ts` around lines 12 - 24,
COLOR_RANGES contains overlapping RGB intervals causing ambiguous classification
(e.g., floor_plaza vs floor_south, road vs floor_plaza, floor_north vs
floor_west); update the ranges in the COLOR_RANGES array so each ColorRange is
mutually exclusive (adjust endpoints to non-overlapping intervals or tighten
channel bounds for 'floor_plaza', 'floor_south', 'road', 'floor_north',
'floor_west'), or if intentional, add a clear comment near the COLOR_RANGES
declaration documenting the priority ordering and why overlaps are acceptable.

@github-advanced-security
Copy link
Copy Markdown

This pull request sets up GitHub code scanning for this repository. Once the scans have completed and the checks have passed, the analysis results for this pull request branch will appear on this overview. Once you merge this pull request, the 'Security' tab will show more code scanning analysis results (for example, for the default branch). Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results. For more information about GitHub code scanning, check out the documentation.

Runner configuration:
- Mac (primary): [self-hosted, macOS, ARM64]
- Linux (Docker only): [self-hosted, Linux, X64]
The AssetGalleryScene module doesn't exist, causing build failures.
Remove the import and scene reference from game config.
The Scalar API reference is used at runtime for serving /docs endpoint.
Moving from devDependencies ensures it's available in production builds.
Use Math.imul for proper 32-bit multiplication and divide by 0x80000000
to ensure the result is in [0, 1) range. Fixes potential off-by-one
errors in randomInt() when random() returned exactly 1.0.
- WorldPackLoader: align default zone bounds with ZoneSystem
- Minimap: change mapHeight from 1664 to 2048 (64x64 tiles)
- TileInterpreter: fix tileTypes order (west before east) and add decoration
- world.test.ts: update MAP_HEIGHT to 64, fix zone positions to match
  new plaza (1024,1024), north-block (960,200), west-block (300,1000)
- worldpack-unified-map.test.ts: replace duplicate 'plaza' with 'lake',
  update all zone bounds and height assertions to 64x64 spec
pnpm/action-setup@v4 now auto-detects version from packageManager
field in package.json. Specifying both causes version conflict error.
CodeQL autobuild requires Node.js to be available on PATH for
TypeScript extraction. Adding pnpm and Node.js setup steps.
codelytv/pr-size-labeler is a container action that only supports
Linux runners. Moving size job from macOS self-hosted to ubuntu.
Sync lockfile with previous commit that moved @scalar/express-api-reference
from devDependencies to dependencies in server package.
These workflows are resource-intensive and only need to run on main branch
pushes. PRs are validated by the main CI workflow (typecheck, lint, test).
These tests were based on old 64x52 map spec and had assertions
for 8 NPCs when only 6 exist. Zone bounds and NPC/facility assignments
are already validated by zone-system-64x64.test.ts and world-pack.test.ts.
These workflows are not needed for PR validation. Docker builds and E2E
tests can be run manually or added back when infrastructure is ready.
Ignore:
- .sisyphus/ (work plans and evidence)
- assets/extracted/, assets/source/ (generated assets)
- packages/client/public/assets/extracted/
- Root-level PNG files (test screenshots)
- convert_to_tiled.py: Convert map data to Tiled JSON format
- generate_road_map.py: Generate road network from map image
- render_map_with_tiles.py: Render map with tile overlays
- visualize_roads.py: Visualize road data
- extract_assets/: Asset extraction utilities
Add individual zone maps from previous 64x52 layout for reference:
- lobby.json, office.json, meeting-center.json
- lounge-cafe.json, arcade.json
- packages/server/assets/maps/village.json
Delete tracked PNG files that were used for testing:
- game-explore-*.png
- game-state-*.png
- typecheck: add self-build step to avoid build dependency bottleneck
- test: depend on build job and download artifacts
- client: add code splitting for phaser/colyseus chunks
- client: adjust chunkSizeWarningLimit for large game engine
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants