Files
vending-machine-game/README.md
T
HamedandClaude Opus 5.5 8cc0e8ce80 Rename the factory to Robot Market, fix GPS search and add GPS test points
- The factory company is now "شرکت روبات مارکت" on signs, dialogue,
  briefing, map label, manifest and README
- Real-world mode no longer shows "location not found" for every
  problem: it takes a quick coarse Wi-Fi/network fix first, keeps
  searching after a GPS timeout, and reports permission denied (1),
  location off (2), still searching (3) or a non-HTTPS page separately
- Three GPS test machines at real coordinates beside the factory, only
  present in real-world mode; the closest machine within reach is used
  and the arrival radius is twice the GPS error (12–40 m)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 11:23:22 +03:30

178 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# عملیات مشهد — روبات مارکت
بازی سه‌بعدی مرورگری (Three.js) با حال‌وهوای بازی‌های اکشن سوم‌شخص، بدون سلاح و خشونت. یک پدر و فرزندش از کارخانهٔ سازندهٔ دستگاه‌ها راه می‌افتند، در خیابان‌های واقعی مشهد می‌گردند، دستگاه‌های وندینگ روبات مارکت را پیدا می‌کنند و با هر خرید امتیاز می‌گیرند. روبات راهنمای روبات مارکت داخل دستگاه‌ها زندگی می‌کند: وقتی خانواده به یک دستگاه می‌رسند، از آن بیرون می‌آید، خوش‌آمد می‌گوید و راهنمایی‌شان می‌کند.
بازی یک PWA است: روی گوشی نصب می‌شود و آفلاین هم اجرا می‌شود.
🎮 [اجرای آنلاین بازی](https://hamedabdollahzade.github.io/vending-machine-game/)
## داستان و هدف
- **شروع:** سالن تولید کارخانهٔ **شرکت روبات مارکت** (بزرگراه مشهد–چناران، خیابان کاله، کنار پارک علم و فناوری خراسان). کارخانه سالن تولید با خط مونتاژ، دفتر، حیاط، دروازه و تابلوی شرکت دارد. ماشین خانواده در حیاط پارک است.
- **مسیر:** از کارخانه تا بوستان ملت حدود ۲۰ کیلومتر رانندگی است، از بزرگراه مشهد–چناران، بزرگراه پیامبر اعظم، بزرگراه آزادی و وکیل‌آباد.
- **نقشهٔ ایران:** صفحهٔ توجیه مأموریت نقشهٔ استان‌های ایران را نشان می‌دهد. فعلاً فقط مشهد (خراسان رضوی) باز است و شهرهای دیگر قفل‌اند.
- **محدودهٔ قابل بازی:** حدود ۱۵ در ۱۸ کیلومتر از شمال‌غرب تا غرب مشهد، همه از داده‌های واقعی OpenStreetMap:
- جزئیات کامل اطراف کارخانه و محدودهٔ بوستان ملت و دانشگاه فردوسی
- همهٔ خیابان‌ها و ساختمان‌ها در فاصلهٔ ۲۵۰ متری مسیر رانندگی
- شبکهٔ خیابان‌های اصلی در کل منطقه
- **دستگاه‌ها:** دستگاه نمونهٔ کارخانه (نصف قیمت)، بوستان ملت، مجموعه آموزشی راحیل (فقط ۷:۳۰ تا ۱۳:۳۰)، دانشگاه فردوسی و دانشکدهٔ ادبیات (۲۰٪ تخفیف دانشجویی)، دانشگاه علوم پزشکی، ایستگاه مترو آزادی، مشهد مال (از ساعت ۱۰). همهٔ دستگاه‌ها و محصولاتشان فرضی‌اند.
## کنترل
| کار | صفحه‌کلید | موبایل |
|---|---|---|
| حرکت / رانندگی | W A S D یا جهت‌ها | جوی‌استیک |
| دویدن | Shift | دکمهٔ «دویدن» |
| چرخاندن دوربین | ماوس (با کلیک قفل می‌شود) یا کشیدن | کشیدن روی صفحه |
| زوم دوربین | چرخ ماوس | — |
| سوار و پیاده شدن از ماشین | F | دکمهٔ F |
| خرید از دستگاه | E | دکمهٔ E |
| کشیدن کارت | C | دکمهٔ E |
| ترمز دستی | Space | — |
| نقشهٔ شهر | M یا کلیک روی نقشهٔ گوشه | لمس نقشه |
کنترل‌ها با صفحه‌کلید فارسی هم کار می‌کنند. دوربین فقط سوم‌شخص است.
## نقشه و مسیریابی
با کلیک روی نقشهٔ گرد گوشهٔ صفحه، نقشهٔ کامل شهر باز می‌شود. فهرست دستگاه‌ها با آدرس، ساعت کاری، باز یا بسته بودن، محصولات و فاصله نمایش داده می‌شود. «مسیریابی تا اینجا» مسیر را روی شبکهٔ واقعی خیابان‌ها پیدا می‌کند (پیاده روی همهٔ مسیرها، با ماشین فقط روی خیابان‌های سواره‌رو). مسیر به شکل خط طلایی روی زمین، روی نقشهٔ گوشه و روی قطب‌نمای بالای صفحه نمایش داده می‌شود. بدون انتخاب دستی، هدف مأموریت فعلی مقصد است.
## امتیاز
| رویداد | امتیاز |
|---|---|
| هر خرید | ۱۰۰ |
| اولین خرید در هر مکان (کشف) | ۲۵۰ |
| خرید نوشیدنی و خوراکی از یک دستگاه | ۱۵۰ |
| خرید از هر پنج نوع مکان شهری | ۵۰۰ |
| خرید در حالت دنیای واقعی | ۲۰۰ |
هفت مأموریت امتیاز و پاداش نقدی (هتومان) دارند. درجه‌ها از «تازه‌وارد» تا «افسانهٔ مشهد» بالا می‌روند. بودجهٔ اولیه ۵۰۰ هتومان است (۱۰ هتومان = ۱۰۰٬۰۰۰ ریال). وقتی پول کافی برای هیچ خریدی نماند، یا همهٔ مأموریت‌ها انجام شود، عملیات تمام می‌شود. هر ثانیهٔ واقعی ده ثانیه از ساعت بازی است.
## نصب روی گوشی (PWA)
- **اندروید و Chrome کامپیوتر:** دکمهٔ «نصب بازی روی گوشی» در صفحهٔ شروع، یا گزینهٔ Install در منوی مرورگر
- **آیفون:** در Safari، دکمهٔ Share و بعد Add to Home Screen. دکمهٔ نصب در بازی همین راهنما را نشان می‌دهد.
آیکون از آواتار روبات روبات مارکت ساخته شده (`public/icons`). بعد از اولین اجرا همهٔ فایل‌ها، از جمله نقشه و فونت Vazirmatn (داخل پروژه، بدون Google Fonts)، کش می‌شوند و بازی آفلاین اجرا می‌شود. با انتشار نسخهٔ جدید، پیام «نسخهٔ جدید بازی آماده است» نمایش داده می‌شود. Service Worker را `vite.config.js` در زمان build می‌سازد.
## حالت دنیای واقعی (GPS)
دکمهٔ «📍 بازی در دنیای واقعی» در صفحهٔ شروع یا «📍 دنیای واقعی» در نوار پایین این حالت را روشن می‌کند. اول نکته‌های ایمنی نمایش داده می‌شود و بعد اجازهٔ موقعیت و قطب‌نما گرفته می‌شود.
- خانواده روی نقشهٔ بازی همان‌جایی است که گوشی هست و با راه رفتن واقعی جابه‌جا می‌شود. جهتشان از قطب‌نمای گوشی یا جهت حرکت گرفته می‌شود.
- موقعیت‌های GPS بر اساس دقتشان نرم و ترکیب می‌شوند. «رسیدن» به دستگاه با شعاع ۲۰ تا ۴۰ متر (بسته به دقت GPS) سنجیده می‌شود. وقتی رسیدید، روبات از دستگاه بیرون می‌آید و خرید ۲۰۰ امتیاز اضافه دارد.
- در سرعت بیش از حدود ۲۲ کیلومتر بر ساعت خرید غیرفعال است. ماشین مجازی هم در این حالت استفاده نمی‌شود.
- بیرون از محدودهٔ بازی، فاصله تا نزدیک‌ترین دستگاه نمایش داده می‌شود.
- موقعیت فقط روی خود گوشی پردازش می‌شود. دوربین و قطب‌نما به HTTPS نیاز دارند.
- اول یک موقعیت سریع و تقریبی از Wi-Fi یا شبکهٔ موبایل گرفته می‌شود و بعد GPS دقیقش می‌کند. وضعیت زیر نام خیابان با کد خطا نمایش داده می‌شود:
- **کد ۱:** اجازهٔ Location داده نشده
- **کد ۲:** Location گوشی خاموش است
- **کد ۳:** هنوز سیگنال نرسیده (جست‌وجو ادامه دارد)
روی کامپیوتر معمولاً موقعیتی پیدا نمی‌شود، چون Chrome برای کامپیوتر از سرویس موقعیت گوگل استفاده می‌کند که از ایران در دسترس نیست. با گوشی تست کنید.
**نقطه‌های آزمایشی GPS:** سه دستگاه آزمایشی فقط در حالت دنیای واقعی کنار کارخانه ظاهر می‌شوند، در مختصات `36.450276, 59.423431`، `36.450172, 59.423209` و `36.449976, 59.422866`. مختصات، نقطه‌ای است که باید آنجا بایستید. با گوشی تا هر نقطه راه بروید: روبات بیرون می‌آید و دکمهٔ خرید فعال می‌شود. شعاع رسیدن دو برابر خطای GPS است (بین ۱۲ تا ۴۰ متر) و همیشه نزدیک‌ترین دستگاه انتخاب می‌شود.
فاز بعد، حالت دوربین (AR)، روی همین پایه ساخته می‌شود: تصویر زندهٔ دوربین به‌عنوان پس‌زمینه، و دستگاه و روبات در جهت و فاصلهٔ واقعی روی آن.
## اجرا
```sh
npm install
npm run dev
npm run build
node --test tests/*.test.js
```
- `?quality=low` یا دکمهٔ «گرافیک» سایه‌ها را خاموش می‌کند (پیش‌فرض گوشی‌ها).
- `?debug` وضعیت بازی را در `window.game` در دسترس می‌گذارد.
## انتشار روی سرور با Docker
تصویر Docker دو مرحله دارد: Node تست‌ها را اجرا و بازی را build می‌کند، و بعد Caddy فایل‌ها را سرو می‌کند. Caddy گواهی HTTPS را از Let's Encrypt خودکار می‌گیرد و تمدید می‌کند. HTTPS برای نصب روی گوشی و GPS ضروری است.
**پیش‌نیاز:**
- رکورد A دامنه‌های `robotmarkets.ir` و `www.robotmarkets.ir` به IP سرور اشاره کند.
- پورت‌های ۸۰ و ۴۴۳ سرور باز و آزاد باشند.
```sh
git clone git@github.com:hamedAbdollahzade/vending-machine-game.git && cd vending-machine-game
docker compose up -d --build
docker compose logs -f
```
- **دامنهٔ دیگر:** `SITE_ADDRESS="example.ir, www.example.ir" docker compose up -d --build`
- **به‌روزرسانی:** `git pull && docker compose up -d --build`. گواهی‌ها در volume `caddy_data` می‌مانند. بازی‌های نصب‌شده روی گوشی پیام نسخهٔ جدید را نشان می‌دهند.
- **اگر روی سرور از قبل Nginx یا پروکسی دیگری روی ۸۰ و ۴۴۳ هست:** در `docker-compose.yml` پورت‌ها را به `"8080:80"` تغییر دهید و `SITE_ADDRESS=":80"` بگذارید. بعد دامنه را در همان پروکسی به `127.0.0.1:8080` بفرستید. در این حالت HTTPS با همان پروکسی است.
**سرور داخل ایران:** Docker Hub و گاهی npm به IPهای ایران سرویس نمی‌دهند. دو راه وجود دارد:
1. **mirror داخلی برای Docker Hub:** در `/etc/docker/daemon.json` یک mirror تعریف کنید، مثلاً `{"registry-mirrors": ["https://docker.arvancloud.ir"]}`، و بعد `sudo systemctl restart docker`.
2. **ساخت روی کامپیوتر خودتان و انتقال تصویر آماده:**
```sh
docker compose build
docker save robot-market-game:latest | gzip > robot-market-game.tar.gz
scp robot-market-game.tar.gz docker-compose.yml user@server:~/game/
ssh user@server 'cd ~/game && gunzip -c robot-market-game.tar.gz | docker load && docker compose up -d --no-build'
```
## CI/CD روی Gitea
مخزن اصلی `ssh://git@git.my-rm.com:2222/Hamed/vending-machine-game.git` با نام `origin` است. GitHub با نام `github` باقی مانده. گردش‌کار [.gitea/workflows/ci-cd.yml](.gitea/workflows/ci-cd.yml) با هر push خودش اجرا می‌شود:
- **هر push و pull request:** نصب، تست‌ها و build
- **push به `main`:** بعد از قبولی تست‌ها، کد از راه SSH به سرور فرستاده می‌شود و `docker compose up -d --build` اجرا می‌شود.
**راه‌اندازی یک‌باره:**
1. در سرور، Docker و Docker Compose نصب باشد و کاربر SSH بتواند `docker` اجرا کند (عضو گروه `docker` یا root).
2. یک کلید SSH مخصوص انتشار بسازید و کلید عمومی را به `~/.ssh/authorized_keys` سرور اضافه کنید:
```sh
ssh-keygen -t ed25519 -N '' -f deploy_key -C gitea-deploy
ssh-copy-id -i deploy_key.pub user@server
```
3. در Gitea، بخش Settings → Actions → Secrets مخزن، این‌ها را تعریف کنید:
- `DEPLOY_HOST`: آدرس سرور
- `DEPLOY_SSH_KEY`: محتوای فایل `deploy_key`
- در صورت نیاز: `DEPLOY_USER` (پیش‌فرض root)، `DEPLOY_PORT` (پیش‌فرض ۲۲) و `DEPLOY_KNOWN_HOSTS` (خروجی `ssh-keyscan server`)
4. اختیاری، در بخش Variables:
- `DEPLOY_PATH`: پوشهٔ روی سرور
- `SITE_ADDRESS`: دامنه‌ها
- `HEALTH_URL`: مثلاً `https://robotmarkets.ir/`، تا بعد از انتشار بالا آمدن سایت چک شود
- `NPM_REGISTRY`: اگر runner یا سرور به `registry.npmjs.org` دسترسی ندارد (معمول در سرورهای ایران)، آدرس یک mirror در دسترس npm را اینجا بگذارید. همین مقدار برای build روی سرور هم استفاده می‌شود.
تا وقتی `DEPLOY_HOST` تعریف نشده، مرحلهٔ انتشار رد می‌شود و فقط تست و build اجرا می‌شوند. هر مرحله محدودیت زمانی دارد (تست ۱۵ دقیقه، انتشار ۳۰ دقیقه) تا runner هیچ‌وقت بی‌پایان معطل نماند.
## ساخت دوبارهٔ دادهٔ نقشه
`src/data/mellat.json` و `src/data/iran.json` از خروجی خام OpenStreetMap (Overpass API) و Natural Earth ساخته می‌شوند. کوئری‌ها در ابتدای اسکریپت آمده‌اند:
```sh
node scripts/build-map-data.mjs --osm city.json zone-factory.json corridor.json major.json --poi poi.json --provinces ne_10m_admin_1_states_provinces.geojson
```
چهار خروجی OSM (محدودهٔ شهر، محدودهٔ کارخانه، کریدور مسیر و خیابان‌های اصلی) با هم ادغام می‌شوند و عنصرهای تکراری حذف می‌شوند. خیابان‌ها هم در مرز منطقه بریده می‌شوند. برای افزودن محدودهٔ جدید، یک خروجی دیگر به `--osm` اضافه کنید و در صورت نیاز `REGION` را در اسکریپت بزرگ‌تر کنید.
## ساختار
- `src/main.js`: صحنه، دوربین سوم‌شخص، کنترل‌ها، رابط کاربری (HUD)، نقشه، خرید و حلقهٔ بازی
- `src/city.js`: ساخت شهر سه‌بعدی از داده‌ها (خیابان، پیاده‌رو، ساختمان، پارک، درخت، ورودی مترو، کوه‌ها) در تکه‌های ۲ کیلومتری که دورها پنهان می‌شوند
- `src/map-render.js`: نقشهٔ گوشه، نقشهٔ کامل شهر (کاشی‌های کش‌شده و نمای کلی) و نقشهٔ ایران
- `src/routing.js`: گراف خیابان‌ها، مسیریابی A* و پیدا کردن نام خیابان
- `src/collision.js`: برخورد با ساختمان‌ها برای شخصیت‌ها، ماشین و دوربین
- `src/locations.js`: دستگاه‌ها، آدرس‌ها، ساعت کاری و محصولات
- `src/game-state.js`، `src/payment.js`، `src/scoring.js`: پول، خرید، پرداخت، امتیاز، درجه و مأموریت‌ها
- `src/characters.js`: پدر و فرزند، و روبات راهنما که از دستگاه‌ها بیرون می‌آید
- `src/factory.js`: محوطهٔ کارخانه، شامل سالن تولید، خط مونتاژ، دفتر، حیاط و تابلوها
- `src/pwa.js` و `public/manifest.webmanifest`: نصب، کار آفلاین و پیام به‌روزرسانی
- `src/realworld.js`: GPS، قطب‌نما، نرم‌سازی موقعیت و شعاع رسیدن
- `src/vending.js`، `src/vehicle.js`، `src/traffic.js`: مدل دستگاه، فیزیک ماشین و ترافیک شهری
- `src/geo.js`: تبدیل طول و عرض جغرافیایی به مختصات بازی
## محدودیت‌ها و گام‌های بعدی
- مدل‌ها هندسی و سبک‌اند و کیفیت گرافیکی بازی‌های AAA را ندارند.
- خیابان‌ها یک‌طرفه و چراغ راهنمایی ندارند، و ترافیک فقط روی بلوارهای اصلی است.
- پیشنهادهای بعدی: امتیاز ایمنی (خط عابر و چراغ قرمز)، تاکسی و قطار شهری، چرخهٔ روز و شب، ذخیرهٔ پیشرفت، جدول امتیاز آنلاین، و محله‌ها و شهرهای دیگر.
نقشه: © مشارکت‌کنندگان OpenStreetMap (ODbL). مرز استان‌ها: Natural Earth.