# DATA_PIPELINE.md — Техническое решение каталога предметов > **Проблема:** ~10 000 скинов CS:GO × 5 wear × 10+ лет = миллионы записей цен. Вручную невозможно. > > **Решение:** 3-слойная архитектура. Якоря вручную + процедурка + опц. скрейпинг. --- ## I. АРХИТЕКТУРА — 3 СЛОЯ ### Слой 1: ЯКОРЯ (вручную, 100-150 шт) **Источник:** `canon/HISTORICAL_STEAM_ITEMS.md` от research-агента (4800 слов). **Что покрыто:** - 30 iconic скинов с реальными ценами по wear/year (Dragon Lore, Howl, Fire Serpent, etc.) - 50 кейсов с датами выхода и ценами по кварталам - 13 Australium weapons с ценами 2020-2024 - 15+ Unusual TF2 effects с типовыми ценами - PUBG sezony S7-S25 с pass-ценами - Все CS:GO Operations с pass-ценами и дроп-листами **Используется как:** **ground truth** для калибровки процедурного слоя. ### Слой 2: ПРОЦЕДУРНЫЙ ГЕНЕРАТОР (формула, генерирует тысячи) **Источник:** `canon/STEAM_ITEMS.md` секция VII (формула ценообразования) + `canon/COLLECTIONS_TIMELINE.md` (статус коллекций по датам). **Алгоритм:** ``` для каждого скина в каталоге: base = base_price[rarity_tier] # из COLLECTIONS_TIMELINE status = collection_status_on_date # active/legacy/retired/contraband для каждого wear FN/MW/FT/WW/BS: price = base × wear_multiplier[wear] # 0.3-4.0 по STEAM_ITEMS.md × status_multiplier[status] # 1.0-30.0 (retired = expensive) × time_curve(date, release) # 0.2-5.0 × stattrak_or_souvenir # 1.0-3.0 × pattern_jitter() # 0.85-1.15 если skin in ANCHORS (якорь из слоя 1): скорректировать формулу чтобы совпадало с якорем ±10% ``` **Что генерируется:** 3000-5000 уникальных скинов × 5 wear = 15-25 тыс записей. **Хранится как:** `assets/data/skins.json` — генерируется один раз при build-е. ### Слой 3: РЕАЛТАЙМ-СКРЕЙПИНГ (опционально, для энтузиастов) **Источник:** Steam Market public API. **Endpoint:** `https://steamcommunity.com/market/priceoverview/?currency=5&appid=730&market_hash_name=` (currency=5 = RUB) **Что даёт:** реальная цена на сегодня для любого скина. **Использование в игре:** - В пред-альфа: НЕ используется (offline-only) - В будущей версии: опциональная "live-pricing" механика для игроков с интернетом **Скорость:** Steam ограничивает ~20 req/sec → 30к скинов = 25 минут на полный апдейт. --- ## II. JSON-СТРУКТУРА ### Файл `assets/data/skins.json` ```json { "version": "1.0.0", "generated_at": "2026-05-24T15:00:00Z", "base_date": "2020-08-21", "skins": [ { "id": "awp_dragon_lore", "weapon": "AWP", "name": "Dragon Lore", "collection": "The Cobblestone Collection", "rarity": "covert", "category": "rifle_sniper", "released": "2014-07-01", "collection_status_timeline": [ {"from": "2014-07-01", "status": "active"}, {"from": "2018-04-01", "status": "rare"}, {"from": "2023-09-27", "status": "legacy_cs2"} ], "base_price_rub_2020": 90000, "wear_multipliers": { "FN": 2.5, "MW": 1.7, "FT": 1.0, "WW": 0.7, "BS": 0.5 }, "stattrak_available": true, "souvenir_available": true, "souvenir_multiplier": 3.0, "low_float_supported": true, "is_anchor": true } ] } ``` ### Файл `assets/data/cases.json` ```json { "cases": [ { "id": "fracture_case", "name": "Fracture Case", "released": "2020-08-06", "collection_status_timeline": [ {"from": "2020-08-06", "status": "active"}, {"from": "2023-09-27", "status": "legacy_cs2"} ], "key_cost_rub_2020": 190, "drop_weights": { "mil_spec": 0.7992, "restricted": 0.1598, "classified": 0.0320, "covert": 0.0064, "knife": 0.0026 }, "knife_pool": ["bayonet", "karambit", "m9_bayonet", "..."] } ] } ``` ### Файл `assets/data/collections_timeline.json` Структурированный экспорт `canon/COLLECTIONS_TIMELINE.md` в JSON. ### Файл `assets/data/cs2_transition.json` События 27.09.2023: смена UI, market shock, panel updates. --- ## III. ГЕНЕРАТОР В GODOT ### Скрипт `scripts/data/skins_generator.gd` ```gdscript extends Node const ANCHORS_JSON = "res://assets/data/anchors.json" # из HISTORICAL_STEAM_ITEMS.md const COLLECTIONS_JSON = "res://assets/data/collections_timeline.json" func generate_full_catalog(seed_value: int) -> Array: var rng = RandomNumberGenerator.new() rng.seed = seed_value var anchors = _load_anchors() var collections = _load_collections() var catalog = [] for collection in collections: for skin_template in _generate_skins_for_collection(collection, rng): for wear in ["FN", "MW", "FT", "WW", "BS"]: var entry = _build_entry(skin_template, wear, collection, rng) catalog.append(entry) return catalog func get_price(skin_id: String, wear: String, on_date: String) -> int: # Расчёт цены любого скина на любую дату var skin = _find_skin(skin_id) var collection_status = _status_on_date(skin.collection, on_date) var time_curve = _time_curve(skin.released, on_date) return skin.base_price \ * skin.wear_multipliers[wear] \ * _status_multiplier(collection_status) \ * time_curve \ * _daily_jitter(on_date, skin_id) ``` ### Скрипт `scripts/data/market_simulator.gd` - Симулирует ежедневные флуктуации ±5-15% - Среда -10% (канон-механика) - 5.12.2023 крах -10-20% - 24.02.2022 курс-скачок +30-50% в RUB - CS2 transition 27.09.2023 спайк ±30-50% --- ## IV. УПРОЩЁННАЯ ВЕРСИЯ ДЛЯ MVP (пролога) ### Что нужно в Акте 1-13 пролога - **Australium Wrench** (1 скин) — захардкоден в коде - **Кейс кесе го Prime** (после покупки игры) — выпадает что-то от Fracture Case (свежий на 21.08.2020). 5-7 возможных скинов рандомно. - **Кейс Пабаджи** — Survivor Crate, 5-7 косметик из реала **ИТОГО для пролога:** ~15-20 скинов в JSON, не тысячи. Полный генератор — для основной игры. ### Файл `assets/data/skins_prolog.json` (минимум) ```json { "australium_wrench": { ... }, "fracture_case_skins": [ "ak47_legion_of_anubis", "m4a4_tooth_fairy", "ump45_gold_bismuth", "..." ], "pubg_survivor_crate_items": [ "trench_coat", "leather_jacket", "..." ] } ``` --- ## V. PIPELINE BUILD ### CLI команда (Forgejo Actions может запускать) ```bash # Из корня проекта godot --headless --script scripts/data/build_catalog.gd --seed 42 # → генерирует assets/data/skins.json (15k записей) # → проверяет соответствие якорям ±10% # → выходит с кодом 0 если ок, 1 если рассогласование ``` ### В `.forgejo/workflows/build.yml` (когда дойдём) Добавить step: ```yaml - name: Build catalog run: godot --headless --script scripts/data/build_catalog.gd --seed 42 ``` --- ## VI. КОГДА ЧТО ДЕЛАТЬ | Этап | Что | |---|---| | **Сейчас (Акт 1 пролога)** | Не нужен полный каталог. 15-20 скинов руками в `skins_prolog.json`. | | **Акт 12-13 пролога (3 фарм-акка)** | Добавить 10-20 базовых скинов кесе го/Пабаджи | | **Эпоха 1 (9 класс)** | Полный каталог: ~3000 скинов через генератор. Реализация `skins_generator.gd`. | | **Эпоха 3 (10 класс, окт 2021)** | Добавить Riptide коллекции | | **Эпилог (CS2 transition)** | State-machine "era": до/после 27.09.2023 | | **Финальный релиз** | Опц. реалтайм-скрейпинг для энтузиастов | --- ## VII. РАЗМЕР DATASET | Слой | Записей | Размер JSON | |---|---|---| | Anchors (вручную) | 150 | ~50 КБ | | Процедурный CS:GO/CS2 | 15 000 | ~5-8 МБ | | TF2 (Mannco + Australium + Unusual) | 8 000 | ~3-5 МБ | | PUBG | 2 000 | ~1 МБ | | **ИТОГО** | **25 000** | **~10-15 МБ** | **Размер билда:** +15МБ — приемлемо (Godot project = 50 МБ базы, итого ~65МБ — без проблем для Steam $5 игры). --- ## VIII. ТРЕНИНГ-СЛОЙ (опционально, для будущего) ML-модель которая обучается на anchors + Steam Market API данных за месяц → может предсказывать цены любого скина с точностью ±10-15%. Запускается в фоне игры, обновляет dataset. **Не нужно для пролога.** Опция для долгосрочного развития. --- ## TBD - Конкретный JSON-формат якорей (выкатим при первой генерации) - Карта rarity → base_price (5-7 уровней) - Какие именно 30 iconic скинов кодируем как якоря (взять из HISTORICAL_STEAM_ITEMS.md) - Логика state-machine "CS-era" (до/после 27.09.2023)