TL;DR
- SimTower의 1993년 원작을 브라우저 멀티플레이어 게임으로 재구현하고, 같은 입력 시퀀스에서 매 틱마다 원작 바이너리와 바이트 단위로 동일한 상태를 만드는 것을 목표로 함.
- 시뮬레이션은 원작 Windows 3.1 바이너리와 틱 단위로 일치하도록 설계됐으며, 상태 머신과 엘리베이터 배차 코드도 정확히 맞추는 것을 목표로 함.
Cloudflare Workers와 타워별Durable Object가 입력 순서를 조정하고, 클라이언트와 서버가 같은 시뮬레이션을 입력 스트림에 맞춰 실행함.React 19,Vite,Phaser 4기반 클라이언트는 서버와 잠금 단계가 맞춰진 시뮬레이션을 로컬에서도 실행해 네트워크를 기다리지 않고 타워를 렌더링함.- 원작 바이너리에서 캡처한 플레이 기록을 틱마다 대조하는 추적 테스트를 제공하며, MIT 라이선스의 클린룸 재구현으로 원작 에셋이나 코드는 포함하지 않음.
프로젝트 소개
tower-together는Cloudflare Worker뒤에서 원작 시뮬레이션을 실행해 여러 명이 실시간으로 같은 타워를 건설하는 게임임.- 원작 SimTower는 사무실, 호텔 스위트, 콘도, 식당, 상점, 엘리베이터로 100층 타워를 건설하고, 입주민을 관리하다 파산하지 않는 것이 목표인 싱글플레이어 게임임.
- 충실도 목표는 엄격하며, 같은 입력 시퀀스에 대해 재구현판이 매 틱마다 원작 바이너리와 바이트 단위로 동일한 상태를 출력하는 것임.
apps/worker/src/sim/trace.test.ts의 추적 테스트는 에뮬레이션 환경에서 실행한 원작 DOS 바이너리의 플레이 기록을 재생하고, 매 틱마다 TypeScript 시뮬레이션의 상태가 원작과 일치하는지 확인함.- 실시간 게임 주소는
towers.world임.
아키텍처
apps/client—React 19+Vite+Phaser 4프런트엔드로, 로비, 타워 생성·참가, 타워 격자 캔버스 렌더링을 담당함. 클라이언트도 서버와 잠금 단계가 맞춰진 동일한 시뮬레이션을 실행하므로 렌더링이 네트워크 응답을 기다리지 않음.apps/worker— 타워마다TowerRoomDurable Object하나를 두는Cloudflare Workers백엔드임. Durable Object는 입력 순서의 기준 권한을 가지며, 클라이언트 입력을 받아 배치 단위로 정렬하고 모든 클라이언트가 동일한 입력 스트림으로 같은 시뮬레이션을 진행하도록 함.apps/worker/src/sim— 실행 환경과 독립적이며 헤드리스로 실행할 수 있도록 설계된 순수 TypeScript 시뮬레이션 코어임. 워커, 클라이언트, 테스트 스위트에서 동일하게 실행됨. 안내 문서는apps/worker/src/sim/AGENTS.md에 있음.specs/— 시간, 수요, 엘리베이터, 경로 설정, 경제, 사람 등 바이너리 동작을 역공학한 메모임. 내용은 일부만 정리됐고 항상 정확한 것은 아니며, 추적 테스트가 기준임.analysis-2825a3c53f/—SIMTOWER.EX_정적 분석용 Ghidra 프로젝트임.
실행 방법
- Node 20 이상과 npm 10이 필요하며, 최초 한 번
npm install로 의존성을 설치함. npm run dev를 실행하면 Turbo를 통해 클라이언트용 Vite 개발 서버와 워커용wrangler dev가 함께 시작됨. Vite가 출력하는 주소를 열면 됨.- 한쪽만 실행하려면 클라이언트는
npm run dev -- --filter=tower-together-client, 워커는npm run dev -- --filter=tower-together-worker를 사용함.
테스트
- 핵심 테스트는 추적 재생 테스트이며,
apps/worker디렉터리에서npx vitest run src/sim/trace.test.ts --testTimeout=30000으로 실행함. apps/worker/src/sim/fixtures의 각.jsonl파일에는 원작 바이너리에서 캡처한(tick, input, expected_state)튜플이 들어 있음. 테스트는 입력을TowerSim에 넣고 매 틱마다 생성된 상태를 바이너리의 상태와 비교함.- N번째 틱에서 차이가 발견되면 보통 더 이른 틱의 버그가 N번째 틱에 이르러 관측된다는 뜻이므로, 가장 먼저 발생한 차이부터 수정해야 함.
- 픽스처 재생성 방법은
simtower/emulator.py를 참고함. 원작SIMTOWER.EX_가 필요하며 저장소에는 포함되지 않음. - 그 밖의 유용한 명령은
npm run typecheck와node_modules/.bin/biome check .이며, 커밋 전에 둘 다 실행함.
배포
npm run deploy는 클라이언트를 워커의dist/에 빌드한 뒤wrangler로 워커를 배포함.- 클라이언트는 Durable Objects를 호스팅하는 동일한 워커에서 정적 에셋으로 제공됨.
라이선스
- 프로젝트 라이선스는 MIT임.
- SimTower의 저작권은 Maxis / OPeNBooK / Yoot Saito에 있으며, 이 프로젝트는 클린룸 재구현으로 원작 에셋이나 코드를 포함하지 않음.
댓글 (0)
로그인하면 이 기사에 내 생각을 남길 수 있어요