🔔 Bladewake GDD
📄 Doc 50 · Kỹ thuật & dữ liệu

50 — Tech Architecture (kiến trúc kỹ thuật Unity 6)

kiến trúc Unity 6 để đội + agent AI dựng TOÀN BỘ game từ GDD này: bản đồ module, đường dữ liệu GDD JSON → Unity, lưu/di trú, hiệu năng 250 quái, tất định seed, runtime Tro Nhớ, Steamworks, input, loc, build/nhánh, công cụ debug, test/Autopilot, ngân sách theo máy, rủi ro.

Cập nhật: 2026-11-07
22
mục ##
36
mục ###
23
bảng
12 754
từ

1Giới thiệu & bản đồ tài liệu

Mục đích: kiến trúc Unity 6 để đội + agent AI dựng TOÀN BỘ game từ GDD này: bản đồ module, đường dữ liệu GDD JSON → Unity, lưu/di trú, hiệu năng 250 quái, tất định seed, runtime Tro Nhớ, Steamworks, input, loc, build/nhánh, công cụ debug, test/Autopilot, ngân sách theo máy, rủi ro. Phụ thuộc: 00-Design-Bible.md (INV-01..18, §4.1 mode, §10.4 hiệu năng/camera, §11 ID) + data/_bible.json; 01-Roadmap.md §5 (PlatformTuning, input, ngân sách máy yếu); 10-Combat-Core.md §1 (tick, toạ độ, hitbox), §7.4, §10 (trần); 14-Adaptive-AI.md §1, §7, §9–§10, §16; 15-Horde-Director-Encounters.md §1, §5; 16-Run-Structure-Map.md §10–§11; 31-VFX-Bible.md §2.4, §6, §8; 32-Audio-Bible.md §5, §11–§12; 51-Data-Schema.md §0–§3, §5, §8; digest code demo (Assets/_Project/Demo); rule code của dự án (code-style, core-system, data-persistence, third-party, compile-validation). Được dùng bởi: mọi lập trình viên + agent code (/run-backlog, /planning-system), QA (test, Autopilot, CI hiệu năng), build/release, agent dữ liệu (đường render §8), doc UI/HUD, doc Steam/marketing (thành tựu, leaderboard).

Độ dài từng mục (nghìn ký tự)Độ dài từng mục (nghìn ký tự)0. Phạm vi & thứ bậc0. Phạm vi & thứ bậc: 0.8k0.8k1. Hiện trạng demo → đích 1.01. Hiện trạng demo → đích 1.0: 2.1k2.1k2. Bản đồ module2. Bản đồ module: 6k6k3. Lõi mô phỏng (gameplay sim)3. Lõi mô phỏng (gameplay sim): 7.8k7.8k4. Kiến trúc hiệu năng cho 250 quái4. Kiến trúc hiệu năng cho 250 quái: 4.4k4.4k5. Ngân sách hiệu năng theo máy5. Ngân sách hiệu năng theo máy: 1.9k1.9k6. Tất định seed (DAILY, Truy Tuần, CU…6. Tất định seed (DAILY, Truy Tuần, CUSTOM): 6.7k6.7k7. Runtime Tro Nhớ (Bladewake.Ash)7. Runtime Tro Nhớ (Bladewake.Ash): 2.3k2.3k8. Đường dữ liệu (GDD JSON → Unity run…8. Đường dữ liệu (GDD JSON → Unity runtime): 2.7k2.7k9. Hệ lưu9. Hệ lưu: 7.6k7.6k10. Tích hợp Steamworks (Bladewake.Pla…10. Tích hợp Steamworks (Bladewake.Platform): 2.8k2.8k11. Lớp input (Bladewake.Input)11. Lớp input (Bladewake.Input): 2k2k12. Đường bản địa hoá (Bladewake.Loc)12. Đường bản địa hoá (Bladewake.Loc): 1.3k1.3k13. Runtime VFX & âm thanh13. Runtime VFX & âm thanh: 1.1k1.1k14. UI (Bladewake.UI)14. UI (Bladewake.UI): 1k1k15. Telemetry & báo crash (Bladewake.T…15. Telemetry & báo crash (Bladewake.Telemetry): 3.4k3.4k16. Build pipeline & nhánh16. Build pipeline & nhánh: 3.3k3.3k17. Công cụ debug & cheat (chỉ BW_DEV)17. Công cụ debug & cheat (chỉ BW_DEV): 1.2k1.2k18. Test tự động & Autopilot18. Test tự động & Autopilot: 2k2k19. Rủi ro kỹ thuật19. Rủi ro kỹ thuật: 1.7k1.7kQuy tắc cho dev/AIQuy tắc cho dev/AI: 2.1k2.1kQuyết định đã chốtQuyết định đã chốt: 4k4k
Bấm tên mục để nhảy tới mục đó.
Mục lục chi tiết (## và ###)
  1. 0. Phạm vi & thứ bậc
  2. 1. Hiện trạng demo → đích 1.0
  3. 2. Bản đồ module
  4. 2.1 Assembly (asmdef) & namespace
  5. 2.2 Luật phụ thuộc (CI kiểm bằng script đọc .asmdef)
  6. 2.3 Vòng frame & thứ tự trong tick (khớp doc 10 §1.1 + doc 14 §1.1 + doc 15 §1)
  7. 3. Lõi mô phỏng (gameplay sim)
  8. 3.1 Đồng hồ & bộ đếm tick (fixed-point)
  9. 3.2 Bố cục thực thể
  10. 3.3 Phân giải trúng (Combat)
  11. 3.4 Trạng thái STS_ (12, statuses.json)
  12. 3.5 Hero runtime
  13. 3.6 Hệ hiệu ứng (Edge, Cộng Hưởng, Tiến Hoá, Relic, affix, kỹ năng)
  14. 3.7 Kẻ địch & AI
  15. 3.8 Director
  16. 4. Kiến trúc hiệu năng cho 250 quái
  17. 4.1 Quyết định ECS
  18. 4.2 Pool & 0 cấp phát
  19. 4.3 Vẽ bầy (sprite batching)
  20. 4.4 Texture & bộ nhớ
  21. 4.5 Hạ cấp dưới tải (1 đồng hồ đo, 3 thang)
  22. 5. Ngân sách hiệu năng theo máy
  23. 5.1 Máy đích
  24. 5.2 CPU main thread (Deck, 250 quái + Ult + 1 Tinh Anh + MESO = kịch bản MS01b)
  25. 5.3 Bộ nhớ & đĩa
  26. 6. Tất định seed (DAILY, Truy Tuần, CUSTOM)
  27. 6.1 Phạm vi tất định (Bible §4.1, doc 16 §10)
  28. 6.2 Luồng RNG (Bladewake.Core.RngStreams)
  29. 6.3 Nộp điểm (mọi bảng; nguồn = doc 43 §6.2–§6.3, doc này chỉ chép giá trị để code)
  30. 7. Runtime Tro Nhớ (Bladewake.Ash)
  31. 7.1 Lấy mẫu PSP (A01–A12, doc 14 §7)
  32. 7.2 Lưu xuyên run
  33. 8. Đường dữ liệu (GDD JSON → Unity runtime)
  34. 8.1 Chuỗi bước
  35. 8.2 Hot reload (chỉ Editor + build BW_DEV)
  36. 8.3 Luật nạp
  37. 9. Hệ lưu
  38. 9.1 File & thư mục
  39. 9.2 Khối đầu, ghi nguyên tử & chịu lỗi (doc 43 §7.2)
  40. 9.3 Phiên bản, di trú & xung đột
  41. 9.4 Tạm dừng & thoát giữa run
  42. 10. Tích hợp Steamworks (Bladewake.Platform)
  43. 11. Lớp input (Bladewake.Input)
  44. 12. Đường bản địa hoá (Bladewake.Loc)
  45. 13. Runtime VFX & âm thanh
  46. 14. UI (Bladewake.UI)
  47. 15. Telemetry & báo crash (Bladewake.Telemetry)
  48. 16. Build pipeline & nhánh
  49. 16.1 Nhánh git (remote origin, nhánh gốc main)
  50. 16.2 Cấu hình build (Build Profiles Unity 6)
  51. 16.3 Chuỗi CI (máy build Windows tự host + 1 Steam Deck nối mạng)
  52. 17. Công cụ debug & cheat (chỉ BW_DEV)
  53. 18. Test tự động & Autopilot
  54. 18.1 Test (Unity Test Framework)
  55. 18.2 Autopilot (Bladewake.Dev.Autopilot, mở rộng bot demo)
  56. 19. Rủi ro kỹ thuật
  57. Quy tắc cho dev/AI
  58. Quyết định đã chốt

20. Phạm vi & thứ bậc

  • Doc này KHÔNG đặt số gameplay. Số gameplay ở GDD/Full/data/*.json; trần hiệu năng copy từ tuning.json caps + platform (nguồn: Bible §10.4, doc 10 §10, doc 15 §5, doc 31 §6, doc 32 §5.3). Lệch → doc luật thắng; sửa doc này.
  • Thứ bậc: Bible + _bible.json > doc 10–17, 20, 30–32, 51 > doc này > code. Code KHÔNG được là nguồn sự thật cho số/ID.
  • Engine khoá: Unity 6000.3.16f1 (dòng 6000.3 LTS tới 1.0; nâng dòng = mục DEC- + 1 sprint kiểm hiệu năng Deck). C# 9, .NET Standard 2.1, IL2CPP x64.
  • Nền tảng 1.0: Windows x64 (Steam), Steam Deck qua lớp tương thích của Steam (cùng build Windows). Console M36+, mobile cuối (doc 01 §3.11–3.12): kiến trúc chừa lớp IPlatformService, PlatformTuning, InputFrame, không code nhánh riêng trước cổng port.
  • Nhãn [ASSUMED] = số kỹ thuật lượt đầu, chốt ở MS01b (M3) bằng đo Deck.

31. Hiện trạng demo → đích 1.0

MảngDemo (Bladewake.Demo, 28 file ~405 KB)Đích 1.0Mốc
Khung1 Game MonoBehaviour + locator tĩnh GRunContext (thể hiện, không tĩnh) + SimWorld thuần C# + Presentation táchP0
HeroHero.cs 58 KB, cứng varekHeroBase + HeroKit dữ liệu + 12 IHeroVerbs mỏngP0 → MS02 (3 hero)
Nâng cấpswitch(effect) + 32 chỗ Prog.HasEffectRuntime trigger × primitive từ mechanics.json (46 × 186)P0
QuáiGameObject + SpriteRenderer mỗi conSoA NativeArray + Burst + vẽ instanced; M/L/XL GameObjectMS01b
AIchuỗi ai cứng, không thích nghiFSM archetype (S) + utility/đồ thị đòn (M/L/XL) + AshMemoryMS01b → MS03
Directorbảng sóng theo giờngân sách TP + EncounterRunner (wave_scripts.csv)MS02
Dữ liệuCSV tay GDD/Data → Resources/BW/Data, F5JSON → build_gdd.py → _unity/ → Bake SO + blob, hot reload devP0
Lưukhông (PlayerPrefs + CSV run)<steamId64>/save/ theo doc 43 §7.1: meta_<steamId>.json + run_active.json + ash_profile_<steamId>.json + settings_shared.json (Cloud) · local/settings_device.json (không Cloud), khối đầu, nguyên tử, di trúMS02
Âm24 nguồn round-robin, không mixerBW_Main.mixer + SfxPlayer 48 + MusicDirector 12 + HordeAudio (doc 32 §12)MS02
AssetResources, RGBA32, Varek ~120 MBAddressables theo hero/biome, trang chỉ mục RG8 + LUTMS01b
Inputđọc thiết bị trực tiếpBW_Input.inputactions → InputFrame, rebind, 5 bộ glyphMS02
SteamkhôngSteamPlatformService (thành tựu, stat, leaderboard, Cloud, Rich Presence, Deck)MS01 (trang) / MS02 (demo app)
UIuGUI dựng bằng code, Text legacy, chữ cứnguGUI + TMP + UIManager/FeatureBaseController, LocServiceMS02
RNGUnityEngine.Random không seedPCG32 10 luồng (doc 16 §10.1)P0

Template mobile trong Assets/_Project (quảng cáo, IAP, mediation quảng cáo, SDK analytics/push di động, LiveOps): gỡ khỏi build PC (asmdef defineConstraints + gỡ package khỏi manifest bản PC; CI kiểm build report 0 assembly quảng cáo/IAP/analytics di động — danh sách tên assembly cấm ở script CI). Giữ: UIManager, DataPlayer/PlayerDataManager, DataManager, TimeManager, GameSystems, EasyEventManager, UniTask, DOTween, bộ thuộc tính Inspector của template (chỉ Editor).

42. Bản đồ module

2.1 Assembly (asmdef) & namespace

Gốc mã: Assets/_Project/Bladewake/<Module>/; namespace Bladewake.<Module>; Editor ở <Module>/Editor/ (asmdef .Editor).

asmdefViệcPhụ thuộc được phépLớp chính
Bladewake.Coređồng hồ, tick, RNG, ID handle, toán sàn 3/4, pool, ring buffer, SimEventUnity.Mathematics, Collections, BurstSimClock, TickMath, Pcg32, RngStreams, IdRegistry, FloorMath, Pool<T>, SimEventBus
Bladewake.Datanạp _unity/*.csv + *.json, SO, blob runtime, tuning, hot reloadCoreDb, DataBaker, TuningTable, HeroKitSO, EnemyTableSO, ContentManifest
Bladewake.Simvòng tick, SimWorld, thứ tự hệ (§2.3)Core, Data, các module sim dướiSimWorld, SimLoop, RunContext
Bladewake.Combathitbox/hurtbox, spatial hash, phân giải trúng, sát thương, Thế, đẩy, trạng thái STS_, log sát thương 30 sCore, DataSpatialHash, HitResolver, DamagePipeline, StatusSystem, DamageLog
Bladewake.HeroesHeroBase, FSM, buffer, ĐÁP, verb giữ, Lướt, tài nguyên, Dư Âm (ULT_GAUGE), kỹ năng, ult, Dạng RènCore, Data, Combat, UpgradesHeroBase, HeroKit, MoveRunner, IHeroVerbs (H01…H12)
Bladewake.UpgradesEdge/RS/EVO/Relic/affix/Then Chốt, draft, mẫu câu thẻCore, Data, CombatEffectRuntime, TriggerBus, PrimitiveRegistry, DraftService, CardTextFormatter
Bladewake.EnemiesEnemyWorld SoA, archetype S, brain M/L/XL, token, vòng vây, flow field, đạn/vùng địch, boss phaCore, Data, CombatEnemyWorld, SmallBrainJob, HeavyBrain, AttackTokens, FlowField, BossScript
Bladewake.AshPSP A01–A12, W10/W20/ROOM/MESO/FIGHT, CV, Sổ Thù, Sẹo, ash_levelCore, DataAshMemory, PspSampler, CounterSelector, AshLedger
Bladewake.Directorngân sách TP, SpawnPlanner, EncounterRunner, mục tiêu trận, PerfGovernorCore, Data, Enemies, AshDirector, SpawnPlanner, EncounterRunner, ObjectiveSystem, PerfGovernor
Bladewake.Runrun state, bản đồ Hồi (MapGen), nút, thưởng, sạp, sự kiện, mode, Khăn Tang/mutator, tổng kếtCore, Data, Upgrades, DirectorRunState, MapGen, NodeRunner, ModeRules, MourningRules, RunSummary
Bladewake.Metahub, 8 trạm ST_*, Phục Dựng GD1–5, 4 tiền tệ, mở khoá, Thông Thạo MR01–20, codex, thoại NPCCore, Data, SaveMetaService, StationService, MasteryService, CodexService, DialogueRunner
Bladewake.Savehồ sơ, run dở, hồ sơ Tro Nhớ, ghi nguyên tử, di trúCoreSaveService, AtomicFile, MigrationChain
Bladewake.Presentationcamera pixel, vẽ bầy instanced, actor lớn, VFX runtime, số sát thương, rung, LUT paletteCore, Data, Sim (chỉ đọc)PixelCamera, CrowdRenderer, ActorView, FxRuntime, DmgTextRenderer, PaletteLut
Bladewake.AudioAudioDb, SfxPlayer, HordeAudio, TellAudio, MusicDirector, AudioSettingsService (doc 32 §12.1)Core, Data, Sim (chỉ đọc)như doc 32
Bladewake.UIHUD, màn hình, thẻ draft, bản đồ, codex, cài đặt, recap chếtCore, Data, Loc, Meta, Run (chỉ đọc + lệnh)HudController, *Controller : FeatureBaseController
Bladewake.InputInputActions → InputFrame, rebind, glyphCore, PlatformInputService, InputFrame, GlyphService
Bladewake.Locbảng chuỗi, mẫu {param}, font theo ngôn ngữCore, DataLocService, TemplateFormatter
Bladewake.PlatformSteamworks, Deck, Cloud, thành tựu, leaderboardCoreIPlatformService, SteamPlatformService, NullPlatformService
Bladewake.Telemetrysự kiện run/ash/perf, ghi NDJSON, gửi opt-inCoreTelemetryService, PerfMeter
Bladewake.Devoverlay F3, console, phím F, cheat, Autopilotmọi moduleDevConsole, DebugOverlay, Autopilot, BotProfile
Bladewake.Tests.EditMode / .PlayModetest §18mọi module—

2.2 Luật phụ thuộc (CI kiểm bằng script đọc .asmdef)

  • Sim không biết hình/âm/UI: Core, Data, Combat, Heroes, Upgrades, Enemies, Ash, Director, Run, Sim KHÔNG tham chiếu Presentation, Audio, UI, Platform, Telemetry, Dev. Sim chạy được -batchmode -nographics (Autopilot cân bằng §18).
  • Sim → hiển thị/âm/UI/telemetry chỉ qua SimEventBus (ring buffer struct, 0 cấp phát) đọc cuối frame. Sự kiện tần suất thấp (xong phòng, lên cấp, mở draft, đổi pha boss, hết run) đẩy thêm qua EasyEventManager với hằng EventName (rule third-party).
  • UI ra lệnh cho sim chỉ qua ICommand xếp hàng (chọn thẻ, mua sạp, chọn nút) áp ở ranh giới tick.
  • Bladewake.Dev có defineConstraints: ["BW_DEV"] → không có trong build phát hành.
  • Cấm trong sim (CI grep + analyzer): UnityEngine.Random, System.Random, DateTime.Now (dùng TimeManager), Time.deltaTime (dùng SimClock), Keyboard.current/Gamepad.current/Mouse.current ngoài Bladewake.Input, GameObject.Find, SetActive cho màn hình (dùng UIManager), async void, Coroutine (dùng UniTask).

2.3 Vòng frame & thứ tự trong tick (khớp doc 10 §1.1 + doc 14 §1.1 + doc 15 §1)

Update() mỗi frame hiển thị (Real dt):
  InputService.Poll()                 → InputFrame (dấu tick)
  acc += min(realDt, 4/60) ; n = 0
  while acc ≥ 1/60 ∧ n < 4:  SimWorld.Tick(); acc −= 1/60; n++      // > 4 tick → bỏ phần dư (doc 10 §1.1)
  Presentation.Sync()  → CrowdRenderer, ActorView, FxRuntime, DmgText, Camera (Real)
  HordeAudio.Flush() → SfxPlayer ; TellAudio ; MusicDirector (0.25 s Hero clock)
  UI.Refresh() (chỉ khi dirty) ; Telemetry.Flush() (cuối phòng) ; PerfMeter.Sample()

SimWorld.Tick() (1/60 s):
  1 InputFrame → buffer 9 tick (INV-07)            7 đạn (hero 300 · địch 12)
  2 Hero FSM (Hero clock) + MoveRunner             8 vùng/hazard (hero 24 · địch 16)
  3 SpatialHash.Build (Burst)                      9 StatusSystem.Tick (DoT 30 nhóm lệch pha)
  4 HitResolver: hitbox hero → quái (§3.3)         10 chết/rơi đồ/XP · EffectRuntime on_kill
  5 Perception.Snapshot · Director (mỗi 15 tick) · Tokens.BeginTick · FlowField (10 Hz) ·
    SmallBrainJob (LOD) + HeavyBrain · Tokens.Resolve · di chuyển + giãn cách (Burst)
  6 đòn quái chạm hero: Hero.Receive (doc 10 §7.4)  11 AshMemory.Record (+ Evaluate 4 Hz) · SimEventBus.Commit
  • Ví dụ bù tick: frame 40 ms → acc 2.4 tick → chạy 2, mang 0.4; frame 120 ms → cần 7.2 → chạy 4, bỏ 3.2 (không tua nhanh).
  • Hiển thị khoá 60 fps (PlatformTuning.target_fps): màn 60/120/180/240 Hz → vSyncCount = refresh/60; tần số lẻ (75, 144, 165 Hz) → vSyncCount = 0 + targetFrameRate = 60. Không nội suy hiển thị (1 tick = 1 frame).

53. Lõi mô phỏng (gameplay sim)

3.1 Đồng hồ & bộ đếm tick (fixed-point)

  • 3 đồng hồ World · Hero · Real (doc 10 §1.1). Mỗi tick sim: scale_q16[c] (65536 = 1.0) = min các nguồn chậm đang chạy (doc 10 §6.3; ngân sách chậm toàn cục 0.60 s/2.0 s do SlowBudget giữ).
  • Bộ đếm khung (startup/active/recovery, windup, i-frame, hồi chiêu, trạng thái) lưu Q16 tick (int): mỗi tick remain_q16 −= scale_q16[clock]; sự kiện bắn khi qua 0. Chuyển động: pos += vel × scale/60 (mượt trong slow-mo, không giật bậc).
  • Ví dụ: Né Hoàn Hảo World ×0.25 trong 0.35 s (21 tick). Quái windup còn 27 tick = 1.769.472 q16; 21 tick × 16.384 = 344.064 → còn 1.425.408 = 21.75 tick → glint 2 (7 tick trước va chạm) vẫn đúng hình, chỉ chậm.
  • Đổi giây → tick lúc NẠP dữ liệu (doc 10 §1.1): cửa sổ có lợi người chơi ceil(t×60 − 0.001), còn lại round(t×60); TickMath.FavorPlayer/Round; code không tự đổi lúc chạy.
  • Hitstop cục bộ (doc 10 §6.1): hitstop_q16 riêng từng actor (hero + mục tiêu); actor đang hitstop bỏ bước bộ đếm + chuyển động, đồng hồ vẫn chạy. Ngân sách hitstop hero 0.20 s/1.0 s ở HeroBase.
  • Draft: World ×0.15 + hero bất tử, tách ngân sách chậm; Director cũng chạy trên World nên chậm theo (doc 15 §1). Tạm dừng: SimLoop.Paused = không gọi Tick().

3.2 Bố cục thực thể

LoạiLưu trữTrần (tuning caps)Ghi chú
Quái SEnemyWorld SoA NativeArray, sức chứa 256, chỉ số slot ổn định, free-listalive_max 250 (Deck preset 180)pos, vel, radius, mass, hp, poise, state, archetype, attack timers, status mask, token, slot ring, anim frame, flags
Quái M/L/XLHeavyActor (class) + GameObject ActorView, ≤ 8 cùng lúcTinh Anh 2 (HUNT/MRN14: 3), mini 1, boss 1dùng chung SpatialHash qua slot ảo 256–263
HeroHeroBase (class), 1—hurtbox CIRCLE r7 + hộp màn 7×26 (doc 10 §1.4)
ĐạnProjectileWorld SoAhero 300 · địch 12quét swept LINE w = 2r khi đi > r/tick
Vùng/hazardZoneWorld SoAhero 24 · địch 16shape doc 10 §1.4, rehit_ticks
Rơi đồPickupWorld SoAshard XP 120, cầu máu 6gộp vào cái gần nhất khi đầy (doc 15 §5.1)
Xác animCorpseWorld SoA80thứ 81+ chỉ tan tro 0.2 s
  • ID chuỗi (Thây Tro EN_B1_01, A3 · COMBO_A H01_COMBO_A_3) → handle ushort qua IdRegistry lúc nạp; sim chỉ dùng handle; lưu file/telemetry/UI dùng chuỗi. Handle không bao giờ ghi ra đĩa.

3.3 Phân giải trúng (Combat)

  • SpatialHash ô 32 px (Bible §10.4), dựng lại mỗi tick bằng counting sort Burst: cellCount[] → prefix sum → cellItems[]. Phòng lớn nhất RM_* 960×1.080 px → 30×34 = 1.020 ô; bộ nhớ ~12 KB.
  • Hitbox hero (≤ 64/tick): AABB(shape) nở r_t_max 28 → duyệt ô → phép thử Minkowski đúng shape (CIRCLE, ARC, RECT, LINE, RING, ELLIPSE; doc 10 §1.4). Cặp thử ≤ 4.000/tick: chạm trần → hitbox còn lại xử lý tick sau (không bỏ). Ví dụ ARC r48: AABB 152×152 px → ≤ 6×6 = 36 ô; 250 quái dồn r100 → ~70 ứng viên/hitbox.
  • Khử trùng "1 hitbox trúng 1 mục tiêu 1 lần": bitset 264 bit (5 × ulong) theo instance hitbox, xoá khi hitbox hết; rehit_ticks → bộ đếm theo slot. 0 cấp phát (thay HashSet demo).
  • max_targets (48, ult 128) chọn theo khoảng cách tăng dần tới gốc đòn (tất định: hoà → slot nhỏ hơn).
  • Pipeline sát thương = doc 10 §3 (thứ tự cộng/nhân §2.3, làm tròn); DamagePipeline.Resolve(in HitDesc, ref Target) thuần, Burst-được cho DoT; mọi kết quả ghi DamageLog (ring 30 s, INV-16) + SimEventBus.Hit.
  • Đòn địch → hero: Hero.Receive(Strike) thứ tự cố định doc 10 §7.4 (bất tử → i-frame Lướt/Né Hoàn Hảo → i-frame trúng → ĐÁP 360° → verb giữ → trúng). Đạn: hộp màn quét liên tục.
  • Cùng tick: ưu tiên INV-07 Lướt > ĐÁP > Ult > Kỹ năng > Đánh; đòn hero xử lý trước đòn quái (bước 4 trước 6) → giết cùng tick với windup chạm = quái chết, không đòn.

3.4 Trạng thái STS_ (12, statuses.json)

  • SoA StatusWorld: tối đa 1.500 instance; mỗi quái ≤ 6 BUILD + 3 CONTROL (doc 10 §5.4). DoT tick 30 tick, nhóm pha = instance_id mod 30 → mỗi tick xử lý ~1/30 instance.
  • Phản ứng cặp (Nứt Nhiệt, Lan Độc, Hành Hình) chỉ khi EffectRuntime báo sở hữu SRS cặp; kiểm ở StatusSystem.Apply.
  • CONTROL lên L/XL đổi thành Thế (doc 10 §5.3) tại StatusSystem, không ở kỹ năng.

3.5 Hero runtime

  • HeroBase (chung 12 hero): di chuyển, hurtbox, buffer 9 tick, ĐÁP (cửa sổ gốc + answer_window_bonus_s + Thương Xót + Edge, kẹp ≥ 9 tick), Lướt (INV-12: i-frame 12 tick, ≥ 2 lượt, nạp 0.70 s), Hurt/Vỡ Thế, tài nguyên (RESOURCE doc 12 §2.2), Dư Âm ULT_GAUGE, ult (UL1/UL2 bất tử toàn clip, UL3 chỉ 0.5 s kích).
  • MoveRunner: đọc hero_moves_<Hxx>.csv (startup/active/recovery tick, shape, coef, hit_class, poise_dmg, flags, vfx_id, sfx_id) + ma trận huỷ doc 12 §3.2. Hướng hitbox khoá ở tick bắt đầu startup (cận chiến) / tick nhả (tầm xa) — doc 10 §1.6.
  • IHeroVerbs (1 class/hero, ≤ 400 dòng): CHỈ verb giữ + hành vi tài nguyên riêng + hook chữ ký; mọi số qua HeroKit. Kỹ năng/ult/Dạng Rèn = mechanic_key → PrimitiveRegistry (§3.6), không code riêng theo id.
  • Palette hero: slot hue (Bible INV-01b) → LUT hàng hero; skin = LUT (§4.4).

3.6 Hệ hiệu ứng (Edge, Cộng Hưởng, Tiến Hoá, Relic, affix, kỹ năng)

  • Từ vựng đóng mechanics.json: 46 trigger × 186 primitive. Build sinh MechanicIds.g.cs (enum TriggerId, PrimitiveId); test AllPrimitivesHaveHandler chặn merge khi primitive thiếu handler.
  • Hàng dữ liệu → EffectInstance {trigger, primitive, owner, rank, params(float[], đã chọn theo hạng)}; TriggerBus.Raise(TriggerId, in TriggerCtx) gọi handler theo thứ tự priority rồi id (tất định).
  • Trần trigger (tuning caps): icd_on_hit 0.1 s · on_kill_per_s 8 · icd_on_status 0.25 s · interval_min 0.5 s · blasts_per_frame 16 · đạn từ nâng cấp 120 · vùng 24 · triệu hồi 8. Chạm trần → bỏ kích, không xếp hàng.
  • action = CUSTOM_HOOK → params.tpl (TPL_Hxx_*) → class gắn [Hook("H01.wide_arc")], quét bằng reflection LÚC BUILD (sinh bảng HookTable.g.cs, không reflection lúc chạy, IL2CPP an toàn).
  • Chỉ số: StatBlock cộng/nhân theo doc 10 §2.3 (bể INC cộng, MORE nhân riêng, inc_soft 1.0, more_dmg 2.5); dựng lại khi build đổi (không mỗi tick).
  • Thẻ: CardTextFormatter = mẫu _vi/loc + params hạng hiện tại → chuỗi (doc 51 §3).

3.7 Kẻ địch & AI

  • S (FSM archetype, doc 14 §2): SmallBrainJob (Burst, IJobFor trên slot): mỗi tick cho quái trong r200 hoặc đang windup/strike; ngoài r200 → 10 Hz (PerfGovernor bậc 2: 5 Hz) phân nhóm slot mod 6. Tham số từ ai_archetypes.csv; roll qua rng_ai (§6) bằng khoá (slot, tick) → kết quả không phụ thuộc thứ tự worker.
  • Token (doc 14 §4, INV-08): AttackTokens chạy main thread sau job: yêu cầu xếp theo (điểm ưu tiên, slot) → cấp tất định; trần MELEE 4/2/0, PROJ 3 loạt + 12 đạn, P_FLOOR 3, P_TOTAL 5, HEAVY 1, OBJECTIVE 6; KHÔNG thay đổi bởi Khăn Tang/Ấn/mutator/Tro Nhớ.
  • Di chuyển: flow field lưới 16 px (flow_cell_px), 1 trường chung + 1 "rộng" L/XL, Burst, 10 Hz hoặc khi địa hình đổi (hố H11, gờ sụt); giãn cách khối bầy (INV-04, doc 10 §4.4) dùng SpatialHash, 2 vòng lặp Burst.
  • M/L/XL: HeavyBrain (managed, mỗi tick, không LOD): utility DECIDE + đồ thị đòn + chống lặp + pha (doc 14 §6); trọng số Tro Nhớ CHỈ qua AshMemory.Weights() (công thức §6.3 doc 14). Boss pha/kịch bản = dữ liệu bosses/<id>.json (phases[], attacks[], links); không id cứng trong code.
  • Perception: ring snapshot hero 0.25 s × 8 (2 s); brain chọn đòn đọc snapshot ≥ 0.25 s tuổi; KHÔNG đọc input/buffer/cooldown/HP/build (doc 14 §1.2) — biên dịch ép: brain chỉ nhận in HeroSnapshot, không tham chiếu HeroBase.
  • Mọi đòn địch mang adaptive_source BASE|ASH; đòn ASH thiếu default → DataBaker lỗi.

3.8 Director

  • Director.Tick() mỗi 0.25 s World (15 tick) theo doc 15 §1: ví TP, target = B_obj × phase × density_mult, SpawnPlanner.Pick/Place (MIX ⊕ composition_bias), beat kịch bản EncounterRunner từ wave_scripts.csv, ELITE/MINI/BOSS giữ chỗ TP, leash 600 px/3 s hoàn TP.
  • Spawn công bằng INV-14: ấn tro 0.6 s (không bao giờ hạ LOD), không trong r90, 0.8 s không windup.
  • Roll Director qua luồng spawn khoá (act, row, lane) + bộ đếm trong phòng.
  • PerfGovernor (doc 15 §5.3) đọc PerfMeter.p95(2 s); bậc 3 chỉ ra ở phòng sau; ghi telemetry perf_tier.

64. Kiến trúc hiệu năng cho 250 quái

4.1 Quyết định ECS

Phương ánƯuNhượcKết luận
Entities (ECS đầy đủ)tối đa 10k+ thực thể2D sprite + uGUI + công cụ pixelfx không hợp; đội 5 dev; đổi API giữa phiên bản; demo không dùngKhông
GameObject mỗi quái (demo)đơn giản250 × (GO + 2–3 renderer) = chi phí chính (digest §3); không BurstKhông cho S
Lai hướng dữ liệuSoA NativeArray + Burst/Jobs cho S, đạn, vùng, trạng thái, hash, flow field; vẽ instanced; M/L/XL + hero = GameObjectBible §10.4 đã khoá "Burst/Jobs mảng dữ liệu"; 250 nhỏ với Burst; giữ công cụ demoChọn
  • Package: com.unity.burst, com.unity.collections, com.unity.mathematics, com.unity.jobs (bản đi kèm 6000.3). Không com.unity.entities.
  • Job chỉ đọc/ghi NativeArray của chính world; Complete() trong cùng tick (không job kéo dài qua tick → không race với lưu/UI).

4.2 Pool & 0 cấp phát

PoolSức chứaCướp khi đầy
slot quái S256không (Director dừng ở alive_max)
HeavyActor + ActorView8không
đạn hero / địch300 / 12hero: cũ nhất; địch: không (chờ token)
vùng hero / địch24 / 16hero: cũ nhất; địch: không (token P)
instance VFX320 (S 160 · M 64 · L 24 · XL 4)cũ nhất cùng hạng, trừ tell (doc 31 §6.1)
số sát thương260 (1 mesh, 24 mới/tick)vòng ghi đè
AudioSource48 one-shot + 12 nhạc + 4 ambience (doc 32 §5.3)theo steal
instance trạng thái1.500thay cái sắp hết
  • Luật: trong giao tranh 0 B GC/frame (đo GC.Alloc profiler marker; CI §18). Cấm LINQ, foreach trên interface, closure, string.Format, boxing trong sim/presentation hot path; chuỗi HUD dùng StringBuilder cache + TMP SetCharArray.
  • Mọi pool tạo ở màn tải (DOCK/hub → run); không Instantiate trong phòng (trừ M/L/XL đã pre-warm theo roster Hồi).

4.3 Vẽ bầy (sprite batching)

  • Render pipeline: URP Universal Renderer (chuyển từ built-in RP ở P0, trước MS01b; 6 shader demo SpriteActor/SpriteXray/SpriteFlash/SpriteAdd/SpriteBasic/Zone viết lại HLSL unlit). Không đèn 2D, không post-process ngoài LUT màn + chớp.
  • Camera pixel (Bible §10.4): render vào RT 480×270 (16:9) hoặc 427×267 (16:10), filter Point, upscale nguyên ×4 / ×3 (1281×801 cắt 1 px); UI canvas riêng ở độ phân giải gốc. Toạ độ sàn → màn: screenX = x − camX, screenY = (z − camZ) × 0.5 + y (doc 10 §1.3), snap 1 px nghệ thuật.
  • CrowdRenderer: job Burst dựng buffer instance {pos_px, uvRect, page, flip, flashT, outlineFlags, lutRow, depth} → mỗi trang atlas 1 lệnh Graphics.RenderMeshInstanced (quad chung). Sort bằng depth: sprite pixel alpha 0/1 → alpha-test + ZWrite, depth = f(z_chân) → không sort CPU, gom lô tự do theo trang. FX cộng (SpriteAdd) + bán trong suốt vẽ sau, sort CPU (≤ 320).
  • 5 lớp VFX → sorting layer Unity đúng doc 31 §2.4 (FXBot, GamePlayLayer1/2, FxLayer, Prioritize, overlay). X-ray hero: pass stencil (hero ref 1, quái ref 2) ZTest Greater 55% (INV-09).
  • Mục tiêu draw call: quái S ≤ 1 lệnh/trang hoạt động (≤ 6), FX ≤ 12, sàn/tile ≤ 8, actor lớn ≤ 16, UI ≤ 30 → ≤ 80 draw call/frame [ASSUMED].
  • Off-screen: bỏ instance ngoài khung + 32 px; anim quái ngoài khung đứng hình ở PerfGovernor bậc 1.

4.4 Texture & bộ nhớ

  • Trang atlas 2048², màu chỉ mục: kênh R = chỉ số palette, G = mask điểm nhấn (INV-01d) → định dạng RG8 8 MB/trang (RGBA32 16 MB). Shader tra PaletteLut (256 × 64 hàng: hàng biome, hàng hero slot, alt tro-xương, skin) = palette LUT 1 pass (Bible §10.4).
  • Ngân sách: ≤ 21 trang thường trú (Bible §10.3) × 8 MB = 168 MB; hero đang chơi ≤ 8 trang (64 MB), bậc VFX 1.7/2.2 nạp lười (doc 31 §8).
  • Addressables: nhóm hero_<Hxx>, biome_<Bn>, boss_<id>, shared, ui, audio_<owner>, data, loc_<lang>; nạp hero ở chọn hero; nạp biome kế + dỡ biome cũ trong DOCK; "Thức Lại" hub → run ≤ 5 s (doc 01 §5.3).
  • Import (giữ luật demo): Point, không mip, không nén lossy, max 2048; UI 25 PPU, world 16 PPU (PixelArtImportRules.cs).

4.5 Hạ cấp dưới tải (1 đồng hồ đo, 3 thang)

PerfMeter: thời gian frame CPU (FrameTimingManager), p95 trượt 1 s và 2 s, số quái sống.

ThangChủVàoCắtKhông bao giờ cắt
VFX L0–L3doc 31 §6.2quái ≥ 180/220 hoặc p95 > 15.0/16.6/20 mshạt ×0.6/×0.35, trúng/chết mỗi frame, đuôi đạntell Y/P/N, chớp trắng, vòng chân, x-ray, ấn spawn, vùng đích đạn P
Director bậc 0–3doc 15 §5.3p95 > 16.6 ms 2/2/3 ssố phụ, VFX chết, AI ngoài r200 5 Hz, density_mult ×0.90, cap_count 180như trên + token, sát thương, va chạm
Âm thanhdoc 32 §5.3, §6voice > 56ảo hoá Pickup → Status → Ambience → EnemyCrowd29 voice dành chỗ (tell, ĐÁP, ult, nhạc)
  • Preset Deck khởi động ở VFX L1 + fx_particle_cap 1.050 (PlatformTuning).

75. Ngân sách hiệu năng theo máy

5.1 Máy đích

MáyCấu hìnhĐích
Steam Deck (trần thiết kế, doc 01 §5.3)APU 4 nhân/8 luồng, 16 GB chung, 1280×80060 fps, p95 ≤ 16.6 ms, CPU main ≤ 10 ms, 250 quái (preset Deck 180 tự hạ khi fps < 50 trong 10 s)
Desktop tối thiểu [ASSUMED]CPU 4 nhân/4 luồng ~2.5 GHz (đời 2015+), 8 GB RAM, GPU DX11 2 GB VRAM hoặc tích hợp đời 2018+, Windows 10 x64, 4 GB đĩanhư Deck, 1080p
Desktop khuyến nghị [ASSUMED]6 nhân, 16 GB, GPU rời 4 GB60 fps khoá, p99 ≤ 16.6 ms, 1440p/4K (upscale nguyên)

5.2 CPU main thread (Deck, 250 quái + Ult + 1 Tinh Anh + MESO = kịch bản MS01b)

HệmsHệms
Input + buffer + Hero FSM0.4Đạn + vùng + hazard0.4
SpatialHash + giãn cách (Burst)0.5Trạng thái (DoT lệch pha)0.4
HitResolver + DamagePipeline1.0Chết/rơi đồ/XP/EffectRuntime0.5
AI S (Burst) + token + flow field1.3AshMemory + Director (khấu hao)0.3
HeavyBrain M/L/XL0.4Tổng sim5.2
CrowdRenderer (job + submit)0.8FxRuntime + số sát thương1.0
Âm (HordeAudio, SfxPlayer, Music)0.4UI/HUD (chỉ khi dirty) + camera0.6
Tổng hiển thị2.8Dự phòng (GC 0, tick bù)2.0
  • Tổng ngân sách = 10.0 ms. Tick bù 2 tick/frame chỉ được phép < 5% frame (p95 vẫn ≤ 16.6 ms). Worker Burst chạy trên 3 nhân còn lại, ≤ 4 ms/frame.
  • GPU Deck ≤ 10 ms [ASSUMED]: RT nhỏ, chi phí chủ yếu overdraw FX cộng; ≤ 1 050 hạt (Deck, preset 70%); 1 500 desktop (tuning platform.fx_particle_cap).

5.3 Bộ nhớ & đĩa

Hạng mụcTrầnNguồn
RAM tiến trình≤ 3 GBdoc 01 §5.3 [ASSUMED]
Texture thường trú≤ 1 GB (atlas ≤ 168 MB + UI + tile)doc 01 §5.3
SFX thường trú/Hồi≤ 40 MB giải nén; nhạc streaming ≤ 12 luồng, đệm ≤ 6 MBdoc 32 §11.3
Cài đặt≤ 4 GB (âm ≤ 400 MB)doc 01 §5.3, doc 32 §11.3
Lưumeta_<steamId>.json ≤ 512 KB · run_active.json ≤ 256 KB · ash_profile_<steamId>.json ≤ 512 KB · settings_shared.json ≤ 32 KB · local/settings_device.json ≤ 16 KB · quota Cloud 64 MB / 40 file [ASSUMED]doc 43 §7.1, §9.1
Tảihub → run ≤ 5 s · DOCK → Hồi kế ≤ 3 s [ASSUMED] · boot → title ≤ 8 s trên Deck [ASSUMED]doc 01 §5.3

86. Tất định seed (DAILY, Truy Tuần, CUSTOM)

6.1 Phạm vi tất định (Bible §4.1, doc 16 §10)

  • Tất định = nội dung: hero, Dạng Rèn, ult, 2 kỹ năng khởi đầu, bản đồ 4 Hồi, icon thưởng, mục tiêu, hàng sạp, sự kiện, 3 Khăn Tang, 2 mutator, vector Tro Nhớ seed. KHÔNG replay tất định khung-theo-khung: luồng spawn/loot/ai lệch theo hành động người chơi; float trong sim được phép.
  • Hệ quả kỹ thuật: không cần lockstep, không cần float tất định giữa máy; CHỈ phần sinh nội dung phải giống hệt trên mọi máy (Windows, Deck, port sau).

6.2 Luồng RNG (Bladewake.Core.RngStreams)

seed64 (DAILY)  = SplitMix64(FNV1a64("BW:DAILY:" + yyyy-mm-dd(UTC) + ":" + dec(content_version)))   // vd "BW:DAILY:2028-01-15:7"
seed64 (WEEKLY) = SplitMix64(FNV1a64("BW:WEEKLY:" + yyyy-Www(ISO) + ":" + dec(content_version)))   // vd "BW:WEEKLY:2028-W02:7"
// dec(int) = số nguyên thập phân, không đệm 0, không dấu cách; FNV1a64 = offset 0xcbf29ce484222325, prime 0x100000001b3, trên byte UTF-8
// SplitMix64(x): z = x + 0x9E3779B97F4A7C15; z = (z ^ z>>30) × 0xBF58476D1CE4E5B9; z = (z ^ z>>27) × 0x94D049BB133111EB; trả z ^ z>>31 (mod 2^64)
seed64 (STANDARD) = 64 bit ngẫu nhiên lúc RUN_START ; CUSTOM = 16 ký tự hex người chơi nhập
k      = FNV1a64(UTF8("BW:" + stream + ":" + join(key, ",")))     // key = số nguyên thập phân
state  = SplitMix64(seed64 XOR k) ; inc = SplitMix64(k) OR 1
rng    = PCG32(state, inc)                                         // PCG-XSH-RR 32 bit
stream (tuning run.rng_streams)keyDùng
map · mutatoractMapGen.Generate(act, seed), mutator Hồi
reward · shop · eventact·100 + row·10 + laneicon thưởng, hàng sạp, sự kiện nút
draftact, draft_index_in_actthẻ draft (không phụ thuộc số con đã giết)
spawn · loot · aiact, row, lane (+ bộ đếm trong phòng)Director, rơi đồ, roll AI (rng_ai)
fxtự dochỉ hình ảnh/âm; luồng DUY NHẤT được dùng không khoá
  • Rút trong job Burst: Pcg32.Stateless(state, (uint)slot, (uint)tick) = băm (state, slot, tick) → kết quả không phụ thuộc thứ tự worker.
  • Chỉ số nguyên cho sinh nội dung: trọng số đổi thành int (×1000, làm tròn lúc nạp), roll NextBounded(totalWeight) không lệch; cấm float so sánh trong MapGen, draft, sạp, sự kiện, mutator. Chuỗi băm = UTF-8 không BOM.
  • Bộ đếm rút mỗi luồng lưu trong run_active.json (doc 16 §11); vào lại phòng sau crash → luồng spawn/loot của nút reset về đầu phòng → cùng kịch bản.
  • content_version = int đơn điệu (doc 43 §11.3; cùng số trong khối đầu save §9.2 + leaderboard details[9] int32). build_gdd.py ghi vào _unity/content_version.txt (1 dòng, số thập phân) chép từ GDD/Full/content_version.txt (1 số nguyên, người giữ dữ liệu tăng tay; bắt đầu 1 ở MS02 [ASSUMED]); CHỈ tăng khi dữ liệu gameplay đổi theo cách cần migration hoặc làm điểm không so được (luật doc 43 §11.3, kèm mục DEC-). Không tăng → seed DAILY/WEEKLY giữ nguyên. Máy chủ (§6.3) và client băm cùng chuỗi → cùng seed.
  • content_hash = giá trị riêng: crc32 của _merged.json, 8 hex chữ thường (vd "3fa91c07"), build_gdd.py ghi _unity/content_hash.txt; client tính lại lúc khởi động trên gói dữ liệu đã nạp. Dùng cho kiểm (e) doc 43 §6.3 (danh sách hash live) và cờ "nội dung đã sửa" doc 43 §16. KHÔNG đưa vào chuỗi seed.
  • Test vàng (§18.1): RngStreams.DailySeed("2028-01-15", 7) → chuỗi "BW:DAILY:2028-01-15:7" → FNV1a64 = 0xb3dfbbdb7003a1cf → seed64 = 0xa11f150d17864318.
  • Ngày UTC lấy từ TimeManager đồng bộ giờ máy chủ Steam (SteamUtils.GetServerRealTime), không giờ máy (chống chỉnh đồng hồ).
  • Ví dụ: draft Hồi II lần draft thứ 3 → key "BW:draft:2,3"; 2 người cùng seed chọn cùng thẻ trước đó → cùng 3 thẻ, dù giết khác số quái.

6.3 Nộp điểm (mọi bảng; nguồn = doc 43 §6.2–§6.3, doc này chỉ chép giá trị để code)

  • Mọi bảng Trusted (doc 43 §6.2): client KHÔNG ghi điểm, chỉ đọc (FindLeaderboard, DownloadLeaderboardEntries Global/AroundUser/Friends). Máy chủ ghi bằng Web API SetLeaderboardScore: KeepBest cho ENDLESS/BOSS_RUSH, ForceUpdate cho DAILY/WEEKLY. Khoá Web API publisher chỉ ở máy chủ, không bao giờ trong build.
  • Tên bảng (doc 43 §6.2): DAILY_<YYYYMMDD> · WEEKLY_<YYYY>W<ww> · ENDLESS_FLOOR + ENDLESS_FLOOR_H01 … _H12 · BOSS_RUSH_<YYYY>W<ww>; build qa/dev_leaderboard (define BW_DEV_LB) → bảng DEV_* tương ứng. STANDARD/CUSTOM không bảng.
  • Bước 1 — kết run tính điểm → RunSubmission {steam_id, board, seed64, daily_key, mode, hero, aspect, ult, do_tang, kills, perfect_answers, acts_cleared, seconds_played, tells_emitted_yellow, tells_emitted_purple, act_times_s[], alive_cap, content_hash, build_number, details[]}; alive_cap = 250 | 180 (trần thấp nhất đã áp trong run); details[] int32 theo doc 43 §6.2 ([9] = content_version int, [10] = build_number).
  • Bước 2 — vé GetAuthTicketForWebApi("bw-score") → POST HTTPS tới máy chủ điểm (URL trong config build; dịch vụ HTTP serverless của studio, skill backend-communication).
  • Bước 3 — máy chủ: AuthenticateUserTicket + quyền sở hữu app → tra content_hash → content_version → đọc seedcap/<daily_key>/<content_version> (CI daily_seed_bake, doc 43 §6.3 bước 0) → 6 kiểm: (a) điểm/giây mỗi Hồi ≤ score_per_s_cap[act] · (b) kills/phút ≤ spawn_max_per_min_250 hoặc _180 theo alive_cap · (c) mỗi act_times_s[i] ≥ act_min_s[i] · (d) perfect_answers ≤ tells_emitted_yellow + tells_emitted_purple · (e) content_hash thuộc danh sách hash live · (f) chưa có lượt cho daily_key/tuần.
  • Bước 4 — đạt → SetLeaderboardScore → client nhận accepted + hạng; trượt → held (hàng duyệt tay, UI "Điểm đang chờ duyệt"). Top 50 mỗi bảng duyệt tay mỗi ngày.
  • Bước 5 — ngoại tuyến/máy chủ lỗi: RunSubmission giữ trong local/score_queue/ (§9.1, ≤ 5 MB), thử lại khi khởi động + mỗi 5 phút ở hub + 10 s sau resume. Hạn (giờ máy chủ nhận, doc 19 §5.2): DAILY trước 02:00 UTC ngày kế ngày của daily_key; tuần trước 02:00 UTC thứ Hai sau tuần đó; quá hạn → expired, client xoá bài, báo "Đã quá hạn nộp".
  • Bước 7 — thưởng top 10% DAILY: client GET /v1/daily/result?key=<daily_key> lúc khởi động + mỗi lần vào hub; điểm ≥ cutoff và key chưa có trong meta.daily_rewards_claimed[] → +5 Nến Canh CUR_VIGIL_CANDLE + thêm key trong CÙNG một lần ghi nguyên tử meta_<steamId>.json (§9.2).
  • Điểm DAILY/WEEKLY = (kills + 50 × perfect_answers + 2000 × acts_cleared) × (1 + 0.05 × Độ Tang) − seconds_played, sàn 0 (Bible §4.1); client tính để hiện, máy chủ là nguồn. ENDLESS = floor × 1 000 000 + (999 999 − min(999 999, giây chơi)); BOSS_RUSH = tổng ms 6 trận (doc 43 §6.2).
  • Khoá nộp (không gửi): CUSTOM · Thương Xót bật bất kỳ mục · content_hash lệch · chế độ ngoại tuyến không Steam (§10). Lượt DAILY/tuần bị tiêu khi bắt đầu run (daily_key ghi vào run_active.json + meta).

97. Runtime Tro Nhớ (Bladewake.Ash)

7.1 Lấy mẫu PSP (A01–A12, doc 14 §7)

  • Nguồn = SimEventBus cuối tick: TellResolved{answer|hold|dash|hit}, HeroDamage{dist, targets, dmg_kind}, DashStart{dir, t_to_impact}, InputPress{kind}, vị trí hero (ô 4×4 sân), EnemyLifespan{role, t_visible}, BrokenWindow{burst}. Chỉ ghi khi nguồn trong vùng an toàn 427×267 + 16 px.
  • Thùng 0.25 s = 15 tick. Kho SoA [axis][bin] 2 số num, den (+ nhánh A01 DEFLECT/HOLD/DASH): W10 40 thùng, W20 80 thùng, tổng chạy cộng thùng mới / trừ thùng hết hạn (O(1)/tick); ROOM cộng dồn; FIGHT cộng dồn trận boss Hồi.
  • AshMemory.Read(axis) = snapshot ≥ 0.5 s tuổi: W20 bỏ 2 thùng mới nhất; MESO = bản chốt cuối phòng trước. Brain chỉ gọi Read/Weights, không chạm kho.
  • Cuối phòng: x_room hợp lệ khi mẫu ≥ min_room(axis) → MESO_n = 0.85 × MESO_{n−1} + 0.15 × x_room; thiếu mẫu → giữ MESO, không reset streak.
  • Evaluate() 4 Hz: MICRO (bật ≥ 5 s, quên 30 s); MESO cuối phòng (bật ≥ 2 phòng liền); MACRO đầu trận boss. Mọi so ngưỡng qua AshMemory.Threshold(hero, axis, dir, row) (on/off hiệu lực theo hero_baseline).
  • L_eff = clamp(L_mode − mask − mercy, 0, 3); = 0 khi run < 60 s (MRN13: 30 s), < 3 run đời, Thương Xót Tro Nhớ 0. Ngân sách CV đồng thời theo Hồi I 1 · II 2 · III–IV 3; đổi CV chỉ ở REST; báo ≥ 60 tick trước hiệu lực.
  • Công tắc pháp lý adaptive.legal_gate (Bible, FTO trước MS03): cờ dữ liệu → MACRO + Sẹo chỉ-thoại (thẻ/bark, không bật CV). Phải bật/tắt được không cần build mới (nằm trong tuning + ghi đè từ xa qua bản vá dữ liệu).
  • Chi phí: ~0.15 ms/tick Deck (12 trục × 2 cửa sổ, cộng trừ thùng); Evaluate 4 Hz ≤ 0.2 ms.

7.2 Lưu xuyên run

  • File save/ash_profile_<steamId>.json (doc 14 §9.1): profile_version, kit_version, heroes[Hxx].meso + streak + runs_played, ledger[Hxx][BS_B1..BS_B4] ring 5, scars[BS_B1..BS_B4] ≤ 2.
  • Ghi: chỉ STANDARD/ENDLESS + công tắc "Trí nhớ xuyên run" BẬT; ở điểm lưu (MAP, BOSS_GATE, đầu DOCK) khi MESO đổi; cuối trận boss ≥ 20 s (ledger); cuối run (age_runs += 1). DAILY/BOSS_RUSH/CUSTOM không ghi.
  • Đọc MACRO: w_k = r_rank(k) × 0.9^age_runs, bỏ bản ghi age_runs > 20, cần ≥ 2 bản ghi (doc 14 §9.2). Test vàng: 3 bản ghi 0.80/0.70/0.55, age 0/2/5 → 0.72.
  • kit_version khác → bỏ active_cv lạ, giữ axes. "Xoá Sổ Thù" trong Cài đặt: theo hero hoặc tất cả, 2 bước xác nhận, không xoá Sẹo.
  • Ghi nguyên tử + Steam Auto-Cloud như §9.3.

108. Đường dữ liệu (GDD JSON → Unity runtime)

8.1 Chuỗi bước

#BướcCông cụĐầu raCổng
1Sửa JSONagent/devGDD/Full/data/**/*.jsonschema tools/schema/*
2Kiểmpy -3 GDD/Full/tools/validate.py --all --banned-terms <ngoài repo>báo ERROR/WARN0 ERROR (CI chặn merge)
3Renderpy -3 GDD/Full/tools/build_gdd.py --only csvGDD/Full/data/_unity/*.csv + *.json (doc 51 §8) + content_version.txt + loc_source.csv (khoá <file>.<id>.<field>, doc 51 §3)không sửa tay bản render
4Đồng bộmenu Unity Bladewake/Sync GDD Data (thay bản demo)chép vào Assets/_Project/Data/Generated/ (Addressables nhóm data)file render được commit; CI so lệch render (build lại → git diff --exit-code)
5BakeDataBaker (Editor, chạy sau Sync + trước build)SO: HeroKitSO ×12, MoveTableSO, EnemyTableSO, BossSO, UpgradeTableSO, EncounterSO, RunMapSO, AudioDbSO, TuningSO, PlatformTuningSO + IdRegistrylỗi tham chiếu/handle → build fail
6CodegenDataBakerMechanicIds.g.cs, HookTable.g.cs, TuningKeys.g.cs, EventName.g.cs (sự kiện doc 32 §12.2)không sửa tay *.g.cs
7RuntimeDb (module của DataManager, chỉ đọc)blob struct (BlobArray trong NativeArray) cho Burst + SO cho managedDb.Ready trước màn title
  • Parser CSV giữ hợp đồng demo: header theo tên (thứ tự tự do, cột lạ bỏ qua, thiếu = mặc định), ngoặc kép "", # comment, bỏ BOM, số invariant, danh sách |; ô params = JSON 1 ô.
  • Tuning: TuningKeys.g.cs sinh hằng từ tuning.json → vd Db.T(TuningKeys.Caps.AliveMax) = caps.alive_max 250 (không chuỗi rơi). Lớp ghi đè nền tảng: PlatformTuning.csv cột nền tảng hiện tại → trống → DEFAULT (doc 01 §5.1); PC_KBM ↔ PC_PAD đổi sống theo thiết bị input cuối.
  • ContentManifest (SO trong Unity, KHÔNG phải dữ liệu GDD): danh sách id cho phép theo build (MS02: H01 H02 H07, Hồi I, 12 SE, 10 RL, STANDARD; cờ build trước U3 — Bible §10). Id ngoài manifest: không vào pool draft/sạp/codex/chọn hero; dữ liệu vẫn nạp (để lưu không vỡ).

8.2 Hot reload (chỉ Editor + build BW_DEV)

Loại đổiCáchÁp lúc
Số (tuning, coef, windup, TP, giá)F5 hoặc theo dõi file _unity/ (FileSystemWatcher, debounce 0.5 s) → parse lại → thay bảngranh giới tick (giữa 2 SimWorld.Tick)
Cấu trúc (id mới, đòn mới, pha boss, wave script)dev.reload --roomkhởi động lại phòng hiện tại, giữ build run
Asset (sheet, âm)Addressables "Use Asset Database" trong Editorlần phát kế
  • Hot reload không chạy ở build phát hành (dữ liệu nằm trong Addressables, khoá theo content_version). Hiển thị số dòng đã đổi + id lỗi trên overlay.

8.3 Luật nạp

  • Mọi id tham chiếu phải có trong IdRegistry; thiếu → Debug.LogError + từ chối dòng (build dev) / build fail (bake).
  • Số tick tính lúc nạp (§3.1). Mẫu câu thẻ kiểm {param} lúc bake (cùng luật validator).
  • Không Resources.Load cho dữ liệu/asset game (chỉ font khẩn cấp + màn lỗi).

119. Hệ lưu

Nguồn luật = doc 43 §7.1–§7.3 (tên file, cỡ trần, Cloud, khối đầu, xung đột); doc này chỉ thêm cách cài đặt. Lệch → doc 43 thắng.

9.1 File & thư mục

Gốc = Application.persistentDataPath/<steamId64>/ (LUÔN có thư mục steamId64, kể cả máy 1 tài khoản). Demo: thư mục save_demo/ thay save/ dưới cùng gốc (doc 43 §7.1; app riêng + productName riêng → Cloud riêng), cùng luật.

File (dưới gốc)Nội dungGhi khiCỡ trầnSteam Cloud
save/meta_<steamId>.jsonmodule DataPlayer: Meta (CUR_SOUL_ASH, CUR_ECHO_SHARD, CUR_VIGIL_CANDLE, UP_*, GD1–5, FLAG_*, END đã đạt, Lễ Vật, Thử Thách, mở khoá), Heroes (mở khoá, MR01–20, Dạng Rèn, ult, skin), Codex (CDX_* đã mở), Modes (lượt DAILY/Truy Tuần đã dùng theo daily_key, kỷ lục ENDLESS/BOSS_RUSH, daily_rewards_claimed[]), Stats (bộ đếm stat/thành tựu), achievements_pending[], active_run_idđiểm lưu run (MAP, BOSS_GATE, đầu DOCK), mua ở trạm hub, màn kết, thoát512 KBcó
save/run_active.jsonđúng danh sách doc 16 §11 (run_id, mode, seed64, daily_key, hero + loadout, Khăn Tang, act, bản đồ, HP, cấp/XP, kỹ năng/Edge/Relic/affix/RS/EVO + hạng, Dư Âm, Vàng Mã, Đổi/Xoá/Giữ, Lửa Gửi, đếm MRN14, bộ đếm luồng RNG, W20/MESO trong run, đồng hồ, thống kê)vào MAP (sau màn thưởng), BOSS_GATE, đầu DOCK; KHÔNG trong phòng256 KBcó
save/ash_profile_<steamId>.json§7.2 (PSP MESO + Sổ Thù + Sẹo, doc 14 §9.1)§7.2512 KBcó
save/settings_shared.jsonmodule Settings phần theo NGƯỜI (khoá S doc 42): remap bindings.<family>.<map>.<action_id>[0..1] (doc 41 §8.1), preset 1 tay, trợ năng, Thương Xót, ngôn ngữ, phụ đề, rung, cỡ chữ, âm lượng, đồng ý telemetry/crash + telemetry_id (§15)đóng Cài đặt, màn đồng ý32 KBcó
save/*.bakbản trước của mỗi file trênmỗi lần ghi= filecó
local/settings_device.jsonmodule Settings phần theo MÁY (khoá D doc 42): độ phân giải, preset đồ hoạ/FX, trần fps, vsync, chế độ cửa sổ, thiết bị âm, Vùng an toàn mànđóng Cài đặt16 KBkhông
local/cloud_marker.jsonsave_seq cuối máy này ghi cho từng file + device_idsau mỗi ghi save/4 KBkhông
Application.persistentDataPath/local/last_user.json (cấp MÁY, ngoài mọi gốc steamId64)steamId64 cuối đã đăng nhập (cho chế độ ngoại tuyến)sau SteamAPI_Init thành công1 KBkhông
local/score_queue/ · local/telemetry_queue/hàng chờ mạng (§6.3, §15)§6.3, §15≤ 5 MBkhông
local/crash/ · local/logs/ · local/conflict/ · local/ash_debug.csvdump, log, bản lưu bị loại (§9.3), debug Tro Nhớ§15, §9.3≤ 20 MBkhông
  • Template DataPlayer/PlayerDataManager: 5 module Meta/Heroes/Codex/Modes/Stats → 1 file meta_<steamId>.json; module Settings tách 2 file (settings_shared.json = khoá S, local/settings_device.json = khoá D doc 42). Thay storage provider mặc định bằng JsonFileProvider (§9.2); mặc định mọi field trong SetupDefaultData() (rule data-persistence); KHÔNG Save() trong Update — gọi SaveService.RequestSave(reason), gộp (debounce 2 s, trừ thoát/suspend = ngay).
  • Tiền meta gửi vào meta_<steamId>.json ở MỖI điểm lưu run (crash/mất Cloud tối đa mất 1 phòng, doc 16 §11).
  • Không có Steam (SteamAPI_Init thất bại, chế độ ngoại tuyến doc 43 §13): gốc = <steamId64> đọc từ local/last_user.json cấp máy; không có file → gốc Application.persistentDataPath/0/ (steamId = 0 trong tên file). Lần mở kế có Steam: 0/ có dữ liệu và <steamId64>/save/ RỖNG → chuyển 0/save/* vào <steamId64>/save/ (đổi tên meta_0.json → meta_<steamId>.json, ash_profile_0.json tương tự), xoá 0/ sau khi ghi nguyên tử xong; <steamId64>/save/ KHÔNG rỗng → áp luật xung đột 2 (§9.3) giữa bản 0/ và bản <steamId64>/, bản không chọn chép vào local/conflict/.
  • Quota Steamworks: 64 MB, 40 file / người [ASSUMED] (dùng thực ~3 MB, 8 file).

9.2 Khối đầu, ghi nguyên tử & chịu lỗi (doc 43 §7.2)

  • Mọi file save/ mở đầu bằng khối header = {schema_version (int), content_version (int, §6.2), save_seq (int, +1 mỗi lần ghi file đó), utc_written (ISO-8601 từ TimeManager), device_id (UUID ngẫu nhiên lưu ở local/cloud_marker.json, không phải mã phần cứng), checksum (CRC32 phần thân, 8 hex chữ thường)}; thân = dữ liệu module.
snapshot = Serialize(state)            // main thread, JSON UTF-8 không BOM; header.save_seq = marker[file] + 1; checksum = crc32(thân)
await UniTask.RunOnThreadPool:
  write save/x.json.tmp ; Flush(true) (fsync)
  File.Replace(x.json.tmp → x.json, backup x.json.bak)     // .tmp → fsync → file cũ thành .bak → .tmp thành file chính (nguyên tử trên Windows)
  cập nhật local/cloud_marker.json[x] = save_seq
Nạp: đọc x.json → checksum sai/JSON hỏng → luật 3 §9.3
  • Ghi tối đa 1 lần/2 s/file; hàng đợi 1 phần tử (bản mới thay bản chờ). Thoát (Application.quitting), mất focus, Deck suspend → ghi đồng bộ ngay nếu bẩn. Không ghi trong phòng chiến đấu.
  • Vượt cỡ trần §9.1 → log lỗi + vẫn ghi (không mất dữ liệu), CI test cỡ file với fixture "người chơi 1 000 giờ".

9.3 Phiên bản, di trú & xung đột

  • schema_version (int) mỗi file. MigrationChain: các bước M<from>_<to> trên cây JSON động (package JSON đi kèm Unity 6, kiểu cây; không giải tuần tự giữa chừng), chạy tuần tự tới bản hiện tại trước khi giải tuần tự. Mỗi bước có fixture Tests/SaveFixtures/v<n>/*.json + test EditMode (nạp → di trú → so kết quả mong đợi).
  • ID bất biến sau ship (doc 51 §2) → lưu chỉ bằng ID chuỗi. ID không còn trong dữ liệu: bỏ mục + hoàn tiền mở khoá đã trả (log save_drop), không crash.
  • Lệch kit_version Tro Nhớ → §7.2.
  • Luật xung đột (doc 43 §7.3, áp theo thứ tự):
#Tình huốngLuật
1Steam phát hiện xung đột Cloud trước khi chạyhộp thoại của Steam client quyết định; game không can thiệp
2file tải vào có save_seq < local/cloud_marker.json của máy nàymàn SaveConflict (doc 40 §2 #45): 2 thẻ {ngày giờ, giờ chơi, Tro Hồn, hero đã mở, Hồi xa nhất}; bản không chọn → local/conflict/<file>_<utc>.json (giữ 3 bản, 30 ngày)
3checksum hỏng / JSON lỗidùng .bak; .bak hỏng → chép file hỏng vào local/conflict/, SaveNotice "Bản lưu hỏng, đã khôi phục bản gần nhất" (meta: tạo mặc định) hoặc bỏ run dở (run)
4run_active.json có run_id ≠ meta.active_run_idbỏ run dở (tiền meta đã gửi giữ nguyên, doc 16 §11), báo 1 dòng
5header.content_version > build đang chạykhoá chỉ-đọc: không ghi đè, SaveNotice "Hãy cập nhật game"
6header.content_version < buildmigration tuần tự v → v+1; lỗi → giữ bản gốc ở local/conflict/, không ghi; run_active.json không có bước di trú run → hộp "Bỏ run" (tiền meta giữ)
7ash_profile lệch meta (khác máy)không gộp; dùng bản có save_seq cao hơn
  • Không bao giờ tự xoá dữ liệu Cloud. "Xoá toàn bộ tiến trình": xác nhận 2 lần (giữ nút 2 s) + chép bản sao vào local/conflict/.
  • Steam Auto-Cloud (cấu hình Steamworks): gốc WinAppDataLocalLow, thư mục con BladewakeDev/Bladewake/<steamId64>/save (biến {64BitSteamID} của Auto-Cloud), mẫu *.json + *.bak (không .tmp, không local/), quota 64 MB / 40 file [ASSUMED]. Demo: BladewakeDev/Bladewake Demo/<steamId64>/save_demo. Deck đồng bộ cùng luật vì cùng build Windows.
  • PlayerSettings.companyName = BladewakeDev, productName = Bladewake (bản Demo: productName = Bladewake Demo) — chuỗi chữ cố định, đặt ở P0 (dự án hiện còn DefaultCompany / G008_Bladewake) và khoá trước MS02 (demo đầu tiên có Cloud); đổi sau đó = đổi Application.persistentDataPath → mồ côi mọi save + file Cloud. CI kiểm 2 chuỗi này trong ProjectSettings.asset.

9.4 Tạm dừng & thoát giữa run

  • Mất focus / Deck suspend / overlay Steam mở → Menu tạm dừng (World + Hero dừng), không cần lưu (doc 16 §11).
  • Thoát giữa phòng (crash, mất điện) → lần mở sau chơi lại phòng từ đầu với trạng thái ở điểm lưu cuối; DAILY cộng seconds_played cả phần mất.
  • Bản build mới nạp run cũ: test di trú bắt buộc trong CI cho mọi schema_version đã ship.

1210. Tích hợp Steamworks (Bladewake.Platform)

  • Thư viện: wrapper C# mã nguồn mở cho Steamworks SDK, đóng gói UPM nội bộ, khoá phiên bản SDK theo build. Mọi gọi qua IPlatformService (doc 43 gọi IPlatformServices, cùng interface); SteamPlatformService · OfflinePlatformService (khi SteamAPI_Init lỗi, doc 43 §13) · NullPlatformService cho Editor/CI (không Steam) và nền port sau.
Tính năngCách làmDữ liệu
Khởi tạoSteamAPI_Init (KHÔNG gọi RestartAppIfNecessary, doc 43 §13); thành công → ghi local/last_user.json (§9.1); lỗi → OfflinePlatformService = chế độ ngoại tuyến doc 43 §13: lưu cục bộ cùng định dạng (gốc §9.1, không Cloud), thành tựu vào achievements_pending[] trong meta_<steamId>.json (đẩy lên Steam ở lần Init thành công kế), stats cộng cục bộ, leaderboard ẩn, rich presence tắt, DAILY = luyện tập (seed vẫn tất định, không nộp); không chặn chơiAppID 1.0 · AppID demo riêng (Next Fest MS02) · AppID playtest
Thành tựu (60 ACH_*)AchievementService nghe SimEventBus/EventName + bộ đếm Stats; SetAchievement(steam_api_name) → StoreStats NGAY (thành tựu mới được miễn trần 60 s, doc 43 §6.1)achievements.json (steam_api_name, hidden, mercy_blocked = không mở khi Thương Xót bật)
StatSetStat ở sự kiện; StoreStats CHỈ ở điểm lưu run (MAP, BOSS_GATE, DOCK), hub, màn kết, thoát game, tối đa 1 lần/60 s (doc 43 §6.1); không bao giờ trong vòng lặp frame; API name STAT_* doc 43 §6.1, đồng bộ với module Stats của meta_<steamId>.jsonsinh steam_stats.vdf từ achievements.json + bảng doc 43 §6.1 lúc build
Leaderboardmọi bảng Trusted, client chỉ đọc; máy chủ ghi (§6.3)tên bảng doc 43 §6.2: DAILY_<YYYYMMDD> · WEEKLY_<YYYY>W<ww> · ENDLESS_FLOOR / ENDLESS_FLOOR_H01…_H12 · BOSS_RUSH_<YYYY>W<ww> · DEV_* (build qa/dev_leaderboard)
CloudAuto-Cloud §9.3 (mẫu *.json + *.bak dưới <steamId64>/save)—
Rich Presencesteam_display = 1 trong 13 token doc 43 §9 (#Menu #Hub #Yard #Run #RunMourning #Boss #Finale #Secret #Daily #Weekly #Endless #BossRush #Custom) + token tham số #Hero_H01…_H12, #Biome_B1…_B4; khoá tham số hero, act (I–IV), biome, dotang, floor, fight; cập nhật chỉ khi đổi trạng thái, ≤ 1 lần/5 s; demo chỉ #Menu #Hub #Yard #Run #Bossfile token Rich Presence sinh từ LocService (1 khối/ngôn ngữ đã ship)
OverlayGameOverlayActivated_t → tạm dừng; mua/web không dùng—
Steam DeckSteamUtils.IsSteamRunningOnSteamDeck() → nền DECK (PlatformTuning, glyph Deck, ui_text_scale_min 1.25, VFX L1); bàn phím nổi ShowFloatingGamepadTextInput cho seed CUSTOM—
Giờ máy chủSteamUtils.GetServerRealTime → TimeManager (ngày DAILY)—
Xác thực máy chủGetAuthTicketForWebApi("bw-score") cho §6.3 (mọi bảng)—
  • Checklist Deck (Verified ở 1.0, Playable ở EA): cấu hình tay cầm mặc định, glyph Deck đúng mọi màn, chữ đọc được ở 1280×800, bàn phím ảo khi cần nhập, không launcher, 60 fps kịch bản MS01b, suspend/resume không mất run.

1311. Lớp input (Bladewake.Input)

  • 1 asset BW_Input.inputactions (Unity Input System 1.19), 3 map Gameplay · UI · Hub (doc 01 §5.2); bố cục nút khoá Bible §1.1 (Đánh X/LMB · ĐÁP-chạm/verb-giữ RB/RMB · Lướt A/Space · Kỹ năng LB·LT·RT / Q·E·F · Ult Y/R · Bình d-pad xuống/1 · Xem Tro Nhớ View/Tab · Bản đồ-tạm dừng d-pad lên·Menu / M·Esc · Tương tác B/G).
  • InputService.Poll() mỗi frame → InputFrame (struct): move (vec2), aim (vec2 sàn, 360° float), cờ pressed/held/released + tick dấu của 11 hành động, device (KBM/PAD/DECK), aimMode. Sim chỉ đọc InputFrame; Autopilot + test bơm InputFrame (không thiết bị).
  • Chạm/giữ RB/RMB: phân loại trong InputService theo ngưỡng tuning (doc 12 §1 sở hữu số); chạm = ĐÁP, giữ = verb giữ riêng hero (INV-11). Buffer 9 tick ở HeroBase (INV-07), không ở input.
  • Ngắm: tay cầm RS ≥ 0.30 → RS, không thì LS ≥ 0.20, không thì giữ (doc 10 §1.6); chuột → điểm sàn x = mouseX + camX, z = mouseY / 0.5 + camZ. Trợ ngắm theo aim_assist_cone_deg/aim_assist_strength (PlatformTuning).
  • Steam Input: mặc định chế độ giả lập tay cầm chuẩn (Input System đọc thiết bị ảo); xuất bản cấu hình Steam Input mặc định (mẫu "Gamepad" cho Deck; nút lưng Deck = L4 POTION · R4 ULT · L5 ASH_INSPECT · R5 INTERACT — bản sao của nút chính, không phải ô remap, doc 41 §8.4 / doc 43 §8.1; gyro tắt; trackpad phải = chuột chỉ trong menu). Khi Steam Input bật: GetInputTypeForHandle chọn bộ glyph; không bật: theo layout thiết bị của Input System.
  • Glyph 5 bộ: bàn phím/chuột · ABXY · ký hiệu hình học · Deck · chạm (mobile, cổng port). Đổi sống theo thiết bị cuối (GlyphService.OnDeviceChanged), HUD/thẻ đọc glyph qua thẻ <glyph=dash>.
  • Remap toàn bộ (PerformInteractiveRebinding), lưu trong save/settings_shared.json (§9.1) dưới khoá bindings.<family>.<map>.<action_id>[0..1] (doc 41 §8.1; 2 ô/action/họ thiết bị; theo người, đi Cloud); xung đột → hoán đổi; nút Menu/Esc không remap. Preset 1 tay + giữ/bật-tắt verb giữ ở Trợ năng.
  • CI grep cấm Keyboard.current|Gamepad.current|Mouse.current|Touchscreen.current ngoài Assets/_Project/Bladewake/Input/.

1412. Đường bản địa hoá (Bladewake.Loc)

KhoáLuật
Nguồnmọi field _vi trong JSON → loc_source.csv (key = <file>.<id>.<field>, cột vi, context, max_len, params) do build_gdd.py; chuỗi UI ngoài GDD → Assets/_Project/Localization/ui_strings.csv (cùng định dạng, khoá ui.<màn>.<slug>)
Ngôn ngữMS02: EN, VI · EA: EN VI zh-CN JA DE · 1.0: 10 (5 ngôn ngữ thêm chốt ở cổng loc 1.0)
Dịchchỉ dịch MẪU có {param}, không dịch câu đã thay số (doc 51 §3); bảng dịch loc_<lang>.csv (key, text) → Addressables loc_<lang>
RuntimeLocService.Get(key) → mẫu ngôn ngữ hiện tại → thiếu → EN → VI; TemplateFormatter thay {param} (+ placeholder toàn cục doc 51 §3), số theo văn hoá ngôn ngữ: % nguyên, giây ≤ 2 chữ số thập phân
FontLatin + tiếng Việt: font pixel phủ đủ dấu tiếng Việt (OFL, kiểm glyph tự động) · CJK: font pixel glyph ≥ 12 px, canvas UI riêng (Bible §10.3) · TMP font asset tĩnh cho Latin/VN, động cho CJK; chuỗi fallback theo ngôn ngữ
Kiểmpseudo-loc xx-PS (+40% độ dài + ký tự có dấu chồng, doc 40 §8 / doc 43 §12.2) chạy Autopilot chụp mọi màn; CI báo khoá thiếu/thừa, {param} lệch giữa ngôn ngữ, vượt max_len
Đóng băng4 tuần trước mỗi MS (Bible §10.3); chuỗi mới sau đó → bản vá
  • Cấm chuỗi hiển thị cứng trong code (CI grep chuỗi có dấu tiếng Việt trong *.cs ngoài Dev/).

1513. Runtime VFX & âm thanh

  • VFX (FxRuntime): id VFX_<owner>_<slug> → sheet Addressables + spec av/*.json (khung, fps 30, pivot, layer, size_class, phases). Phát bằng instanced sprite (cùng atlas/material SpriteAdd/SpriteActor), không ParticleSystem cho FX gameplay (doc 31 §6.1). Tint: TELL FIXED (#ffd84a/#b14cff), HERO = hàng LUT slot hero. Nâng hạng tất định: bậc sheet {1.0, 1.3, 1.7, 2.2}, k = argmin |ln(t/s_k)|, seed fnv1a32(VFX id) mod 2^16 (doc 31 §8). Trần + thứ tự cướp doc 31 §6.1; LOD §4.5.
  • Âm (doc 32 §12.1): AudioDb (từ audio_sfx.csv + audio_music.json) · SfxPlayer pool 48 · HordeAudio gộp hit/kill mỗi frame sau Combat · TellAudio lịch PlayScheduled (tell không bao giờ bị cắt) · MusicDirector cường độ I mỗi 0.25 s, stem lượng tử ô nhịp, 12 AudioSource xen kẽ dspTime · mixer BW_Main.mixer 17 group + 9 snapshot (doc 32 §5.1–5.2). Pitch World clock max(0.75, 0.5 + 0.5 × scale). Real Voices 64 / Virtual 256, DSP buffer 256 mẫu.
  • Gameplay chỉ bắn sự kiện (SimEventBus cho hit/kill/spawn/tell tần suất cao; EasyEventManager + EventName cho sự kiện thưa); không gọi AudioSource/FX trực tiếp từ sim.

1614. UI (Bladewake.UI)

  • uGUI + TextMeshPro; màn hình qua UIManager (không SetActive), controller kế thừa FeatureBaseController; DOTween UI SetUpdate(true), xử lý Kill/OnComplete (rule third-party).
  • Màn = enum UiScreen doc 40 §2 (#1–#40 + phần thêm #41–#46: PhotoWarning, QuickSetup, ChoosePath, HubPause, SaveConflict, SaveNotice); mỗi dòng = 1 prefab UI_<Name>.prefab + 1 controller <Name>Controller : FeatureBaseController. Chọn hero = tab Kẻ Thức của Hearth; Khăn Tang = TestBell; Codex = tab của Ledger; recap chết = DeathRecap (INV-16, từ DamageLog). Doc này không giữ danh sách màn riêng.
  • HUD canvas riêng neo vùng an toàn gameplay 427×267 (Bible §10.4); ui_text_scale_min theo nền; số sát thương ở lớp BattleDmgText (1 mesh, doc 10 §6.5) không thuộc uGUI.
  • Điều hướng tay cầm mọi màn (không màn nào cần chuột); focus mặc định khai báo trong controller; glyph qua GlyphService.
  • HUD cập nhật theo cờ dirty (máu, Dư Âm, hồi chiêu = 10 Hz; bộ đếm mục tiêu = khi đổi), không rebuild layout mỗi frame.

1715. Telemetry & báo crash (Bladewake.Telemetry)

Nguồn luật: danh mục sự kiện + vỏ chung = doc 60 §3 (tên canon); đồng ý, quyền riêng tư, crash = doc 43 §15. Doc này chỉ đặt cách cài đặt.

Sự kiện (tên canon doc 60 §3.3)Trường chínhNguồn luật
session_start · session_end · consent_changed · settings_changedbuild_number, content_version, nhánh, ngôn ngữ, is_deck, bậc CPU/GPU/RAM, lựa chọn đồng ýdoc 43 §15.2, doc 60 §3.3
run_start · run_state · run_endrun_id, mode, seed64/daily_key, hero, Dạng Rèn, ult, Độ Tang, MRN, MUT, kết quả, thời lượng, build cuốidoc 16 §9.3, doc 17 §11.1
room_start · room_endact, node, objective, thời lượng, sát thương nhận/gây, kills/phút, max alivedoc 15 §14, doc 17 §11.1
draft_offer · draft_pick · shop_purchase · event_choice · rest_choicethẻ hiện/chọn, món mua, giádoc 13 §16.2, doc 16, doc 17 §11.1
hero_diedlast5 (tên đòn, Y/P/N, giây, % sát thương, cv_id) + killing_blow + top_source + one_shotINV-16, doc 14 §15, doc 17 §11.1
ash_room · ash_cv_on/off · ash_budget_block · ash_spawn_shift · ash_hit · ash_fight · ash_death · ash_trick · ash_settingsdoc 14 §15doc 14 §15
perf_tier · perf_roombậc, alive, fx_active, p50/p95/p99 frame, voice thật, RAM, GC bytedoc 15 §5.3, §4.5
crash · bug_reportchữ ký stack, log_tail 2 000 dòng, 30 sự kiện cuốidoc 43 §15.3, doc 60 §3.3
  • Danh tính: mọi sự kiện mang telemetry_id = UUID v4 ngẫu nhiên tạo lần chạy đầu, lưu trong save/settings_shared.json (§9.1; "Tạo mã ẩn danh mới" ở Cài đặt › Quyền riêng tư sinh UUID mới). KHÔNG BAO GIỜ gửi steamId, băm của steamId, tên tài khoản, IP lưu lâu, đường dẫn thư mục người dùng, tên máy (doc 43 §15.1–15.2).
  • Ghi local: NDJSON local/telemetry_queue/<run_id>.ndjson (mọi build; không rời máy nên không cần đồng ý) → bản dev/QA đọc bằng balance_calc.py.
  • Gửi xa: chỉ khi người chơi chọn "Đồng ý" ở màn Consent (doc 43 §15.1: mặc định CHƯA chọn, 2 nút ngang nhau) hoặc build Playtest (kèm thông báo). Gửi theo lô ở hub / màn kết / thoát, không trong phòng chiến; gzip ≤ 50 KB/run (doc 43 §15.2, doc 60 Q3); hàng chờ local/telemetry_queue/ ≤ 5 MB, đầy → bỏ lô cũ nhất; endpoint HTTPS của studio (cùng hạ tầng máy chủ điểm §6.3, khác endpoint).
  • Crash (IL2CPP, gồm crash native):
    1. Bộ bắt crash của engine ghi minidump + Player.log vào thư mục crash theo dự án của engine (%LOCALAPPDATA%\Temp\<companyName>\<productName>\Crashes\); Application.logMessageReceivedThreaded ghi thêm local/logs/ (vòng 2 000 dòng) + vòng 30 sự kiện run cuối vào local/logs/last_events.ndjson (ghi đè mỗi 5 s, không trong vòng lặp frame).
    2. Lần mở kế: CrashCollector (trước màn Title) tìm thư mục crash mới hơn local/crash/last_seen → chép minidump + 2 000 dòng log cuối + build_number + content_version + content_hash + preset + 30 sự kiện cuối vào local/crash/<utc>/; xoá đường dẫn người dùng (C:\Users\<tên> → <USER>) và tên máy khỏi log trước khi lưu.
    3. Theo lựa chọn crash doc 43 §15.1: "Luôn gửi" → gửi ngay; "Hỏi mỗi lần" → hộp thoại "Game đã gặp lỗi lần trước. Gửi báo lỗi?" (Gửi / Không); "Không bao giờ" → không gửi.
    4. Gửi: POST /v1/crash (multipart: meta.json + log.txt + dump.dmp, nén ≤ 2 MB; vượt → bỏ dump, giữ log). Chưa gửi được → giữ local/crash/ 7 ngày rồi xoá. Máy chủ giữ 90 ngày, gộp theo chữ ký stack.
    5. Symbol IL2CPP (GameAssembly.pdb + il2cpp line mapping) lưu nội bộ theo build_number (doc 43 §11.1, không trong depot) để giải dump.
  • Demo Telemetry.cs (CSV bladewake_runs.csv) giữ ở build BW_DEV tới MS02 rồi bỏ.

1816. Build pipeline & nhánh

16.1 Nhánh git (remote origin, nhánh gốc main)

NhánhViệcLuật
maintrunk, luôn build xanhmerge qua review + CI §16.3; không push thẳng
feat/<id> · fix/<id>việc ngắn ≤ 5 ngàyrebase lên main; nhánh agent theo skill run-backlog
release/<ms> (vd release/ms02-demo, release/ms03-ea)cắt ở ngày đóng băng loc (4 tuần trước MS)chỉ sửa lỗi; mỗi sửa cherry-pick ngược về main
hotfix/<ver>vá sau phát hànhtừ tag v<x.y.z>; merge vào release/* + main
  • Tag: v<MAJOR.MINOR.PATCH>+<build_number> (vd v0.1.0+1842); build_number = số CI tăng đơn điệu toàn dự án (doc 43 §11.3), in ở góc màn title, watermark build qa/rc/dev_leaderboard, mọi telemetry + crash + leaderboard details[10].
  • Phiên bản (doc 43 §11.3): chuỗi hiển thị MAJOR.MINOR.PATCH (build_number); demo 0.9.x · EA MS03 0.1.0 · U1 0.2.0 · U2 0.3.0 · U3 0.4.0 · 1.0 1.0.0 · hero hậu 1.0 1.1.0, 1.2.0… · vá nóng tăng PATCH. PlayerSettings.bundleVersion = MAJOR.MINOR.PATCH; content_version (int) tách riêng (§6.2).
  • Git LFS: *.png, *.wav, *.ogg, *.psd, Audio_Src/** (doc 32 §11.1). Dữ liệu JSON/CSV không LFS.

16.2 Cấu hình build (Build Profiles Unity 6)

ProfileDefineNội dungĐích Steam
DevBW_DEV BW_STEAMđủ + overlay/console/cheat/Autopilot, hot reload, Development Build, nộp điểm vào DEV_*nhánh qa (mật khẩu, mỗi đêm, doc 43 §11.2)
PlaytestBW_STEAM BW_TELEMETRY_REMOTEContentManifest playtest, telemetry bật kèm thông báoAppID playtest
DemoBW_STEAM BW_DEMOMS02: H01 H02 H07, Hồi I, STANDARD, EN + VIAppID demo
ReleaseBW_STEAMContentManifest của MS hiện tạirc → beta (7–14 ngày trước bản lớn) → default (qua checklist doc 43 §11.4)
Release + BW_RC_WATERMARKBW_STEAM BW_RC_WATERMARKnhư Release + watermark góc "build_number + nhánh"; không cheatnhánh rc (mật khẩu)
Release + BW_DEV_LBBW_STEAM BW_DEV_LBnhư Release + watermark; nộp điểm vào bảng DEV_* (doc 43 §6.2)nhánh dev_leaderboard (mật khẩu)
  • Chung: Windows x64, IL2CPP, .NET Standard 2.1, Managed Stripping Medium (link.xml cho HookTable), Burst AOT, Addressables build trong pipeline, giữ PDB/symbol nội bộ cho crash.

16.3 Chuỗi CI (máy build Windows tự host + 1 Steam Deck nối mạng)

mỗi PR:      validate.py --all (0 ERROR) + từ cấm → build_gdd.py → so lệch render
             → credits_check: audio_sfx.csv × sfx-credits.json × licenses/ → Assets/_Project/Data/credits.json; thiếu dòng CC BY → FAIL (doc 40 §5.7.1, AT-08)
             → Sync + Bake (batchmode)
             → biên dịch → EditMode test → PlayMode smoke (3 phút Autopilot H01) → kiểm asmdef/grep cấm §2.2
mỗi đêm:     + build Dev + Playtest → Autopilot 12 hero × 1 run sim-only → báo cân bằng (CSV) → SteamPipe nhánh qa
hằng tuần:   + Deck: Autopilot + F10 (+150 quái) 10 phút, MS01b scene → p95 lùi > 10% → chặn merge (doc 01 §5.3)
release:     + build Release/Demo → test di trú lưu mọi schema đã ship → smoke Deck tay → SteamPipe nhánh `rc` → (checklist doc 43 §11.4) `beta`/`default`
  • Lệnh build: Unity -batchmode -quit -projectPath . -executeMethod Bladewake.Editor.BuildPipeline.Run -profile <Dev|Playtest|Demo|Release> [-define BW_RC_WATERMARK|BW_DEV_LB] -buildNumber <n>; upload: steamcmd +login <builder> +run_app_build app_build_<appid>.vdf (depot Windows x64).
  • Build report CI: kích thước cài đặt ≤ 4 GB, không assembly mobile (quảng cáo/IAP/analytics di động), không Bladewake.Dev ở Release/Demo.

1917. Công cụ debug & cheat (chỉ BW_DEV)

PhímViệc (giữ từ demo)PhímViệc
F1bật/tắt AutopilotF6+1 cấp (mở draft)
F3overlay: trang AI · BRAIN · PSP · PERF · DIRECTOR · HITBOXF7sinh Tinh Anh roster Hồi
F4bất tửF8vào boss Hồi
F5hot reload dữ liệu (§8.2)F9World ×0.25
F10+150 quái (stress)~console
  • Lệnh console: run.seed <hex> · run.goto <act> <row> · hero <Hxx> · give.edge <id> [rank] · give.relic <RLxxx> · give.cur <CUR_*> <n> · give.skill <Hxx_SKnn> [rank] · spawn <EN_/EL_/MB_/BS_id> [n] · mrn.set <MRNnn> <rank> · mut.set <MUTnn> · ash.set <axis> <v> · ash.force <cv_id> · ash.level <0-3> · ash.ledger.fake <boss> <n> · ash.ledger.clear · ash.trace on · ai.tokens <T> <n> · dir.budget <x> · perf.tier <0-3> · vfx.lod <0-3> · hitbox.show · save.dump · save.corrupt.test · loc.lang <code> · loc.pseudo · time.scale <x> · dev.reload --room.
  • Overlay HITBOX vẽ hình sàn hitbox/hurtbox 1 px (QA khớp VFX ↔ hitbox, doc 31 §7). PERF: p50/p95 frame, ms từng hệ (ProfilerMarker), alive, voice, hạt, sheet, draw call, GC/frame.
  • Cheat ghi cờ cheated = true vào run_active → không thành tựu, không leaderboard, không ghi Sổ Thù.

2018. Test tự động & Autopilot

18.1 Test (Unity Test Framework)

BộKiểmVí dụ test vàng
EditMode: TickMathceil/round đổi tick0.14 s → 9 tick; 0.12 s glint → 7
EditMode: DamagePipelinedoc 10 §3.6 ví dụ tínhsố khớp bảng doc 10
EditMode: HitShapes6 shape Minkowski + quétđiểm biên ± 0.01 px
EditMode: RngStreamsPCG32/FNV/SplitMix vector cố định; chuỗi seed DAILY1.000 số đầu của seed 0x5A17C3E1 khớp file vàng; DailySeed("2028-01-15", content_version 7) → "BW:DAILY:2028-01-15:7" → FNV1a64 0xb3dfbbdb7003a1cf → seed64 0xa11f150d17864318 (cùng giá trị ở test máy chủ điểm)
EditMode: MapGendoc 16 §2.5 hợp lệ 10.000 seed; doc 16 §2.7bản đồ Hồi I seed 0x5A17C3E1 = ví dụ doc 16
EditMode: AshMemoryEMA, ngưỡng hiệu lực, MACRO§7.2 → 0.72; H07 A02 on = 1.05 → không bật (doc 14 §7.3)
EditMode: Savenguyên tử, .bak, khối đầu (checksum), 7 luật xung đột §9.3, di trú mọi v<n>, chuyển 0/ → <steamId64>/fixture hỏng → nạp .bak; save_seq 380 < marker 410 → SaveConflict
EditMode: Datamọi CSV nạp, mọi id giải, AllPrimitivesHaveHandler, mẫu {param}0 lỗi
EditMode: Lockhoá đủ, {param} khớp giữa ngôn ngữ—
PlayMode: smokeboot → hub → run → 1 phòng → chết → recap → hub< 3 phút
PlayMode: InvariantsINV-02/03/08/11/12/14/15 đo runtime (windup ≥ sàn, token ≤ trần, ĐÁP 360°, i-frame 12 tick, ấn spawn 0.6 s, dịch trọng số ≤ trần)0 vi phạm trong 10 phút Autopilot
Hiệu năngGC.Alloc = 0 B/frame trạng thái ổn định; p95 theo §5.2Deck hằng tuần

18.2 Autopilot (Bladewake.Dev.Autopilot, mở rộng bot demo)

  • Chơi qua InputFrame từ Threat (không đọc sim nội bộ ngoài thứ người chơi thấy); BotProfile mỗi hero (12) × lối: DEFLECT, DASH, HOLD, AOE, RANGED, MASH → kéo trục PSP để thử Tro Nhớ.
  • Chế độ: showcase (quay clip marketing, render thật) · balance (sim-only -batchmode -nographics, N run → CSV cho balance_calc.py, doc 17 §11) · stress (F10, 250 quái) · soak (2 giờ, RAM tăng ≤ 50 MB) · daily (30 ngày seed → CRC nội dung, so giữa Windows và Deck) · loc (pseudo-loc, chụp mọi màn).
  • Tốc sim-only: ≥ 20× thời gian thực trên máy CI [ASSUMED] (1 run 32 phút ≈ 1.5 phút).

2119. Rủi ro kỹ thuật

#Rủi roXác suất · tác độngGiảm thiểuCổng
R1Deck trượt 10 ms CPU ở 250 quáiTB · caoSoA + Burst từ P0; MS01b đo sớm; dự phòng preset 180 (Bible §10.4)MS01b M3
R2Hệ hiệu ứng 186 primitive phình code/lỗi tương táccao · caotừ vựng đóng + test handler + trần trigger; CUSTOM_HOOK có templateP0
R3Chuyển URP làm lệch shader/stencil/x-rayTB · TBchuyển ở P0 trước nội dung; ảnh vàng so pixel 8 hướngP0
R4Hỏng lưu / xung đột Cloudthấp · caoghi nguyên tử + .bak + crc + save_seq + test di trúMS02
R5Nội dung DAILY lệch giữa máyTB · TBchỉ số nguyên trong sinh nội dung, content_version trong seed, test CRC chéo máyMS03
R6Font pixel thiếu dấu tiếng Việt / CJKcao · TBchọn font OFL phủ VN ở P0 (font demo chỉ Latin cơ bản — cần kiểm); CJK font riêng ≥ 12 pxMS02 / MS03
R7Glyph sai khi Steam Input bật trên DeckTB · TBGetInputTypeForHandle; test tay trên Deck mỗi MSMS03
R8Tro Nhớ bị cổng pháp lý chặnTB · caocờ adaptive.legal_gate đổi bằng dữ liệu → chỉ-thoạitrước MS03
R9Bộ nhớ asset 12 hero × 8 trangTB · TBRG8 + LUT, Addressables nạp theo hero/biome, ≤ 21 trang thường trúMS01b
R10Hot reload làm lệch trạng thái runTB · thấpáp ở ranh giới tick; cấu trúc = khởi động lại phòngP0
R11GC giật trong bầyTB · cao0 B/frame là cổng CI; pool pre-warmMS01b
R12Nâng Unity giữa dự ánthấp · caokhoá 6000.3 LTS; nâng = DEC- + sprint đo Deck—
R13Gian lận leaderboardTB · TBmọi bảng Trusted + máy chủ 6 kiểm (a)–(f) doc 43 §6.3 + seedcap CI + duyệt top 50MS03
R14Quyền riêng tư telemetrythấp · caođồng ý rõ (doc 43 §15.1), telemetry_id UUID ngẫu nhiên (không suy từ steamId), xoá đường dẫn/tên máy khỏi log crashMS02
R152 trần voice lệch (doc 15 §5.1 ghi 24, doc 32 §5.3 ghi 64 Real)— · thấptheo doc 32 (chủ mixer): 64 Real, pool 48 one-shot; doc 15 cần sửa dòng SFXđã ghi Quyết định

22Quy tắc cho dev/AI

  • Số/ID chỉ từ dữ liệu (Db, TuningKeys.g.cs, IdRegistry); không hard-code id quái/đòn/hero/Edge trong code (thay "thrall", "BS01_B1", CODE="varek" của demo). Thêm cơ chế = sửa mechanics.json + doc 12 §9–§10 trước, rồi handler.
  • Sim (Core…Run, Sim) không tham chiếu Presentation/Audio/UI/Platform/Telemetry/Dev; sim → hiển thị qua SimEventBus; UI → sim qua ICommand ở ranh giới tick.
  • 1 tick = 1/60 s; bộ đếm Q16 theo đồng hồ; tối đa 4 tick bù/frame; đổi giây → tick lúc nạp (ceil cho cửa sổ có lợi người chơi). Không Time.deltaTime trong sim.
  • RNG: chỉ RngStreams (10 luồng PCG32); fx là luồng duy nhất tự do; cấm UnityEngine.Random/System.Random; sinh nội dung chỉ số nguyên.
  • Async = UniTask (không Coroutine, không async void); thời gian = TimeManager (không DateTime.Now); màn hình = UIManager (không SetActive); cấu hình chỉ đọc qua DataManager; người chơi qua DataPlayer/PlayerDataManager; không Save() trong Update; mặc định ở SetupDefaultData(); sự kiện thưa = EasyEventManager + EventName; DOTween UI SetUpdate(true) + Kill.
  • Hot path (sim + vẽ + âm trong giao tranh): 0 B GC/frame; cache GetComponent/Find ở Awake; StringBuilder cho chuỗi động.
  • Không bao giờ hạ dưới tải: tell Y/P/N (glint, vùng, SFX), va chạm, sát thương, token, vòng chân, x-ray, ấn spawn, vùng đích đạn P (doc 10 §10, doc 15 §5.3, doc 31 §6.2).
  • Trần token/INV không đổi bởi Khăn Tang/Ấn/mutator/Tro Nhớ; brain chỉ nhận HeroSnapshot ≥ 0.25 s tuổi; PSP chỉ qua AshMemory.Read() ≥ 0.5 s; mọi ngưỡng qua AshMemory.Threshold().
  • Lưu: ghi nguyên tử .tmp → fsync → replace + .bak; mỗi file có schema_version; mỗi bản ship thêm fixture + test di trú; lưu bằng ID chuỗi, không handle.
  • Input chỉ trong Bladewake.Input; sim đọc InputFrame; mọi màn điều hướng được bằng tay cầm.
  • Chuỗi hiển thị chỉ qua LocService (khoá <file>.<id>.<field> hoặc ui.*); không chuỗi cứng trong .cs.
  • Mỗi PR: validate.py --all 0 ERROR, render khớp, test EditMode xanh, grep cấm sạch; sửa .cs → kiểm biên dịch qua Unity MCP (rule compile-validation).
  • Không tên game/studio/nhà phát hành/thương hiệu/nhân vật có thật trong code, dữ liệu, chuỗi, tên asset.

23Quyết định đã chốt

QQuyết địnhLý do
2026-11-07 — ECS?Lai hướng dữ liệu: SoA NativeArray + Burst/Jobs cho quái S, đạn, vùng, trạng thái, hash, flow field; vẽ instanced; M/L/XL + hero GameObject; không package EntitiesBible §10.4 khoá Burst/Jobs; 250 nhỏ với Burst; đội 5 dev; giữ công cụ demo
2026-11-07 — Render pipeline?Chuyển built-in RP → URP Universal Renderer ở P0 (trước MS01b), 6 shader viết lại unlit; RT 480×270/427×267 Point, upscale nguyênbuilt-in RP ở chế độ bảo trì; port console M36+ cần SRP; chuyển trước nội dung rẻ nhất
2026-11-07 — Sort bầy?alpha-test + ZWrite, depth = z chân; FX cộng sort CPUpixel art alpha 0/1 → gom lô theo trang, không sort 250 sprite
2026-11-07 — Texture?trang 2048² màu chỉ mục RG8 (chỉ số + mask điểm nhấn) + PaletteLutINV-01d đã đòi LUT; 8 MB/trang thay 16 MB; ≤ 21 trang = 168 MB
2026-11-07 — Bộ đếm slow-mo?Q16 tick theo đồng hồ; chuyển động × scalegiữ số tick nguyên của dữ liệu mà slow-mo vẫn mượt
2026-11-07 — Sự kiện tần suất cao?SimEventBus struct ring buffer 0 cấp phát cho hit/kill/spawn/tell; EasyEventManager + EventName cho sự kiện thưatinh chỉnh doc 32 §12.1 ("sự kiện dùng EasyEventManager"): 250 quái × hit/frame không được cấp phát; luật "gameplay chỉ bắn sự kiện" giữ nguyên
2026-11-07 — Hiển thị > 60 Hz?khoá 60 fps (target_fps), không nội suy; vSyncCount = refresh/60 khi chia hết, không thì targetFrameRate 60Bible/PlatformTuning khoá 60; pixel art 1 tick = 1 frame
2026-11-07 — Khoá RNG?k = FNV1a64("BW:"+stream+":"+key), state = SplitMix64(seed64 XOR k), inc = SplitMix64(k) OR 1, PCG32; job dùng băm (state, slot, tick)doc 16 §10.1 ghi PCG32(hash(seed64, stream, key)) chưa định nghĩa hash; cần độc lập worker + máy
2026-11-07 — content_version?int đơn điệu (doc 43 §11.3) → _unity/content_version.txt, chuỗi seed dùng số thập phân; content_hash = crc32 _merged.json 8 hex → _unity/content_hash.txt, chỉ cho kiểm (e) + cờ mod (thay bản chuỗi <MS>-<yyyymmdd>-<crc32> cũ)khớp khối đầu save + details[9] int32 + doc 60 §3.2; client và máy chủ băm cùng chuỗi seed
2026-11-07 — Nộp điểm?theo doc 43 §6.2–§6.3: mọi bảng Trusted, client chỉ đọc; vé bw-score → máy chủ 6 kiểm → SetLeaderboardScore (KeepBest ENDLESS/BOSS_RUSH, ForceUpdate DAILY/WEEKLY); hàng chờ local/score_queue/, hạn 02:00 UTC ngày kế (doc 19 §5.2) (thay bản cũ: LB_<MODE>_<key>, client ghi KeepBest, 4 kiểm, hạn +24 h)doc 43 là chủ Steam; 1 đường nộp cho mọi bảng
2026-11-07 — Trần voice?theo doc 32 §5.3: Real 64 / Virtual 256, pool 48 one-shot + 12 nhạc + 4 ambiencedoc 15 §5.1 còn ghi "24 voice" (số demo); doc 32 là chủ mixer → cần sửa dòng SFX doc 15
2026-11-07 — Hồ sơ lưu?theo doc 43 §7: gốc luôn <steamId64>/; save/ = meta_<steamId>.json + run_active.json + ash_profile_<steamId>.json + settings_shared.json + *.bak (Cloud); local/ không Cloud; khối đầu + 7 luật xung đột; không Steam → last_user.json hoặc 0/; companyName BladewakeDev khoá trước MS02 (thay bản profile.json cũ)doc 21/40/41/42/43 đã theo bố cục này; không cần ô lưu
2026-11-07 — Nền 1.0?1 build Windows x64 IL2CPP cho PC + Deck (lớp tương thích Steam); không build Linux gốc [ASSUMED]Deck Verified đạt được với build Windows; giảm ma trận QA
2026-11-07 — Gating nội dung theo mốc?ContentManifest SO trong Unity theo build profile; dữ liệu GDD luôn đủkhông thêm file render ngoài doc 51 §8; lưu không vỡ khi id bị ẩn
2026-11-07 — Telemetry xa?đồng ý rõ (màn Consent, mặc định chưa chọn), bật ở playtest có thông báo; telemetry_id UUID ngẫu nhiên, không gửi steamId hay băm của nó; gzip ≤ 50 KB/run; tên sự kiện canon doc 60 (hero_died, shop_purchase)doc 43 §15, doc 60 Q1/Q3; KPI doc 14/17 vẫn có từ playtest + người đồng ý
2026-11-07 — Credits trong CI?thêm bước credits_check vào chuỗi mỗi PR §16.3 ngay sau render: audio_sfx.csv × sfx-credits.json × licenses/ → Assets/_Project/Data/credits.json; thiếu dòng CC BY → FAILdoc 40 §5.7.1 + AT-08 (doc 32 §14) + doc 60 bước 9b đã đòi bước build này; chuỗi CI ở đây thiếu