# 🗺️ AOD2 — แผนที่ "อยากทำอะไร ไปที่ไหน" (แผ่นเดียวจบ)

> อัปเดตล่าสุด: 2026-06-22 — เพิ่มอะไรใหม่ให้มาเติมตารางนี้เสมอ

## 📁 โครงโฟลเดอร์ Backend/ (จัดระเบียบแล้ว)

```
Backend/
├── *.php          ← API endpoints ที่เกมเรียก (+ db.php) — deploy ทั้งหมดไป aod2api/
├── admin/         ← Admin Dashboard (index.php ไฟล์เดียว, CSS ในตัว) — deploy เป็น aod2api/admin/
├── sql/           ← schema ทั้งหมด (import ใน phpMyAdmin — ไม่ต้อง deploy)
├── OPS.md         ← ไฟล์นี้
└── (config.php    ← อยู่บนเครื่อง server เท่านั้น ห้าม commit)
```

## 🖥️ Admin Dashboard (วิธีหลัก — ง่ายสุด)

เปิด **`http://<host>/aod2api/admin/`** (รหัสตั้งใน config.php: `'admin_pass' => '...'`)
→ ปิด/เปิดทั้งเกม, ปิดรายเซิร์ฟ, ดูสถานะ/ผู้เล่น, แก้ประกาศ/เวอร์ชันขั้นต่ำ ได้จากหน้าเดียว
(ตาราง phpMyAdmin ด้านล่างเป็นทางเลือก/สำรอง — dashboard แก้ค่าชุดเดียวกัน)
⚠️ ยังกดเปิด/ปิด server process จากเว็บไม่ได้ (รอ Agent) — ใช้ Build/*.bat

**หน้าย่อยใน admin (ลิงก์บน top bar):**
- **📬 ส่งจดหมาย** (`admin/mail.php`) — ส่ง mail รายคน (Character ID) / ทั้งหมด (global broadcast, shardId 0=ทั้งโลก) + ของแนบ (Gold/Diamond/Exp/Item ใส่ Item ID)
  - **ตรวจสอบในหน้าเดียวกัน**: รายการ "📢 ประกาศที่ส่งไปแล้ว" (+ลบได้) · "👤 จดหมายรายคนที่ส่งไปแล้ว" · "🔍 เช็คว่าตัวละครนี้จะได้รับไหม" (ใส่ characterId → บอกรายฉบับว่าได้/ไม่ได้ เพราะอะไร: หมดอายุ / คนละเซิร์ฟ / ส่งก่อนตัวละครถูกสร้าง / ผู้เล่นลบทิ้ง)
  - ⚠️ **ประกาศหมดอายุ = cron `mail_cleanup` ลบทิ้งถาวร** (ค่า default ในฟอร์ม = 3 วัน) — อยากให้อยู่ยาวตั้ง `0`
  - ⚠️ ผู้เล่นที่ออนไลน์อยู่ตอนส่ง **ไม่เด้งแจ้งเตือนสด** — เกมโหลดจดหมายตอนเข้าเกม + ตอนเปิดกล่องจดหมายเท่านั้น
- **🗑️ ลบตัวละคร** (`admin/character.php`) — ใส่ Character ID **หรือชื่อ** (หรือกด "เลือก" จากลิสต์ตัวละครล่าสุด) → เห็น preview ว่ามีข้อมูลอยู่ตารางไหนบ้าง → ยืนยันด้วยการพิมพ์ชื่อตัวละคร → **ลบครบทุกตารางในครั้งเดียว** (transaction): `characters` + `character_sections` + `leaderboards` (อันดับ) + `mail` + `global_mail_claim` + ปล่อยห้องดันที่จองค้าง · ติ๊กเพิ่มได้: ลบ account ด้วย (เฉพาะตอนไม่เหลือตัวละครอื่น — ใช้ตอนอยากสมัคร username เดิมซ้ำ)
  - ⚠️ ตัวละครที่ยังออนไลน์ ให้ออกจากเกมก่อนลบ ไม่งั้น autosave ของ server เขียนกลับเข้ามาใหม่
  - ★ **เพิ่มตารางใหม่ที่ผูกกับตัวละคร ต้องไปเพิ่มใน `$CHAR_TABLES` หัวไฟล์ `admin/character.php`** (นับ+ลบใช้ลิสต์เดียวกัน) ไม่งั้นลบแล้วเหลือข้อมูลผี
- **⏰ Cron** (`admin/cron.php`) — เปิด/ปิด job, ตั้งทุกกี่นาที, Run now (ตอนนี้มี job `mail_cleanup` ลบ mail หมดอายุ). tick เคาะจาก server heartbeat เอง ([[cron-system]]) — **ไม่ต้องตั้ง Task Scheduler**

## ⚙️ Ops รายวัน (แก้ใน phpMyAdmin — ไม่ต้องแตะไฟล์/build)

| อยากทำ | ไปที่ | แก้อะไร |
|---|---|---|
| 🔴 ปิดปรับปรุง "ทั้งเกม" | ตาราง `global_config` | `maintenanceAll` = `1` (เปิด = `0`) |
| 🏪 ดู/ลบประกาศในตลาด | ตาราง `market_listings` | `status`: 0=กำลังขาย 1=หมดเวลารอเอาคืน 2=จบแล้ว · `closeReason`: 1=ขายได้ 2=ยกเลิก 3=เอาคืน 4=ส่งจดหมาย · **ลบแถวตรงๆ = ของในประกาศหายถาวร** ใช้ตอนจำเป็นเท่านั้น |
| 🟠 ปิดเฉพาะเซิร์ฟ (Server 1/2) | ตาราง `server_maintenance` | `maintenance` = `1` ของ shardId นั้น |
| ℹ️ `server_status` | = heartbeat ล้วนๆ (เครื่องรันไหม/กี่คน) | **ไม่มี maintenance แล้ว** — ดูอย่างเดียว ไม่ต้องแก้ |
| 📢 ข้อความประกาศ (โชว์ตอนปิดเซิร์ฟ) | ตาราง `global_config` | `announcement` = ข้อความ |
| 🔢 บังคับผู้เล่นอัปเดตเกม | ตาราง `global_config` | `minClientVersion` = เช่น `1.2.0` (ว่าง = ไม่บังคับ) |
| 🔗 ลิงก์หน้าโหลดเกม | ตาราง `global_config` | `storeUrl` |
| 👤 ดู/แก้ข้อมูลผู้เล่น | ตาราง `characters` | คอลัมน์ `data` (JSON — **ค่าจริงอยู่ที่นี่** คอลัมน์อื่นเป็นสำเนาไว้ค้นหา) |
| 🏆 ดู/ล้างอันดับ | ตาราง `leaderboards` | ลบแถวตาม boardKey ได้ |

## 🖥️ เปิด/ปิด Server จริง (เครื่อง server — `e:\Game Develop\AOD2\Build\`)

| อยากทำ | ใช้อะไร |
|---|---|
| เปิด/ปิด/ดูสถานะ ทุก zone | `start-all.bat` / `stop-all.bat` / `status.bat` |
| คุมราย zone | `ServerManager.ps1 start|stop|restart|status <zone>` |
| เพิ่ม zone ใหม่ | แก้ `zones.json` (เพิ่มบรรทัดเดียว ไม่ต้องแก้สคริปต์) — **ชื่อ zone ต้องตรงกับชื่อฉากใน Build Settings เป๊ะ** และฉากนั้นต้องถูกติ๊กใน Build Settings ด้วย ไม่งั้น client วาร์ปไปแล้วต่อไม่ติด |
| ⚠️ zone ที่ชื่อมีเว้นวรรค (เช่น `3_Combat 2`) | ทำงานได้แล้ว (ServerManager ครอบ quote ให้ 2026-08-13) — **แต่แนะนำตั้งชื่อไม่มีเว้นวรรค** · อาการตอนพัง: log ขึ้น `โหลด zone: <คำแรกเท่านั้น>` + Photon ตอบ `A game with the specified id already exist` = โซนนั้นไม่มี server รันจริง |
| โหมดรัน server | `zones.json`: **`"headless": true`** = `-batchmode` (ไม่มีหน้าต่าง แก้ DXGI stutter — **ต้อง true เสมอ**). **`"nographics": true`** = `-nographics` (ตัด graphics device เต็มตัว = server จริงไม่มี GPU รันได้). **กฎเหล็ก: prefab ที่ SERVER เสก (Runner.Spawn) ห้ามมี component กราฟิก** (SpriteRenderer/Material/custom shader) ไม่งั้นโหลดบน -nographics **native crash** (ForceLoadFromInstanceID). วิธีถูก = แยก logic prefab (server) ออกจาก visual prefab (client) เช่น Arrow_Server(logic)/Arrow_Client(visual) — client ทุกตัวเสก visual เอง. **แก้ launch script อย่างเดียว ไม่ต้อง rebuild เกม — แค่ stop-all → start-all** |

⚠️ ลำดับปิดปรับปรุงเต็ม: ตั้ง `maintenanceAll=1` + ใส่ `announcement` → `stop-all.bat` → (ทำงาน) → start server → `maintenanceAll=0` → ลบประกาศ

## 🎮 ตั้งค่าในโปรเจกต์ Unity (แก้แล้วต้อง rebuild)

| อยากทำ | ไปที่ |
|---|---|
| เวลารีเซ็ตเควสรายวัน | `2_Scripts/Helper/ServerTime.cs` → `DAILY_RESET_HOUR` (**ของจริง = 0** เที่ยงคืน — เปลี่ยนเฉพาะตอนเทส แล้วเปลี่ยนกลับ!) |
| เวอร์ชันของ build | Player Settings → Version (ใช้เทียบกับ minClientVersion) |
| เพิ่ม/แก้เควส | `4_Data/Quests/` → QuestData SO (id อัตโนมัติ, เข้า database เอง) |
| เพิ่ม/แก้ไอเทม | ItemData SO (id อัตโนมัติ, เข้า ItemDatabase เอง) |
| รายชื่อเซิร์ฟที่ client เห็น | `2_Scripts/Persistence/ServerCatalog.cs` |
| URL/secret ของ backend ฝั่ง client | DataServiceBootstrap ในซีน 1_Login (+ Golden_village สำหรับ dev) |
| 🧃 **ยาอัตโนมัติ** | **มีหน้าตั้งค่าในเกมแล้ว** (ผู้เล่นตั้ง %HP/%MP เอง เซฟลง DB) · prefab ผู้เล่น → CharacterInventory เหลือแค่ "ความถี่ตรวจ" ที่เป็นค่าระบบ · ใช้ยาจากแถบ Hotkey เท่านั้น |
| 🤖 **Auto Farm** (รัศมีฟาร์ม / ต่างระดับ Y / ความถี่คิด / ตีเฉพาะมอน) | prefab ผู้เล่น → **PlayerAutoFarm** |
| 📏 ระยะโจมตี (ใช้ตัดสินว่า Auto เดินเข้าไปตีเมื่อไหร่) | ItemData → StatModifier `AttackRange` (ธนู/เวทตั้งไว้แล้ว) · ไม่ตั้ง = ระยะประชิด 1.5 · **ไม่โชว์ในกล่อง detail** |
| 🏪 **ตลาดผู้เล่น** (5 ช่อง / 2 ชม. / ค่าธรรมเนียม 10% / ขอบราคา 2x-10000x ของราคาขาย NPC) | `2_Scripts/Market/MarketRules.cs` — ค่าคงที่ที่เดียว (ฝั่งเกมเป็นคนตรวจกติกาทั้งหมด PHP แค่ดูแลความเป็นเจ้าของ+atomic) |
| 🪨 หินที่ใช้แทนอาวุธ/ชุดตอน Merge | ItemData → ติ๊ก **"หินแทนวัตถุดิบ 🪨"** (เฉพาะของในโฟลเดอร์ `Merge Stones`) · เพิ่มหินใหม่แล้วรันเมนู `AOD2/Tools/🪨 ตั้งหินแทนวัตถุดิบ` |

## 🔐 ไฟล์บนเครื่อง server (ไม่ commit)

| อะไร | ที่ไหน |
|---|---|
| รหัส DB + api_secret | `config.php` ใน folder ที่ deploy PHP (ห้าม commit!) |

## 🧪 เฉพาะตอน Dev (ห้ามหลุดไป production)

| ของ | ที่ไหน | สถานะ |
|---|---|---|
| กด G = เสกไอเทม | CharacterInventory (debugGiveItemID) | TEMP — ลบก่อนปล่อย |
| คลิก portal วาร์ป | ZonePortalClickTest | TEMP — ลบก่อนปล่อย |
| ชื่อ "Hero"+id ตอนรันตรง | CharacterPersistence fallback | TEMP |
| log `[Counter]` `[Quest]` | MonsterHealth / QuestState | TEMP — ลบก่อนปล่อย |
| **สลับตัวละครตอนรันตรง (เทสหลาย id)** | **DataServiceBootstrap → `devCharacterId`** (Inspector, ในซีนเกม) — เปลี่ยนเลข = สลับตัว · เข้าผ่าน Login จริงค่านี้โดนทับอัตโนมัติ | ตั้งใจ (dev สะดวก) |
| ⚠️ ใส่ id ที่ยังไม่มีใน DB (useRestApi=true) | load คืน `exists:false` = เล่นเป็นตัวใหม่ได้ แต่ตอน save จะ **INSERT row ใหม่ใน `characters` ที่ไม่ผูก account** → ไม่โผล่ในรายการตอน login (ขยะ DB) | เทสได้ แต่ควรใช้ id ที่มีจริง หรือใช้ Mock |
