Universe Survival v0.0.66

История версий

Каждый шаг разработки — что сделано, зачем, какие грабли разрешены. Свежие версии сверху.

v0.0.66 31 мая 2026 текущая

Рыболовная сеть и 5 видов рыбы

Карась
Карась · обычная
Окунь
Окунь · обычная
Щука
Щука · необычная
Лосось
Лосось · редкая
Осётр
Осётр · очень редкая
Рыболовная сеть
fishing_net · 32×64
Сеть в воде
Сеть в воде
  • Рыболовная сеть. Дорогой крафт из верёвок (10 верёвок + 2 дерева). Ставится как костёр, но только на воду. Раз в минуту с шансом 50% ловит рыбу — улов копится скрытно внутри: сколько там рыбы, видно только когда «вытащишь» сеть.
  • Сеть изнашивается. Каждое вытаскивание — минус один заряд; всего сеть можно вытащить 4 раза, потом она рвётся. Установка сети качает умение Рыбак, а от Рыбака растёт и максимум рыбы в сети (базово 20 штук).
  • 5 видов рыбы. Карась, окунь, щука, лосось, осётр — у каждой своя питательность и редкость. Редкая рыба (лосось, осётр) попадается в сеть реже. Питательность пока только записана — есть рыбу можно будет, когда добавим голод.
  • Настройки в Admin. Новый раздел «Рыбалка»: интервал ловли, шанс, максимум рыбы, число вытаскиваний, «только в воду». Питательность и редкость каждой рыбы — в разделе «Предметы».
  • Технически: флаг RequiresWater у placeable, скрытый улов и прочность сети на сервере (персист в placeables.json). ⚠ Формат пакетов изменился — клиент и сервер обновляются вместе.
v0.0.65 30 мая 2026

Умения, окно персонажа и витрина выживания

  • Умения, которые качаются от действий. 8 умений — бой, дровосек, шахтёр, рыбак, фермер, стрельба, охотник, повар. Каждое — число, стартует с 0 и очень плавно растёт за повторение профильного действия (теоретически бесконечно), давая множитель к результату 1 + уровень × коэффициент.
  • Что качается прямо сейчас. Дровосек / шахтёр / фермер — от рубки берёзы, добычи камня и сбора волокон, и увеличивают выход дропа. Повар — от крафта еды, увеличивает выход порций. Бой / стрельба — от удара по игроку (хук: реального урона ещё нет, готовим под будущую боёвку; melee vs стрельба определяется по луку в руке). Рыбак и охотник зарегистрированы и ждут своих механик (рыбалки и животных пока нет).
  • Витрина выживания. Жизни, голод и жажда теперь хранятся у персонажа и показываются — но пока не убывают (это фундамент; механику голода/жажды/смерти добавим отдельной версией).
  • Окно «Персонаж». Кнопка 👤 в HUD или клавиша P: чиби-портрет, полоски жизни / голода / жажды и список умений с уровнем и множителем.
  • Опыт/уровень убран. Классический «xp-уровень» удалён из конфига, БД и панели Admin — прогрессия теперь только через умения.
  • Технически: 4 новых сетевых пакета (умения + витрина + удар), EF-миграция БД AddVitalsAndSkills. ⚠ Формат пакетов и схема БД изменились — клиент и сервер обновляются вместе. Новый скилл-док player-skills.
v0.0.64 30 мая 2026

Окно крафта: группы, компактные карточки и прокрутка колесом

  • Зачем. После v0.0.63 рецептов стало 9, а окно крафта вмещало лишь ~5 карточек (экран 720px, прокрутки не было) — новые меч/нож/лук/стрела обрезались за нижним краем и были не видны.
  • Группировка по категориям. Рецепты разбиты на группы по категории результата: Инструменты, Оружие, Боеприпасы, Постройки, Ресурсы. У каждой группы — заголовок с разделителем. Категория берётся из уже существующего поля предмета — новых сетевых пакетов не понадобилось.
  • Прокрутка колесом мыши. Список крафта скроллится вертикально, справа — ползунок-индикатор. Вмещает любое число рецептов.
  • Компактные карточки. Высота карточки уменьшена (96→78px) — больше рецептов помещается без прокрутки.
  • Правка чисто клиентская: CraftingScreen.cs + словари локализации (ru/en). Формат сетевых пакетов, БД и сервер не менялись.
v0.0.63 30 мая 2026

Четыре новых предмета: каменный меч, нож, лук из веток, каменная стрела

Каменный меч
stone_sword · 32×64
Каменный нож
stone_knife · 32×64
Лук из веток
branch_bow · 64×32
Каменная стрела
stone_arrow · 32×32
  • Каменный меч — оружие ближнего боя в руке; крафт: 2 верёвки + 2 дерева + 4 камня.
  • Каменный нож — короткий клинок; крафт: 1 дерево + 1 камень + 1 верёвка.
  • Лук из веток (2 дерева + 2 верёвки) и каменная стрела (1 дерево + 1 камень + 1 волокно → 4 шт).
  • У ножа и лука — новый процедурный облик в руке персонажа (DrawKnife / DrawBow).
  • Боёвки пока нет: предметы со статами и обликом (как железный меч), готовы к будущей боевой системе.
v0.0.62 27 мая 2026

Костёр-контейнер: 9 универсальных ячеек, реальное горение топлива

Костёр
campfire · 32×32
Потухший
campfire_unlit · 32×32
Огниво
flint_steel · 32×32
Уголь
coal · 32×32
Древесный уголь
charcoal · 32×32
  • Костёр стал контейнером. ПКМ → меню → «📋 Открыть» открывает окно: слева сетка 3×3 универсальных ячеек, справа грид-инвентарь игрока. Любая ячейка принимает любой предмет.
  • Drag-and-drop между сторонами. Перетащил wood из сумки в любую ячейку костра — лежит. Перетащил обратно — забрал. ПКМ по ячейке (костра или сумки) открывает UiSplitDialog как в обычном инвентаре.
  • Огниво по костру без топлива — пустой удар. Анимация играет, прочность огнива тратится, но огонь не зажигается. С топливом — 10% шанс зажечь → сразу сжигает 1 шт. найденного fuel-предмета, FuelMs = его BurnDuration*1000.
  • Реальное горение из ячеек. Каждое истечение FuelMs → сервер ищет любой слот с BurnDuration > 0 и продлевает горение. Дрова 30 с, волокна 5 с (быстро прогорают), уголь 120 с. Когда топлива нет — гаснет.
  • Динамическая шкала. PlaceableInstance +`LastBurnMs` (длительность последнего сожжённого fuel). UI рисует ratio = FuelMs / LastBurnMs — каждое новое полено даёт 100% и убывает к 0. Перешёл на уголь после дров → шкала перестроилась под уголь.
  • Реалтайм-индикатор. Окно костра подписано на PlaceableStateChanged — счётчик секунд и шкала тикают раз в секунду без действий игрока.
  • Огниво ломается за КАЖДЫЙ удар. Прочность снимается всегда, когда в руке огниво и есть unlit-placeable под курсором — независимо от наличия топлива или результата roll'а. «Удачный roll, но нет топлива» — durability потратилась впустую (логируется в server log).
  • Анимация чирканья — букет искр. В strike-кадре swing 9 пикселей: белая центральная вспышка + 4 жёлто-оранжевых луча по диагоналям + 3 отлетающие искры в самом пике (frame=2). Читается как реальное чирканье кремней.
  • Pickup выгребает содержимое. При «✋ Подобрать» сервер сначала TryAdd каждого слотного предмета в инвентарь игрока, остаток — на землю как ground items; потом удаляет сам костёр. Никаких потерь.
  • Древесный уголь временно отключён. `wood.charcoalYield` = 0 — горение дров больше не даёт уголь автоматически. Спрайты `coal` (минеральный) и `charcoal` (древесный) остаются в каталоге для будущего отдельного крафта (печь).
  • Архитектура: PlaceableInstance.Slots = `Dictionary<byte, InventorySlot>` (универсально, без enum Fuel/Cook/Output). PlaceableConfig.SlotCount = 9 у костра. 2 новых сетевых пакета: `PlaceableInventoryUpdated 0x87` и `PlaceableSlotMoveRequest 0x88`. +ItemDef.CharcoalYield.
  • Формат сетевых пакетов изменён (PlaceableInstance, PlaceableStateChanged, ItemDef) — клиент и сервер обновлять вместе.
v0.0.61 26 мая 2026

Костёр и Огниво: первая система размещаемых объектов на карте

Иконка костра
campfire · 32×32
Потухший костёр
campfire_unlit · 32×32
Анимация костра 6 кадров
campfire_burn · 192×32 (6 кадров)
Огниво
flint_steel · 32×32
  • Костёр — первый placeable. Третий слой над тайлами (после ground tiles и resources). Крафтится из 3 дерева + 4 камня + 5 волокна. В MainHand даёт полупрозрачный призрак под курсором: зелёный — можно поставить, красный — вода/ресурс/занято/далеко. ЛКМ — поставить, 1 шт. списывается из стэка.
  • ПКМ → контекстное меню действий. Открывается рядом с курсором: «🔥 Зажечь огнивом» (если есть огниво + потушен), «🪵 Добавить дров» (если в инвентаре топливо), «💧 Погасить» (если горит), «✋ Подобрать» (если потушен — горящий нести нельзя). Esc/клик-вне закрывает.
  • Огниво — новый инструмент. Крафтится из 2 камней. Шанс зажечь = 10% за попытку, прочность 30 (в среднем ~3 костра на один). Каждая попытка тратит прочность, удачная или нет; при 0 ломается. На чиби-аватаре в руке — два процедурных камня + жёлтая искра в кадре «strike» (heldVisual = «flint»).
  • Топливо — универсальное. У предмета поле burnDuration (секунд горения). Дерево = 30 сек. Будущий уголь = 120 без правок системы. Костёр держит до 180 сек fuel-буфера, при зажигании огнивом получает 30 сек, добавление дров увеличивает.
  • Горение в реальном времени. Сервер 10 hz уменьшает fuel у Lit-placeables; в 0 — состояние автоматом меняется на Unlit, всем летит broadcast. Костёр горит 6-кадровой анимацией (атлас от Gemini 3×2 → SpriteGen ProcessAtlas нарезает в strip 192×32, кадр 150 мс); потухший — статичный 32×32.
  • Архитектура готова к расширению. PlaceableConfig в server-config.json описывает любой новый тип (стена, дверь, полка, сундук): спрайты, supports-флаги, fuel, BlocksMovement. Состояние мира — отдельный JSON-файл world.usmap.placeables.json рядом с картой, персист раз в 30 секунд.
  • 7 новых сетевых пакетов (0x80..0x86) + ItemDef расширен полями placeableId/burnDuration/igniteChance. Клиент и сервер обновлять вместе.
v0.0.60 24 мая 2026

Анимация удара: рукой, топором, киркой и любым предметом

strike-кадры с оружием в руке
момент удара: кулак · топор · кирка · меч · дубина · копьё
  • Раньше при ЛКМ персонаж стоял столбом. Курсор-кирка крутился, у ресурса резко пропадало HP, но на самом аватаре игрока не было никакого движения — выглядело как «телепортный удар». Теперь персонаж коротко замахивается и бьёт сверху-вниз: ~0.35 с на полный цикл windup → апогей → strike → recover.
  • Голая рука бьёт кулаком. Если в `MainHand` ничего нет — в момент удара в кисти появляется маленький кулак (3×3 пятно кожи), и им наносится удар. Без этого «голая» атака была бы попросту невидима.
  • Любой предмет в руке участвует в ударе автоматически. Топор, кирка, меч, копьё, дубина, факел, щит — все семь визуальных типов едут за кистью через все 4 кадра замаха, в трёх направлениях. Будущее новое оружие умеет бить «из коробки» — добавил `heldVisual` в конфиг, оно бьёт.
  • Чужих игроков ты тоже видишь, когда они бьют. Сервер при каждом успешном HarvestRequest бродкастит лёгкий пакет «N ударил по тайлу (tx,ty)» (PlayerActionAnim 0x28) — наблюдающие клиенты разворачивают аватар бьющего лицом к цели и крутят swing-анимацию у него на экране.
  • Бежать и бить нельзя. WASD-движение моментально прерывает swing — переход обратно в walk. Совмещение «бежать + бить» в чиби-стилистике выглядит хаотично, поэтому сознательно запретили.
  • Технически: атлас чиби-аватара вырос с 256×192 до 256×384 — в нём теперь два «режима движения» (walk + swing) по 3 направления × 4 кадра. Общая точка HandPoint для предмета не меняется — он бесплатно едет за кистью и в swing-кадрах.
  • Формат сетевого пакета изменён (новый PlayerActionAnim 0x28) — клиент и сервер обновлять вместе.
v0.0.59 23 мая 2026

Прочность инструмента: топор и кирка изнашиваются и ломаются

  • Каждый удар стоит 1 единицы прочности. Каменный топор и кирка выдерживают по 60 ударов, железный меч — 100. Когда прочность доходит до нуля — предмет уничтожается, рука становится пустой. Эти числа задаются в Admin → «Предметы» → «Прочность (max)».
  • Бар прочности под иконкой. В гриде инвентаря и в слоте экипировки внизу иконки рисуется тонкая полоса: зелёная (>60%), жёлтая (>30%), красная (≤30%). Tooltip добавляет строку «Прочность: X / Y».
  • Износ независим от того, подходит инструмент ресурсу. Топор по камню тратит прочность так же, как топор по дереву — берёг инструменты значит используй их по назначению.
  • Старые инструменты не сломаются от деплоя. Каменные топоры/кирки, которые лежали в инвентаре до этой версии, при первом входе получают полную прочность автоматически — а не нулевую.
  • Восстановление прочности — отдельная задача. Наковальня/починка появятся в следующих версиях. Пока инструмент только изнашивается.
  • Формат сетевых пакетов изменён (ItemDef + MaxDurability, InventorySlot + Durability) — клиент и сервер обновлять вместе.
v0.0.58 23 мая 2026

Инструмент важен: топор быстрее рубит дерево, кирка быстрее ломает камень

  • Был баг балансировки. Каменный топор и кирка существовали в игре с v0.0.46, но рубили любой ресурс ровно с той же скоростью, что и голая рука — по 1 HP за удар. Смысла крафтить инструмент почти не было.
  • Подходящий инструмент = больше HP за удар. Каменный топор по берёзе (HP 2–4) — 1–2 удара вместо 2–4 рукой. Каменная кирка по валуну (HP 3–5) — 2–3 удара вместо 3–5 рукой.
  • Неподходящий инструмент = удар рукой. Топор по камню или кирка по дереву теперь снимают 1 HP — наглядный «штраф», чтобы было видно: возьми подходящее. Высокая трава по-прежнему рвётся одинаково любым инструментом и рукой.
  • В Admin → «Предметы» 3 новых поля. «Инструмент для добычи» (axe / pickaxe / shovel / sword / не инструмент), «Сила удара» (1..20) и «Визуал в руке» — последнее раньше правилось только через JSON.
  • В Admin → «Ресурсы» новое поле «Эффективный инструмент». Дерево → axe, камень → pickaxe, земля → shovel, или «(любой)» — рвётся всем подряд.
  • Новые материалы — без кода. Чтобы ввести медный/железный топор, достаточно создать предмет в Admin с поднятым «Сила удара» — никаких правок исходников, только настройки.
  • Формат сетевого пакета `ItemDef` изменился (+ToolType, +HarvestPower) — обновлять клиент и сервер вместе.
v0.0.57 22 мая 2026

Колесо мыши приближает и отдаляет камеру

  • Зум камеры колесом мыши. Крути колесо вперёд — картинка приближается, назад — отдаляется. Каждый игрок сам подбирает масштаб, при котором ему удобно играть.
  • Выбранный масштаб запоминается. Зум сохраняется в настройках и переживает перезапуск игры — заходишь в мир уже с привычным видом.
  • Плавное приближение. Камера подъезжает к новому масштабу мягко, без рывков. Пределы — от 1× (далеко, видно много) до 4× (близко, крупный персонаж); по умолчанию 2×, как было раньше.
  • Изменения только в клиенте — сервер пересобирать не нужно.
v0.0.56 21 мая 2026

Панель быстрого доступа — предмет в руку одной клавишей

  • Внизу экрана — хотбар из 10 ячеек. Ячейки на клавишах 1, 2, 3, 4, 5, 6, 7, 8, 9, 0.
  • Перетащи предмет в ячейку. Открой инвентарь, возьми предмет и перетащи на ячейку панели — она запомнит этот предмет. Сам предмет остаётся в сумке. ПКМ по ячейке — очистить её.
  • Нажал цифру — взял в руку. Нажатие клавиши-цифры (или клик по ячейке) — персонаж достаёт предмет из ячейки в руку. Не мгновенно: проигрывается короткая анимация «возни» с полоской прогресса над головой, и лишь затем предмет оказывается в руке. Движение или клик по миру прерывают доставание.
  • Хотбар переключает инструмент. Если в руке уже что-то есть — предметы меняются местами, прежний возвращается в сумку. Содержимое хотбара сохраняется между запусками игры.
  • Нужно обновить и клиент, и сервер.
v0.0.55 21 мая 2026

Управление сервером в Admin — полностью переделано

  • Была путаница. В админ-панели было три похожих действия — «Сохранить», «Обновить», «Pull с сервера». Их легко перепутать, и правки настроек не доходили до игры.
  • Теперь Admin — панель управления сервером. При запуске сам подключается к серверу и загружает его конфиг; все секции правят реальный конфиг сервера.
  • Чёткий поток: Остановить → Вайп → Запустить. Кнопки зависят от состояния сервера. «Запустить» заливает настройки на сервер и стартует — настройки применяются при запуске.
  • Убрано лишнее: «Открыть / Сохранить / Перезагрузить», «Обновить» / «Рестарт» / «Pull». Изменения затрагивают только программу Admin.
v0.0.54 21 мая 2026

Карту мира рисует сервер — источник истины

  • Карта приходит по сети. Раньше клиент рисовал карту из локального файла world.usmap рядом с собой — не связанного с сервером, к которому подключён. Сменишь размер мира в настройках — а в игре старая карта.
  • Теперь сервер — источник истины. При входе в мир сервер присылает клиенту актуальную карту пакетом WorldMapSnapshot, и клиент рисует именно её.
  • Тайлы сжимаются. Карта почти вся однородная, поэтому жмётся Deflate в разы: 128×128 — ~16 КБ в ~2–3 КБ, 1024×1024 — ~1 МБ в ~50 КБ.
  • Локальный world.usmap онлайн-клиенту больше не нужен — остался как оффлайн-фоллбэк. Нужно обновить и сервер, и клиент.
v0.0.53 21 мая 2026

Управление сервером — доработка панели

  • «Удалённый сервер» → «Управление сервером». Переименован верхний раздел админ-панели — у проекта один сервер, слово «удалённый» убрано из названий.
  • Вайп сервера теперь в одном месте. Раньше полный вайп был сразу в двух разделах админ-панели — лишний убран, остался один.
  • Вайп только на остановленном сервере. Кнопка вайпа разблокируется лишь когда сервер остановлен — на живом сервере вайп повредил бы данные. После вайпа сервер остаётся выключенным.
  • Новая карта генерируется автоматически. После вайпа достаточно нажать «Старт» — сервер сам создаёт свежую карту и применяет чистую схему базы данных.
  • Кнопка «Обновить» применяет изменения. Новые предметы, рецепты и настройки попадают в игру по кнопке «Обновить» — заливает конфиг и перезапускает сервер, без полного вайпа.
  • На игроков не влияет: формат сети и базы данных не менялся, обновлять клиент не нужно.
v0.0.52 21 мая 2026

Сервер переведён на PostgreSQL

  • Аккаунты и персонажи теперь в PostgreSQL. Игровой сервер хранит данные игроков в полноценной базе PostgreSQL вместо файла SQLite. SQLite удобен на стадии соло-разработки, но сериализует запись — один пишущий за раз. Для цели в 1000 игроков онлайн нужна база, которая держит много одновременных записей.
  • На проде PostgreSQL развёрнут рядом с сервером. На нашей машине установлена и настроена СУБД, создана отдельная база и роль. Пароль хранится только в переменной окружения, в конфиги и git не попадает.
  • База запущена с чистого листа. Старые тестовые аккаунты не переносились — после обновления нужно зарегистрироваться заново.
  • Backward-compatible для клиента: формат сетевых пакетов не менялся — обновлять нужно только сервер. Схема данных (аккаунты, персонажи, инвентарь, внешность) осталась прежней, сменился лишь движок БД.
v0.0.51 21 мая 2026

Возврат в игру по кнопке «Продолжить»

  • Из меню можно вернуться в игру. Если во время игры нажать Esc, ты попадаешь в главное меню. Раньше единственная кнопка возврата называлась «Войти» и вела заново через экран входа и выбор персонажа — хотя ты уже в игре.
  • Кнопка «Продолжить». Теперь, пока сессия с сервером жива, кнопка «Войти» в меню превращается в «Продолжить» — клик возвращает прямо в мир тем же персонажем и на то же место, где ты нажал Esc. Ни логина, ни выбора персонажа заново.
  • Обычный «Войти» — на месте. При свежем запуске игры или после потери соединения с сервером кнопка по-прежнему «Войти» и ведёт на экран входа.
  • Версия игры стала единой. Раньше номер версии был вписан руками в нескольких местах и разъезжался: в меню висела v0.0.47, в сервере — 0.0.50. Теперь и меню, и сервер берут версию из одного источника — при релизе меняется одна строка, а число везде совпадает.
  • Backward-compatible: формат сетевых пакетов и базы данных не менялся.
v0.0.50 21 мая 2026

Зажатая ЛКМ повторяет действие

  • Можно держать ЛКМ вместо того, чтобы тыкать. Маленькое продолжение v0.0.49: добыча и атака теперь повторяются сами, пока зажата левая кнопка мыши (интервал ~0.45 с). Срубить дерево из 3–4 HP — это один зажим, а не 3–4 отдельных клика.
  • Зажим фиксирует цель. Нажатие захватывает объект под курсором; пока кнопка зажата, удары идут по нему. Когда цель «закончилась» (дерево срублено, предмет поднят) — захват сам перенацеливается на то, что под курсором сейчас. Можно, не отпуская кнопку, сметать соседние деревья или подобрать выпавший лут.
  • Быстрый клик — как раньше. Нажал-отпустил = одно действие; клик по далёкой цели = персонаж сам подойдёт и выполнит.
  • Атака остаётся заглушкой (боевой системы пока нет) — зажатая ЛКМ по другому игроку ничего не делает.
  • Backward-compatible: правка чисто клиентская, формат сетевых пакетов и базы данных не менялся.
v0.0.49 21 мая 2026

Действие на ЛКМ: курсор наводится точно на цель

Курсор добычи — кирка
добыча · ресурсы
Курсор «поднять» — рука
поднять · предметы
Курсор атаки — меч
атака · игроки
  • Действие переехало с клавиши E на левый клик мыши по объекту под курсором. Раньше E добывал «всё ближайшее» вокруг персонажа — нельзя было выбрать, что именно собрать. Теперь ты кликаешь ЛКМ точно по тому, на что наведён курсор.
  • 3 контекстных курсора. Курсор меняет вид по типу цели под ним: кирка — ресурс (добыть), рука — предмет на земле (поднять), меч — другой игрок (атака). Над пустым местом — обычная стрелка. Иконки — pixel-art, сгенерированы через AI (openrouter-image) и обработаны новой командой SpriteGen cursors.
  • Точное наведение. Деревья, валуны и трава ловятся пиксель-точно — по непрозрачным пикселям спрайта, а не по прямоугольнику вокруг: наводишься на крону дерева, а не на пустой угол. Наведённая цель обводится цветной рамкой — зелёной (добыча), жёлтой (поднять), красной (атака).
  • Авто-движение к цели. Клик по объекту вне радиуса действия (≈2 тайла) — персонаж сам идёт к нему по прямой и выполняет действие, как только подойдёт. Нажатие WASD мгновенно отменяет авто-движение — ручное управление в приоритете.
  • Атака — пока заглушка. Боевой системы (здоровье, урон, мобы) в игре ещё нет, поэтому клик «атака» по другому игроку только меняет курсор. Курсор-меч уже на месте — появится боёвка, заработает и он.
  • Backward-compatible: формат сетевых пакетов и базы данных не менялся — правка чисто клиентская, сервер пересобирать не требуется.
v0.0.48 20 мая 2026

Экипировка в руках: оружие, инструменты и щиты на персонаже

Чиби-персонажи Universe Survival с оружием, инструментами и щитом в руках
топор · кирка · меч+щит · копьё · дубина · факел
  • Оружие и инструменты теперь видно в руках персонажа. Предмет, надетый в слот руки, рисуется прямо на чиби-аватаре — и на твоём, и на других игроках. Первыми облик в руках получили каменный топор и каменная кирка, а также железный меч.
  • Процедурный paper-doll-слой. Компоновщик CharacterArt рисует предмет в точке руки поверх тела — без единого PNG-ассета, ровно как само тело и причёски. Точка руки следует за подпрыгиванием корпуса и взмахом руки на шаге, поэтому предмет анимируется во всех 3 направлениях и 4 кадрах ходьбы автоматически. Предмет второй руки (щит/факел) не рисуется в виде сбоку — дальняя рука уходит за корпус.
  • 7 параметрических форм: топор, кирка, меч, копьё, дубина, щит, факел — каждая нарисована кодом (каменные/стальные головы, деревянные рукояти, верёвочные обмотки). Неизвестный тип → обобщённый инструмент, чтобы будущий предмет был хотя бы виден в руке.
  • Система готова к будущим предметам. Чтобы новое оружие или инструмент отображались в руке — достаточно задать строку heldVisual у предмета в server-config.json; новая форма = одна функция в CharacterArt. Никаких миграций БД, никаких PNG-ассетов.
  • Сеть. Новый тип EquipmentVisual добавлен в PlayerSnapshot — другие игроки видят твоё оружие сразу при входе в мир. Новый пакет PlayerEquipmentChanged (0x26) рассылается при экипировке/снятии предмета руки — чужая экипировка обновляется в реальном времени.
  • Фикс. Пакет позиций PlayerPositions пересобирал снимок игрока и терял его внешность — теперь все неизменяемые поля (внешность, экипировка) сохраняются.
  • Backward-compatible: формат БД не менялся. ⚠ Формат сетевых пакетов изменился (PlayerSnapshot + экипировка, ItemDef + heldVisual) — клиент и сервер должны обновляться вместе.
v0.0.47 20 мая 2026

Чиби-персонажи: внешность, кастомизация, анимация

Разные чиби-персонажи Universe Survival
чиби-аватар · процедурная кастомизация
  • Полная переработка персонажа — чиби-стиль. Старый статичный человечек 32×48 заменён на послойный чиби-аватар 64×64 с большой выразительной головой, чтобы черты лица были хорошо видны. Один процедурный компоновщик CharacterArt рисует игрока, других игроков и (в будущем) человекоподобных NPC.
  • Глубокая кастомизация внешности. 2 пола, 3 телосложения, 3 тона кожи, 5 форм головы, 11 причёсок (5 мужских + 5 женских + лысый), 8 цветов волос, 5 форм глаз, 6 цветов глаз, 5 форм носа, 5 форм рта, 5 видов бровей — сотни тысяч комбинаций.
  • Экран создания персонажа. Новый редактор с живым портретом: переключаешь любую черту стрелками — превью обновляется мгновенно. Есть кнопка «🎲 Случайно».
  • Анимация ходьбы. Процедурная — 4 кадра в 4 направлениях (вниз/вверх/влево/вправо): корпус подпрыгивает, ноги шагают, тень поджимается.
  • Архитектура. Внешность — компактный CharacterAppearance (11 байт) в shared-network, хранится JSON в БД (AppearanceJson + EF-миграция), летит по сети внутри CharacterInfo/PlayerSnapshot — другие игроки видят твой настоящий облик. Полностью процедурно: ни одного PNG-ассета частей тела, MCP openrouter-image задал чиби-стиль эталонами, код его воспроизводит — слои гарантированно совпадают.
  • Backward-compatible: старые персонажи без внешности получают облик по умолчанию (по полу) при первом входе. ⚠ Формат сетевых пакетов изменился — клиент и сервер должны обновляться вместе.
v0.0.46 19 мая 2026

Меню крафта + 3 первых крафтовых предмета

Иконка предмета верёвка
rope · 32×32
Иконка предмета каменный топор
stone_axe · 32×64
Иконка предмета каменная кирка
stone_pickaxe · 32×64
  • Появилась полноценная система рецептов. Отдельная HUD-кнопка «🛠 Крафт (C)» в правом-верхнем углу карты (или хоткей C) открывает overlay-экран в стиле Аллодов: тёмное дерево, brass-рамка, пергаментный фон. Внутри — карточки рецептов: иконка результата + название + горизонтальный ряд ингредиентов (иконка + «есть/нужно», зелёная рамка если хватает, красная — если нет) + большая кнопка «Скрафтить» (зелёная активная если хватает всех материалов, серая иначе).
  • 3 новых предмета. Верёвка (rope, 1×1, stack 50, 0.3 кг, category resource) — AI-сгенерированный pixel-art моток через MCP openrouter-image (Gemini 2.5 Flash Image). Каменный топор (stone_axe, 1×2, equipSlot MainHand, stats: damage 6 / harvestPower 2 / durability 60). Каменная кирка (stone_pickaxe, 1×2, equipSlot MainHand, stats: damage 4 / miningPower 2 / durability 60). Оба инструмента — flint-головы на деревянных рукоятях с веревочной обвязкой.
  • 3 стартовых рецепта (редактируются в Admin → Рецепты крафта):
    • rope_from_fiber: 3 волокна → 1 верёвка
    • stone_axe: 1 верёвка + 2 камня + 2 дерева → 1 каменный топор
    • stone_pickaxe: 1 верёвка + 3 камня + 2 дерева → 1 каменная кирка
    Цепочка естественная: срубил траву → волокна, скрафтил верёвку, нарубил берёз и валунов → собрал инструменты.
  • Архитектура. Новые типы RecipeConfig/RecipeIngredient в shared/Config/ServerConfig.cs, новая секция recipes в server-config.json. Два новых сетевых пакета: RecipeConfigSnapshot 0x70 (сервер → клиент один раз при EnterWorld, после InventorySnapshot) и CraftRequest 0x71 (клиент → сервер на нажатие «Скрафтить»). Клиент держит NetworkSession.KnownRecipes. Сервер на craft-запрос через новый InventoryService.CountItem валидирует наличие всех ингредиентов, списывает через TryConsume, выдаёт результат через TryAdd (overflow → SpawnGroundItem под ногами игрока), пришлёт InventoryUpdate.
  • Клиент. Новая сцена client/scenes/CraftingScreen.tscn + скрипт CraftingScreen.cs (~330 строк). Один Control с _Draw — стиль идентичен InventoryScreen. Hot-key через новый action crafting_toggle (default C); строка биндинга появилась в Настройки → Управление. Esc-приоритет: открытый инвентарь → открытый крафт → menu_back. HUD-кнопка HudLayer/CraftButton добавлена в TestMap.tscn (anchor=top-right, ширина 164 px).
  • Admin. Новая секция «Рецепты крафта» в левой панели (между «Группы ресурсов» и «NPC»). UI: picker всех рецептов + кнопки ➕/🗑, форма ID/Name/ResultItem (ComboBox из всех предметов)/ResultCount, контейнер ингредиентов с кнопкой ➕ — каждый ингредиент = строка с ItemId-ComboBox + count NUD + ✕. Реализовано по образцу секции «Группы ресурсов».
  • Локализация: ru.json/en.json получили craft.* (title/hint/button/empty/not_enough/sent), controls.action.crafting_toggle, map.craft_button. Help-строка карты обновлена: «...C — крафт. Esc — меню.»
  • Backward-compatible: формат БД и существующих пакетов не менялся. Старые клиенты (≤ v0.0.45) подключаются к v0.0.46-серверу — они просто проигнорируют RecipeConfigSnapshot (logged как «неизвестный PacketId=0x70»), крафта не будет, но всё остальное работает. Новые клиенты с v0.0.45-сервером — KnownRecipes останется пустой, экран крафта покажет «Рецептов пока нет».
v0.0.45 19 мая 2026

Admin → полное удалённое управление prod-сервером по SSH

  • Admin становится единой точкой администрирования. До v0.0.44 Avalonia-программа UniverseSurvival.Admin.exe была простым редактором локального server-config.json, а вся фактическая администрация prod-сервера (старт/стоп/wipe/деплой конфига) делалась руками через ssh-команды. По требованию: «всё управление сервером — только у меня и только через Admin» — теперь верхний пункт левой панели «🌐 Удалённый сервер» содержит всю операционную панель.
  • Кнопки сервиса. «▶ Старт» / «⏹ Стоп» / «🔄 Рестарт» вызывают systemctl <action> universesurvival-server.service. «📊 Обновить» pull-ит статус: active/inactive, PID, объём RAM, время старта — из systemctl show. Плашка горит зелёным «● Активен» или серым «○ Остановлен».
  • Управление конфигом. «📥 Pull с сервера» скачивает /opt/universesurvival-server/server-config.json через SCP и открывает в Admin — обычные вкладки «Сеть»/«Мир»/«Предметы»/«Ресурсы»/etc. теперь редактируют этот удалённый snapshot. «📤 Push + рестарт» сериализует UI обратно в JSON, заливает на сервер, chown usgame:usgame и рестартует сервис — изменения сразу применяются.
  • Полный wipe одной кнопкой. «⚠ Wipe сейчас» с подтверждением через диалог WipeConfirmWindow (как и раньше в локальном режиме). Логика на сервере: systemctl stoprm -fv world.usmap universesurvival.db* server.locksystemctl start. Мир и БД генерируются с чистого листа.
  • Журнал systemd. «📋 Журнал systemd (50 строк)» — journalctl -u universesurvival-server -n 50 в окно лога. То же окно лога показывает действия Admin (pull/push/start/stop/restart, OK или ОШИБКА с сообщением).
  • Архитектура. Новый класс admin/UniverseSurvival.Admin/RemoteAdminClient.cs (~200 строк) — обёртка над SSH.NET 2024.2.0 без зависимостей от Avalonia. TryLoadCreds() читает untracked tools/.new-server-creds поднимаясь по дереву от exe-каталога — работает и из dev-репо, и из портативной сборки в go/. Все методы синхронные; UI вызывает их через Task.Run, чтобы не блокировать поток рисования. В ConfigLoader (shared) добавлены SerializeToString и DeserializeFromString — pull/push идут по сети без temp-файла.
  • Локальный режим сохранён. Кнопки «Открыть…» / «Сохранить» / «Перезагрузить» работают как раньше — нужны для подготовки initial-config до первого деплоя на голый сервер. Просто переключаешься между удалённым snapshot'ом и локальным файлом через секцию «🌐 Удалённый сервер» или классический файл-пикер.
  • Что НЕ сделано (намеренно): real-time tail журнала (сейчас одноразовый pull — для непрерывного мониторинга есть journalctl -f по ssh); редактирование creds-файла из UI (правится текстовым редактором, формат тривиальный); смена сервера-цели через UI (Admin завязан на один prod-сервер из creds-файла — multi-env-dropdown появится когда добавится staging). Backward-compatible: формат server-config.json, БД, сетевые пакеты и .usmap не менялись.
v0.0.44 19 мая 2026

Своя инфраструктура: переезд на dedicated сервер 77.91.92.186

  • Игра вышла в онлайн. Игровой сервер UniverseSurvival.Server впервые работает не у разработчика на Windows, а на отдельной Linux-VM (Ubuntu 24.04, .NET 10 runtime, systemd auto-restart). UDP 9050 открыт миру — любой клиент с интернетом может зайти. Раньше клиент v0.0.26+ был жёстко прибит к 127.0.0.1 и сервер запускался только локально.
  • Клиент конфигурируется по адресу сервера. В GameSettings появились поля ServerHost (default "universesurvival.ru") и ServerPort (default 9050). Сохраняются в user://settings.json рядом с разрешением/языком. NetworkSession.Connect() читает их вместо хардкода 127.0.0.1. Для локальной разработки достаточно поставить "serverHost": "127.0.0.1" — клиент сразу пойдёт в локальный exe.
  • Своя машина — никаких соседей. Прежний хостинг (старый неприспособленный сервер, общий с чужими проектами владельца) — это были постоянные риски задеть чужое. Новый сервер (77.91.92.186, Ubuntu 24.04, 2 vCPU, 3.8 ГБ RAM, 58 ГБ диск) принадлежит только universesurvival — можно ставить что угодно без опаски.
  • Стек на сервере: nginx 1.24 (vhost /etc/nginx/sites-available/universesurvival.ru) + certbot 2.9 (LE-сертификат, авторенью через timer) + dotnet-runtime-10.0 + aspnetcore-runtime-10.0 (apt-пакеты из packages.microsoft.com) + systemd unit universesurvival-server.service (User=usgame, Restart=always, логи в /var/log/universesurvival/ + /opt/universesurvival-server/logs/). UFW: 22/tcp, 80/tcp, 443/tcp, 9050/udp.
  • Deploy: dotnet publish -c Release -r linux-x64 --no-self-contained → tar.gz (~2.9 МБ) → pscptar xzf в /opt/universesurvival-server/systemctl restart universesurvival-server. Helper-скрипт tools/new-server-ssh.sh — обёртка над plink/pscp с пинными hostkey. Aутентификация — только пароль (по требованию разработчика).
  • Что НЕ переехало: dev-инструменты (Admin, Map Editor, Test Client) остаются на разработческой Windows-машине — серверу они не нужны. Локальный SQLite-файл с тестовыми аккаунтами не копировался — на продакшене стартанули с чистой БД (миграции применились автоматически, мир 1024×1024 сгенерировался с тем же seed=1).
  • Документация: docs/deploy-server.md описывает полный пайплайн (новый файл); web/README.md переписан под новый адрес.
v0.0.43 19 мая 2026

Новый ресурс «Валун» и предмет «Камень»

Спрайт каменного валуна
stone_rock · 32×48
Иконка предмета камень
stone · 32×32
  • Третий ресурс в мире. После берёзы (v0.0.32) и высокой травы (v0.0.39) теперь на карте стоят каменные валуны 32×48 — серые, с мшистой нашлёпкой на верхне-левой грани. Частые на горных тайлах (биом Stone, плотность 5%), редкие на равнинах (биом Grass, 0.5%), отсутствуют на песке. Блокируют движение (как берёза) — нужно обходить. HP=3..5 — три-пять нажатий E, и валун разбивается.
  • Каждый разбитый валун = 1–3 камня (stone, chance: 1.0). Предмет stone уже был заведён в server-config.json::items ещё в v0.0.36 (1×1 в гриде, stackSize 99, weight 2 кг, category resource), но без источника выпадения — висел как «мёртвый» id. С v0.0.43 валун становится первой реальной точкой выпадения. Камень — будущий основной строй-материал для крафта стен/печей/инструментов.
  • AI-пайплайн. Спрайт ресурса stone_rock.png 32×48 и иконка предмета stone.png 32×32 сгенерированы через MCP openrouter-image (Gemini 2.5 Flash Image) → SpriteGen process-resource (chroma-key + bbox + crop + area-downsample + alpha-binarisation). Палитра валуна: #6E7378 база, #4A4F54 тень, #9AA0A6 хайлайт, #3D4146 трещина, #5C7349/#7A8A66 мох. Strong 1-px outline #1A1A1A. Bottom-center anchor (как берёза). Raw 1024×1024 коммитятся в git как source-of-truth.
  • Никаких новых пакетов. Сервер v0.0.34+ полностью type-agnostic: WorldResourceSnapshot/ResourceRemoved/ResourceDamaged несут просто байт типа ресурса. ApplyResourceDrops в PacketDispatcher сам выкидывает stone в инвентарь через InventoryService.TryAdd, а если инвентарь переполнен — на землю как ground item через WorldStateService.SpawnGroundItem. Подключение нового ресурса = одна правка enum + одна запись в config.
  • Map Editor: новая кисть 🪨 Валун рядом с 🌳 Берёза и 🌾 Высокая трава. В статистике карты — счётчик «валунов N» наряду с «берёз N» и «травы N».
  • Backward-compatible: формат .usmap не менялся, пакеты не менялись. v0.0.42-клиенты со v0.0.43-сервером не упадут (неизвестный TypeByte=3 пропустится через default: continue в MapView.cs switch), но валун не нарисуется — нужно обновить и клиент. Старые конфиги без stone_rock в resources[] работают (загружаются как «нет валунов»); при wipe сервер регенерирует мир с новой плотностью.
v0.0.42 19 мая 2026

Admin: полный CRUD предметов и расширенная настройка ресурсов

  • Секция «Предметы» — больше не stub. До v0.0.41 в Admin под «Предметами» висела надпись «редактируется в JSON напрямую». Теперь — полноценный редактор: picker всех ItemConfig, кнопки «➕ Создать предмет» / «🗑 Удалить предмет», и форма из ВСЕХ полей предмета: id, имя, категория (ComboBox: resource/weapon/armor/food/tool/misc), вес, размер стэка, ширина/высота в гриде, слот экипировки (ComboBox из 11 вариантов + «нет»), доп. клетки грида при ношении в рюкзаке. Изменения подхватываются сервером при следующем старте; клиенты — при следующем входе в мир.
  • Drops в Ресурсах — теперь выбор из каталога. Поле itemId для каждого выпадающего предмета — было ручной ввод TextBox, стало ComboBox со всеми предметами из секции «Предметы». При выборе показывается «Имя (id)». Если в существующем конфиге drop ссылается на удалённый предмет — он виден как «⚠ {id} (нет в каталоге)», чтобы пользователь сразу увидел проблему.
  • Кнопки ➕/🗑 для ресурсов. В «Ресурсах» рядом с picker'ом — две новых кнопки: «➕ Создать ресурс» (создаёт с свободным TypeByte, дефолтная плотность 0.01 на траве) и «🗑 Удалить ресурс». ⚠ Полностью новый вид ресурса будет виден в игре только если в shared/World/ResourceType.cs есть enum-значение с тем же TypeByte — клиентский рендер пока завязан на switch по enum. Смена параметров уже существующих ресурсов работает без правок кода.
  • Спрайты «вплоть до файла» — через FilePicker. В обеих секциях («Предметы» и «Ресурсы») появилось поле «Иконка / Спрайт» с тремя контролами: путь, «📁 Выбрать файл…», «✕». Кнопка 📁 открывает диалог выбора PNG, копирует выбранный файл в client/assets/items/{Id}.png (или .../resources/{Id}.png) и записывает относительный путь в новое поле ItemConfig.IconPath (или ResourceConfig.SpritePath). Под полем — превью 96×128 px. Пустое поле = клиент использует конвенцию res://assets/items/{Id}.png (backward-compat).
  • ItemConfig.IconPath пробрасывается клиенту через расширенный ItemDef. К сетевому пакету добавлена строка iconPath. InventoryScreen.ReloadItemIcons и MapView.GetItemIcon сначала проверяют поле — и если непусто, грузят оттуда (с поддержкой как res://..., так и assets/...), иначе fall-back на старую конвенцию. ⚠ Пакет ItemDef удлинился на одну строку — серверы и клиенты должны обновляться одновременно.
v0.0.41 19 мая 2026

Слияние стэков через drag-and-drop

  • Перетащил один стэк на другой того же предмета — они слились. Например, два стэка волокон (30 шт. и 50 шт.) → объединяешь → получаешь один стэк ×80, исходная ячейка освобождается. До v0.0.41 такое перемещение блокировалось как «занято».
  • Overflow ведёт себя предсказуемо. Если сумма превышает max stack (например, оба по 60, max 99) — целевой наполняется до 99, остаток (21 в этом случае) остаётся на исходной позиции, чтобы пользователь не терял привычной точки.
  • Не сливаются: предметы разных ItemId; предметы с stackSize == 1 (оружие, броня); полный целевой стэк — в этих случаях перенос проваливается на обычную «не помещается» логику.
  • Серверная правка единичная — InventoryService.TryMove с блоком merge до проверки FitsInGrid. Никаких новых пакетов, никаких клиент-side изменений — клиент с v0.0.40 уже шлёт правильные (toX, toY), сервер сам разбирается.
  • Backward-compatible: Сервер 0.0.41 совместим со старыми клиентами; старый сервер с новым клиентом просто не будет мерджить.
v0.0.40 19 мая 2026

Урна, разделение стэков и выкидывание на землю

  • Урна. В правом-нижнем углу окна инвентаря нарисована урна 64×64 — трапеция с крышкой и вертикальными полосами, в стиле Аллодов. Тащишь любой предмет из грида на неё → отпускаешь → появляется модальное окно «Уничтожить «{name}» ×{count}? — Уничтожить / Отмена». Подтвердил — стэк удаляется без следа. Урна подсвечивается красно-оранжевым при наведении драга.
  • ПКМ — разделить стэк. Правый клик по стэку в гриде (если в стэке больше одного предмета) открывает модальный диалог со слайдером 1..(N-1) и спин-боксом — они синхронизированы между собой. По OK сервер создаёт новый слот в первой свободной ячейке грида; если места нет — операция отвергается. По умолчанию слайдер выставлен на половину стэка. Enter — подтвердить, Esc — отмена.
  • Перенос за пределы окна — выкинуть на землю. Тащишь предмет и отпускаешь мышь ВНЕ окна инвентаря → предмет падает на тайл под ногами игрока как обычный ground item. Другие игроки в мире видят его и могут поднять через E. Drop ВНУТРИ окна, но мимо целей — игнорируется (предмет «вернётся на место»).
  • 3 новых сетевых пакета (InventoryDestroyRequest 0x45, InventorySplitRequest 0x46, InventoryDropRequest 0x47) — все клиент→сервер. Ответом служит уже существующий InventoryUpdate с полным новым состоянием инвентаря — никаких новых server→client пакетов.
  • 2 новых UI-компонента: UiConfirmDialog (модальный Yes/No) и UiSplitDialog (слайдер + спин-бокс). Оба собираются кодом в _Ready (без .tscn-файлов), стиль одинаковый — тёмное дерево + brass-рамка + пергаментный текст. Затемнение фона блокирует клики на инвентарь под ним.
  • Серверный InventoryService получил 3 новых метода: TryDestroy (удалить слот/уменьшить Count), TrySplit (раздвоить стэк), TryRemove (снять и вернуть для последующего SpawnGroundItem). Все помечают сессию dirty — Broadcaster сохраняет инвентарь в БД раз в 5 сек.
  • Подсказка обновлена: «ЛКМ — перетащить · ПКМ — разделить стэк · R — повернуть · вне окна — выкинуть · I или Esc — закрыть».
  • Backward-compatible: формат БД (InventoryJson), старые пакеты, .usmap — ничего не менялось.
v0.0.39 19 мая 2026

Новый ресурс «Высокая трава» + предмет «Волокна»

Спрайт высокой травы
tall_grass · 32×32
Иконка предмета волокна
fiber · 32×32
  • На карте появились пучки высокой травы. Гораздо чаще, чем берёзы (10% травяных тайлов против 4% у берёз). Спрайт 32×32 — лежит ровно на тайле, не торчит выше как берёза. Не блокирует движение: можно идти прямо сквозь траву как по обычному тайлу. Одно нажатие E — пучок сорван.
  • Спрайт через Gemini. Промт через MCP openrouter-image: 6-9 травяных лезвий веером от общего основания, тёмно-зелёная палитра, жёлто-зелёные кончики, тонкие тан-колоски-семенники, овальная тень у основания, 1-px outline #1A1A1A. Raw 1024×1024 → SpriteGen process-resource → финальный 32×32 PNG с alpha.
  • Новый предмет «Волокна» (fiber). 1×1 в гриде инвентаря, стек до 99, вес 0.1 кг, категория «ресурс». Иконка 32×32 — пучок дрика сухих волокон в тёплых тан-коричневых тонах. С каждого пучка травы выпадает 1–2 волокна (гарантированно). Будущая основа для верёвок / тетивы лука / факелов.
  • Связка end-to-end готова. Игрок → E на пучке травы → сервер удаляет, broadcastит ResourceRemoved, применяет Drops, InventoryService.TryAdd(fiber, 1..2), шлёт InventoryUpdate. Если инвентарь полный — волокна падают на тайл, подбираются E. Новых пакетов не понадобилось — обе системы (ресурсы и предметы) уже type-agnostic.
  • Map Editor: новая кисть «🌾 Высокая трава» в секции «РЕСУРСЫ» рядом с «🌳 Берёза». В статистике карты — счётчик «травы N».
  • Per-biome density: высокая трава спавнится только на тайлах Grass (0.10 плотность). На песке и камне — никогда. Логично: травы там и так нет.
  • Без миграции. Формат .usmap не изменился (v2/v3 как было) — старые карты остаются валидны. ResourceType.TallGrass = 2 — следующий свободный байт после BirchTree = 1, не переиспользует.
v0.0.38 19 мая 2026

Хотфикс: поворот предметов рисуется правильно

  • Баг 1: R-поворот растягивал картинку, а не поворачивал. В v0.0.37 при нажатии R во время drag спрайт wood'а (native 32×64) рисовался растянутым в 64×32 рамку как горизонтальная «лента», потому что DrawTextureRect не вращает текстуру — он только меняет размер. Теперь через новый хелпер DrawTextureFitRotated с DrawSetTransform(center, π/2, 1) текстура реально поворачивается на 90°.
  • Баг 2: drag-ghost у уже-повёрнутого предмета показывал неправильную ориентацию. При подъёме горизонтально лежащего бревна (которое лежит в гриде с Rotated == true, GridWidth=2, GridHeight=1 в БД) рамка ghost'а становилась 1×2 вместо 2×1. Причина — двойной swap: _dragRotated инициализировался как slot.Rotated (для повёрнутого предмета = true), а БД уже хранит swapped размеры. Заменили на swap = _dragRotated != item.Rotated — swap только при реальном изменении ориентации через R.
  • Drop-позиция тоже исправлена. Раньше горизонтальный предмет при отпускании мыши приземлялся со смещением в 1 клетку — побочный эффект Бага 2. Теперь сетка цели рассчитывается с правильным swap.
  • Применено везде: DrawGridItem (предметы лежащие в гриде), DrawDragGhost (ghost при drag), DrawEquipCell (предметы в equipment-слотах) — все три прохода теперь рисуют повёрнутые предметы реально повёрнутыми, а не растянутыми.
  • Backward-compatible. Пакеты, БД и конфиги не менялись — фикс чисто клиентский. Серверные сборки v0.0.37 совместимы; обновлять стоит только клиента.
  • Скил inventory пополнен граблями: «GridWidth/Height уже swapped в БД при Rotated=true — не делай double-swap» и «DrawTextureRect не вращает — используй DrawSetTransform».
v0.0.37 19 мая 2026

Предметы и первый предмет «Дерево»

Спрайт берёзы
birch_tree · 32×48
Иконка предмета дерево
wood · 32×64
  • Связь добычи и инвентаря наконец замкнулась. До v0.0.37 у нас были по отдельности: ресурсы (берёзы) и инвентарь (с плейсхолдером test_block). Теперь срубаешь берёзу → выпадает «Дерево» (item id wood) и реально оказывается в инвентаре игрока — с стэком до 99 в одной клетке, сохранением в БД, drop-on-ground при переполнении, и подбором по клавише E.
  • Иконка wood.png 32×64 px — AI через Gemini. Промт через MCP openrouter-image: вертикальное берёзовое полено, белая кора с тёмными штрихами-лентицелями, торец с годовыми кольцами (выраженные кольца тан/коричневый/тёмная сердцевина). Raw 1024×1024 PNG (~750 КБ) → SpriteGen process-resource (chroma-key + bbox + crop + area-downsample + alpha-binarisation) → финальный 32×64 PNG с alpha. Тот же pipeline, что у берёзы-ресурса.
  • ItemConfig расширен грид-полями для всех 8 предметов. gridWidth/gridHeight/equipSlot/capacityBonusCells теперь у wood (1×2), stone/iron_ore (1×1), iron_sword (1×3, MainHand), leather_armor (2×2, Body), bread/meat (1×1), wolf_pelt (1×1). test_block удалён — больше не нужен.
  • InventoryService.TryAdd — алгоритм stack-then-place. Сначала достекает в существующие стэки этого ItemId (до StackSize), потом для остатка ищет свободное место в гриде (скан слева-направо, сверху-вниз). Возвращает фактически положенное; остаток (=desired-placed) — для drop-on-ground.
  • HandleHarvest применяет ResourceConfig.Drops. На последнем ударе по ресурсу для каждого DropEntry roll'ится Chance, считается count = Random[QuantityMin..QuantityMax], идёт TryAdd. Остаток (не влез) → SpawnGroundItem на тайле срубленного дерева. Игроку шлётся InventoryUpdate с новым состоянием.
  • Drop-on-ground — полная система. Сервер: WorldStateService._ground: Dictionary<(tx,ty), List<GroundEntry>>. На тайле может лежать несколько стэков разных ItemId, одинаковые сливаются. Если тайл занят — BFS до 2 тайлов радиусом находит свободный сосед. Транзитное состояние (не persist'ится в .usmap пока — TODO v0.0.38+).
  • 5 новых сетевых пакетов (0x50, 0x60..0x63): ItemConfigSnapshot (Сервер→клиент при EnterWorld, словарь ItemDef для tooltip'ов), WorldGroundItemsSnapshot, GroundItemSpawned, GroundItemRemoved (broadcast'ы), ItemPickupRequest (Клиент→сервер: {tileX, tileY, itemId}).
  • Клавиша E теперь универсальная «interact». Сначала TryPickupNearest — если в радиусе HarvestRangePx (64 px) есть ground item, шлёт ItemPickupRequest. Если ничего нет — fallback на TryHarvestNearest (как раньше). Pickup приоритетнее: добытое сразу подбирается без переключений.
  • Стартовый инвентарь — ПУСТОЙ. В v0.0.35–v0.0.36 у новичков был зашитый test_block; теперь всё через добычу. Старые персонажи v0.0.35-эпохи потеряют test_block при логине (item-id больше не в конфиге — слот игнорируется).
  • Tooltip с реальными данными. Hover на «Дерево» → панель «Дерево / Ресурс · 1 кг · 1×2 · стек до 99». Данные приходят с сервера через ItemConfigSnapshot; клиент держит NetworkSession.KnownItems. Категории локализованы (item.category.resource/weapon/armor/food/misc) ru/en.
  • Общий UiTooltip.cs — компонент, который заменил inline-tooltip в InventoryScreen. Один экземпляр на сцену, потребители зовут ShowText(title, details)/MoveTo(globalPos)/HideTooltip(). В будущем будет использоваться и в HUD карты, и в чате, и в admin-командах.
  • Новый скил items — единый source-of-truth (.claude/skills/items/SKILL.md): mental model, архитектура файлов, lifecycle (creation/storage/removal paths), AI-генерация иконок, гайд по выбору GridWidth × GridHeight, 10-шаговый чек-лист добавления нового предмета, грабли, anti-patterns.
v0.0.36 19 мая 2026

Инвентарь: переработка UI + фикс R-поворота

  • Новый «манекен» персонажа. AI-сгенерированный человек в T-позе с расставленными руками (256×448 px, Vitruvian-style, через MCP openrouter-image/Gemini): кожа натуральная, льняная рубаха+штаны, босой, без оружия. Анатомически читаем — голова, шея, торс, кисти на концах рук, ступни.
  • Слоты размещены анатомически. Полностью переработан layout — каждый из 10 equipment-слотов 36×36 стоит там, где соответствующая часть тела: Head на черепе, Amulet на шее, Body на груди, Backpack за плечом, Hands на руке, MainHand/OffHand в кистях, Ring1/Ring2 на пальцах, Feet на стопах. Больше не «слоты разбросаны бессистемно».
  • Пустые слоты показывают placeholder-символ (⚔/⛨/○/▲/◆/▣/✋/▼/□) — намёк на тип экипировки. При hover на пустом слоте — tooltip с локализованным названием («Шлем», «Амулет», «Меч»...). Помогает понять куда что класть до появления реальных предметов.
  • Новый фон inventory_bg.png — перегенерирован под широкое окно. Резная славянская рамка, ornate brass-углы, чистый пергамент внутри. Палитра прежняя — тёмное дерево + cream + золото.
  • Окно расширено с 640×440 до 880×600. Левая панель — под большой манекен. Правая — под грид + место для индикатора веса. Анимация открытия (FadeIn + Scale 0.95→1.0 за 0.2 с) сохранена.
  • R-поворот починен. В v0.0.35 R не работал — корневой Control в Godot без FocusMode не получает keyboard events через _GuiInput, событие молча терялось. Перенесли в _Input(InputEvent) с SetInputAsHandled. Теперь во время drag нажатие R моментально поворачивает ghost-preview и при drop сервер применяет поворот.
  • Заголовки секций «Экипировка» и «Сумка» через Label-ноды с outline для читаемости. Tooltip на пустых слотах — мгновенный (без задержки), на занятых предметах — через 0.5 с (как было).
  • Backward-compatible. Структуры данных, сетевые пакеты и БД-схема не менялись — все персонажи v0.0.35 продолжают работать как ни в чём не бывало.
v0.0.35 19 мая 2026

Инвентарь (только система)

  • Клавиша I открывает инвентарь. Overlay-окно поверх карты в стиле «Аллодов 1/2» — тёмное дерево с резной рамкой и brass-углами, пергаментная зона внутри (AI-фон через Gemini, MCP openrouter-image). Открытие — FadeIn+Scale (0.95→1.0) за 0.2 с. Esc сначала закрывает инвентарь, потом выходит в меню.
  • Слева — equipment-силуэт. Затенённый «манекен» персонажа в центре + 10 слотов по периметру: Голова / Тело / Руки / Ноги / Рюкзак / Оружие / Щит / Амулет / Кольцо ×2. Каждый слот 36×36 px с brass-рамкой и подписью под низом.
  • Справа — грид 6×4 клеток в Tetris-стиле. Предметы занимают прямоугольник W×H клеток (1×1 — кольцо/еда, 1×2 — меч/брусок, 2×2 — броня). Если в Backpack-слоте лежит предмет с CapacityBonusCells > 0 — грид растёт по высоте (рюкзак с +12 = 6×6).
  • Drag-and-drop мышкой. Левый клик на предмет — берём; движение мыши — ghost-preview под курсором; клик на свободную ячейку грида или совместимый equip-слот — отпускаем. R во время drag поворачивает предмет (меняет видимые W/H местами).
  • Тестовый «брусок» 1×2. На время теста у каждого нового персонажа в гриде лежит зашитый test_block (32×64 px серый плейсхолдер с надписью «TEST»). Перетаскивай, поворачивай, закрывай инвентарь и открывай снова — позиция сохраняется. Реальные предметы и интеграция с добычей берёзы — следующее ТЗ.
  • Сервер — авторитет. 5 новых пакетов: InventorySnapshot (0x40), InventoryMoveRequest (0x41), InventoryEquipRequest (0x42), InventoryUnequipRequest (0x43), InventoryUpdate (0x44). InventoryService проверяет: предмет существует, целевая позиция свободна (Tetris-проверка пересечений прямоугольников), EquipSlot совместим. Никакого доверия к клиенту.
  • Вес и вместимость. Базовая вместимость = 6×4 = 24 клетки. MaxWeight = Strength × 5, Strength = 10 — заглушка (v0.0.36+: реальные характеристики). Перегруз не блокирует подъём, только индикатор.
  • БД. Новая миграция AddCharacterInventory — колонка InventoryJson TEXT в characters. Broadcaster.PersistInventories раз в 5 сек сохраняет «грязные» инвентари. При CreateChar дефолтный инвентарь сразу пишется в БД.
  • Хотфикс v0.0.34: MapView теперь принимает .usmap v3 (раньше падал с «версия формата 3 не поддерживается» при загрузке свежего файла из v0.0.34).
  • Скил inventory — полная архитектурная документация для Claude Code: data flow, чек-лист добавления нового equipment-слота, грабли и anti-patterns. Активируется при запросах «добавь предмет / слот / инвентарь / drag&drop».
v0.0.34 19 мая 2026

Тонкая настройка ресурсов в Admin

  • Per-biome плотность спавна. Вместо одного spawnDensity теперь словарь spawnDensityByTile: { Grass: 0.04, Sand: 0, Stone: 0.001 } — частые берёзы в лесу, нет в пустыне, изредка на скалах. В Admin → «Ресурсы» три отдельных поля NumericUpDown под три биома.
  • HP (прочность) ресурса. Новые поля hpMin / hpMax — каждое дерево/камень при генерации получает случайное HP. Каждое нажатие E игроком снимает 1 (сервер-авторитарно). HP > 0 → broadcast ResourceDamaged; HP = 0 → ResourceRemoved. Клиент рисует HP-бар над повреждёнными ресурсами (28×3 px, зелёный→красный). На v0.0.34 damage всегда = 1 (кулак, без инвентаря); каркас под топоры готов.
  • Drops — список с шансом. Раньше — один dropItemId. Теперь — [{itemId, quantityMin, quantityMax, chance}, ...]. В Admin кнопка ➕ «Добавить предмет», у каждой строки ✕ удалить. Несколько одинаковых ItemId допускаются (wood ×2..4 chance=1.0 + rare_seed ×1 chance=0.05).
  • Группы ресурсов — новая вкладка в Admin. Концепция «лесов и рудных жил». ResourceGroupConfig {id, spawnOnTiles, targetCount, radiusTiles, densityInGroup, entries[]}. Генератор подбирает N случайных центров в подходящих биомах и в круге радиуса R рассыпает ресурсы группы по weighted-random выбору (entries с долями). В дефолтном конфиге — пример «Берёзовая роща» (3 кластера × 8 тайлов).
  • Сложение спавнов. Сначала индивидуальный per-biome scatter, потом группы — поверх свободных тайлов (НЕ перетирают). Можно иметь «одиночные деревья по всему лесу» + «явные густые рощи» одновременно.
  • .usmap v3. После блока ресурсов идёт третий W×H блок — текущий HP каждого тайла. v1/v2 файлы читаются обратно-совместимо (HP=1 для всех живых). Размер 128×128: 33 КБ → 49 КБ. Старые файлы сервер автоматически перегенерирует в v3.
  • Новый пакет ResourceDamaged (0x33). Бродкастится после каждого не-смертельного удара. Клиент обновляет HP в локальном слое, рисует HP-бар.
  • Авто-миграция старых конфигов. ResourceConfig.Migrate() вызывается в ConfigLoader.LoadFromFile: если в JSON ещё старые поля, они переносятся в новый формат и зануляются. Идемпотентно. Пользовательский server-config.json v0.0.32–v0.0.33 не требует ручной правки.
v0.0.33 19 мая 2026

Берёза перерисована через AI

  • Спрайт берёзы заменён. Версия из v0.0.32 была нарисована процедурно в C# (bmp.SetPixel) — geometric, lifeless, off-style. Перерисована через Gemini 2.5 Flash Image (MCP openrouter-image): теперь крона — лохматая «брокколи» из комков листвы, ствол узкий белый с тёмными штрихами берёзы, мягкая тень у основания.
  • Новая команда SpriteGen process-resource <input> <output> <W> <H> — детерминированный конвейер AI-сурс → финальный pixel-art: chroma-key белого фона (Gemini игнорит «transparent background»), bbox непрозрачных пикселей, симметричный pad под аспект 32:48, area-averaging downsample + бинаризация альфы (порог 96). Результат: 32×48 PNG с чистой альфой и crisp-пикселями.
  • Новая папка tools/UniverseSurvival.SpriteGen/resource-sources/ — аналог textures/ для тайлов, но для ресурсов. Raw 1024×1024 PNG от Gemini (~750 КБ) commit'ится в git как источник истины: SpriteGen воспроизводим без повторных вызовов AI.
  • GenerateAllResources теперь по двум путям: сначала ищет resource-sources/<id>_raw.png и зовёт ProcessResource → если есть, используется AI-вариант; иначе fall-back на старый процедурный рисователь. Никаких потерь функциональности, AI-путь стал основным.
  • Скил resource-objects переписан. §1a делает AI-генерацию основным путём с готовым промт-шаблоном для любого нового ресурса (заменить только название + палитру). §1b — процедурный SpriteGen как fallback. §1c — жёсткие правила стиля. §8 пополнился anti-pattern «don't draw resource sprites procedurally pixel-by-pixel» с прямой ссылкой на ошибку v0.0.32.
  • Backward-compatible. Структура birch_tree.png та же (32×48 PNG с alpha) — клиенты v0.0.32 работают; после автообновления увидят более красивое дерево.
v0.0.32 18 мая 2026

Ресурсы: первый вид — берёза

  • На карте появились деревья. Генератор мира раскидывает берёзы по травяным тайлам с плотностью ≈ 4% (≈ 500 деревьев на 128×128). Игрок подходит и нажимает E (или Space) — ближайшее дерево в радиусе 64 px пропадает. Срубленный ствол блокирует движение, как и вода.
  • Серверно-авторитарная добыча. Клиент шлёт HarvestRequest { tx, ty }, сервер валидирует расстояние и наличие ресурса в своей памяти, удаляет и бродкастит ResourceRemoved всем игрокам в мире. Клиент локально ресурс НЕ убирает до подтверждения — чит «срубить через всю карту» отсекается на сервере.
  • Спрайт берёзы 32×48 — белая кора с тёмными штрихами (характерная берёзовая фишка), зелёная крона из «комков» листвы, плоская корневая шейка. Якорь bottom-center (как у персонажа). Сгенерирован процедурно тем же кодом, что плейсхолдер-персонаж — светотень, контур #1A1A1A и тень-овал совпадают, ничего «инородного» в кадре.
  • Формат .usmap v2. После блока тайлов идёт второй блок W×H байт ресурсов (один байт = ResourceType). Загрузчик читает v1 и v2; v1-файлы автоматически регенерируются в v2 при следующем старте сервера. Размер 128×128: с 16 КБ → 33 КБ. Структура «параллельный массив», а не upper-биты TileType — 255 видов ресурсов независимо от типов тайлов.
  • 3 новых сетевых пакета (0x30..0x32): WorldResourceSnapshot (С→К, шлётся новичку при EnterWorld), HarvestRequest (К→С), ResourceRemoved (С→К всем в мире). Snapshot ≈ 6 байт × n_resources — 3 КБ для 500 деревьев, ОК для UDP.
  • Map Editor получил отдельную секцию «РЕСУРСЫ» с инструментами «🌳 Берёза» и «✂ Срубить ресурс». ПКМ теперь чистит ОБА слоя (тайл + ресурс) — стандарт пиксель-арт-редакторов. В статистике — счётчик берёз.
  • Admin — новая секция «Ресурсы» с выпадающим списком и формой: id, typeByte, density, spawnOnTiles, yield min/max, dropItemId, harvestTimeSeconds, isBlocking. Применяется при следующей генерации мира.
  • Новый action interact (E + Space) в InputBindings — 6-й по счёту. Локализован в ru/en, переназначается в Настройках → Управление как любой другой.
  • Новый скил .claude/skills/resource-objects/SKILL.md — инструкция, как добавить следующий вид ресурса (дуб, камень-объект, куст ягод). Чек-лист 9 шагов от enum до docs.
v0.0.30 18 мая 2026

Меню настроек: графика, язык (ru/en), звук

  • Заглушка «Настройки» из v0.0.22 заменена на полноценный экран с тремя вкладками. Изменения применяются СРАЗУ — кнопка «Назад» только сохраняет всё на диск (user://settings.json).
  • Графика: режим окна (оконный / без рамки / полноэкранный) + разрешение из списка 11 стандартных размеров современных мониторов — от 1280×720 (HD) до 3840×2160 (4K UHD), включая 16:10 (1440×900, 1680×1050, 1920×1200) и ultra-wide (2560×1080, 3440×1440). Полноэкранный — ExclusiveFullscreen, без декораций ОС.
  • Язык: Русский и English. Все строки UI вынесены в JSON-словари client/i18n/{код}.json. Смена применяется в РАНТАЙМЕ (без перезапуска): SettingsScreen перерисовывает свои надписи через событие OnLanguageChanged, остальные экраны при следующем _Ready. Дефолт — ru, fallback на ru если в выбранном языке ключа нет (плюс WARN в лог — помогает выявить недопереводы).
  • Звук: два слайдера 0..100 (эффекты + музыка). Реально звуков и музыки в игре пока нет — это заглушки. Значения сохраняются и применятся к Godot AudioBus'ам Sfx/Music, когда они появятся.
  • Архитектура: GameSettings.cs (статика с persist в user://settings.json, методы Load/Save/Apply) + Localization.cs (статика, грузит словари через Godot.FileAccess, T(key) + T(key, args) с подстановкой {name}). MainMenu._Ready вызывает их ДО сетевых операций — окно и язык корректны с первого кадра.
  • Локализованы 6 экранов: MainMenu, LoginScreen, RegisterScreen, CharacterSelect, SettingsScreen, CreditsScreen + HUD в MapView. В .tscn остаются русские дефолты (для превью в Godot-редакторе) — скрипты их перезаписывают в рантайме.
  • Документация: docs/i18n.md — как добавить новый язык за 3 шага (скопировать JSON, перевести значения, дописать запись в Localization.AvailableLanguages), соглашения по именам ключей, что НЕ переведено.
v0.0.29 18 мая 2026

Фикс: при входе в мир видишь тех, кто уже там

  • Симптом: два клиента, оба в мире. Тот, кто зашёл первым, ВИДИТ второго. Тот, кто зашёл вторым, второго НЕ видит — пустая карта. Асимметрия.
  • Причина: race между EnterWorldResponse (триггерит ChangeSceneToFile — деферренное переключение сцены) и сразу следом WorldStateSnapshot со всеми уже-в-мире игроками. К моменту прихода snapshot — CharacterSelect уже отписан, MapView ещё не подписан → событие падает в пустоту.
  • Решение: в NetworkSession добавлен буфер KnownOthers (Dictionary). Все 4 multiplayer-пакета (Snapshot/Joined/Left/Positions) обновляют буфер ДО вызова события — независимо от того, есть подписчики или нет. MapView._Ready после подписок сразу читает буфер и спавнит спрайты для всех уже-известных. ClearWorldState чистит буфер.
  • На сервере ничего не менялось — баг был чисто клиентский, race-condition Godot lifecycle.
v0.0.28 18 мая 2026

Lockfile против зомби-процессов сервера

  • Что было сломано: в третий раз подряд (v0.0.25, v0.0.27, теперь снова) пользователь сообщил «сервер не запускается». Каждый раз диагноз один: предыдущий Server.exe остался жить в памяти (после неудачного завершения / убийства окна крестиком / зависшего теста) и держит UDP-порт 9050. Новый запуск падал на SocketException (10048) AddressAlreadyInUse. Понятное FATAL-сообщение печаталось, но требовало taskkill руками.
  • Решение: сервер при старте захватывает server.lock рядом с exe эксклюзивно (FileShare.None). Если файл занят — другой Server.exe уже работает, новый отказывается стартовать ДО попытки открыть UDP-порт. FATAL-сообщение явно говорит: «Сервер уже запущен — lockfile эксклюзивно занят. Кто это: tasklist | findstr. Убить: taskkill /PID <pid> /F».
  • Корректно с stale lockfile: когда процесс умирает (нормально / жёстко / краш), ОС автоматически отпускает lockfile-handle. Следующий запуск открывает файл успешно, видит stale PID и перезаписывает, залогировав «Lockfile захвачен: pid=N. Перезаписан stale PID M».
  • При port conflict не от нас: если lockfile свободен, но server.Start всё равно падает — значит порт держит ЧУЖОЙ процесс (не наш Server.exe). Сообщение уточнено: «Lockfile свободен — значит порт держит ЧУЖОЙ процесс. Кто слушает: netstat -ano | findstr :9050».
  • Smoke-тест: запустил 2 сервера подряд. Первый: «Lockfile захвачен pid=21376». Второй: «FATAL: Сервер уже запущен» + exit code 6. После kill — следующий запуск чистый.
v0.0.27 18 мая 2026

Хотфикс v0.0.26: multiplayer теперь реально работает

  • Симптом у пользователя: «при регистрации кнопка зарегистрироваться всегда неактивная, даже если заполнены все поля».
  • Расследование по логам: на сервере клиент успешно подключался ([+] peer=0 подключился), но клиент НЕ видел этого ответа — статус навсегда оставался Connecting, кнопка дисейблилась навечно.
  • Корень — две сцепленные Godot-грабли: (1) _PollerNode был nested-классом внутри NetworkSession — Godot source generator не генерит method-bindings для nested Node-классов, _Process никогда не зовётся, PollEvents() не дёргается, события LiteNetLib молча копятся в очереди. (2) После выноса в top-level — tree.Root.AddChild() из MainMenu._Ready силентно не сработал (Root заблокирован во время инициализации сцены).
  • Решение: top-level NetworkPoller : Node в отдельном файле + tree.Root.CallDeferred(Node.MethodName.AddChild, poller). Полная цепочка в логе теперь: «NetworkPoller добавлен в Tree.Root → подключено к 127.0.0.1:9050 peer=0».
  • UX-фикс параллельно: кнопки в формах больше НЕ дисейблятся по состоянию сети. Активность только по валидности полей. На клике, если сеть оторвалась — ошибка «Нет соединения с сервером — пробую переподключиться» и автоматический retry Connect(). Пользователь видит причину, а не мёртвую кнопку.
  • Грабли зафиксированы в памяти — больше не встанем дважды.
v0.0.26 17 мая 2026

Multiplayer: клиент и сервер связаны, игроки видят друг друга на карте

  • Главное: конец одиночного режима. Регистрация и логин идут на сервер (SQLite), список персонажей грузится с сервера, при входе в мир сервер выдаёт точку спавна и snapshot всех уже онлайн-игроков, потом рассылает их позиции 10 раз в секунду. Запусти сервер + два Universe Survival.exe — увидите друг друга на карте.
  • Чужие игроки рисуются как Sprite2D с именем над головой (мужской — голубоватый тон, женский — розоватый). Lerp 12/sec сглаживает рваность 10-герцовых апдейтов.
  • Новый протокол — отдельный проект shared-network/ (net8.0, общий с Godot и сервером): 14 типов пакетов (Register/Login/ListCharacters/CreateCharacter/EnterWorld/MoveCommand/WorldStateSnapshot/PlayerPositions/PlayerJoined/PlayerLeft + responses). Все с Write(NetDataWriter) / Read(NetDataReader).
  • Server-side: SessionTable (peerId→состояние), PacketDispatcher (switch + EF Core Linq), Broadcaster (tick-loop 10 hz собирает «грязные» позиции в один батч + раз в 5 сек сохраняет в БД). Новые поля CharacterEntity.LastX/LastY + миграция AddCharacterPosition.
  • Client-side: статический NetworkSession с невидимым PollerNode в Tree.Root (переживает смену сцен — не autoload, чтобы не нарваться на C#-autoload-граблю Godot 4.6). 14 C#-events, сцены подписываются в _Ready. В MainMenu — индикатор ● Онлайн / ⟳ Подключение / ○ Не в сети / ✗ Сервер недоступен. Новая сцена LoginScreen.tscn между MainMenu и CharacterSelect.
  • Локальное хранение убрано: AccountStore.cs и CharacterStore.cs удалены, БД — единственный источник истины.
  • Что НЕ сделано (специально): позиция игрока не строго server-authoritative — клиент двигается сам и просто шлёт координаты, сервер валидирует только границы карты (вода НЕ проверяется на сервере). «Сервер-как-broadcast» — упрощёнка для соло-разработки. Настоящий serverside-auth (с client prediction + reconciliation) — задача под нагрузочные тесты.
  • Соединение: UDP 9050, по умолчанию 127.0.0.1 (зашито, потом вынесем в конфиг клиента).
v0.0.25 17 мая 2026

Файловое логирование всего: ошибки startup'а больше не теряются

  • Что было сломано: в v0.0.24 пользователь сообщил «сервер не запустился». Расследование показало: сервер на самом деле стартовал, но UDP-порт 9050 был занят зомби-процессом от предыдущего теста. Сервер корректно печатал ошибку в консоль — но cmd-окно от ярлыка закрывалось мгновенно. Видимое поведение: «запустился — закрылся — ничего непонятно».
  • Решение: единый логгер UniverseSurvival.Shared.Logging.Log для всех .NET-программ + параллельная копия client/scripts/Log.cs для Godot. Пишет одновременно в консоль (с цветом для warn/error/fatal) и в файл — server: logs/server-YYYY-MM-DD.log рядом с exe; Godot client: user://logs/client-YYYY-MM-DD.log (на Windows — %APPDATA%\Godot\app_userdata\Universe Survival\logs\). Append-режим, одна сессия = разделитель с PID и timestamp.
  • Перехват исключений через AppDomain.UnhandledException и TaskScheduler.UnobservedTaskException — даже асинхронный сбой попадает в файл с полным stack trace.
  • При port-conflict сервер теперь сам подсказывает что делать (три FATAL-строки): tasklist | findstr UniverseSurvival.Server, netstat -ano | findstr :9050, taskkill /PID <pid> /F.
  • При fatal-ошибке в интерактивном режиме сервер ждёт нажатия клавиши перед выходом (через Console.IsInputRedirected-проверку) — чтобы пользователь успел прочитать ошибку. В CI/pipe режиме просто выходит с exit code.
  • Smoke-тест: запустили два сервера подряд, второй залогировал FATAL: Не удалось открыть UDP-порт 9050 с тремя подсказками — всё видно в файле после факта.
  • TODO v0.0.26+: подключить логгер в Admin/MapEditor (Avalonia) для единообразия, добавить ротацию старых логов (удалять файлы старше N дней).
v0.0.24 17 мая 2026

БД: пивот с PostgreSQL на SQLite — zero install для dev-стадии

  • Что случилось: в v0.0.23 подключил полноценный Postgres — оказался оверкилл для соло-разработки (200 МБ инсталлятор, UAC, пароль, env-переменная, отдельный setup-скрипт). Переехал на SQLite — та же EF Core схема, но zero install: файл universesurvival.db создаётся рядом с server.exe при первом запуске.
  • Postgres вернётся когда подойдём к нагрузочному тестированию (~1000 онлайн — SQLite столько concurrent writes не тянет). План возврата зафиксирован в docs/database.md и в комментариях кода — поменять пакет, UseSqliteUseNpgsql, расширить DatabaseConfig, перегенерировать миграции.
  • Что упростилось: DatabaseConfig с 8 полей до 3 (enabled с дефолтом true, path, applyMigrationsOnStart). NuGet: убран Npgsql, добавлен Microsoft.EntityFrameworkCore.Sqlite 9.0.0. GameDbContext стал провайдеро-нейтральным (нет timestamptz). DatabaseBootstrap без проверки env-пароля. Удалены tools/pg-setup.bat и старая Postgres-миграция.
  • Новая миграция InitialCreate под SQLite: INTEGER PRIMARY KEY AUTOINCREMENT вместо bigint identity, TEXT вместо varchar. Структура схемы та же — accounts + characters с FK CASCADE. Foreign Keys включены явно через Foreign Keys=True в connection string (SQLite по умолчанию их игнорирует).
  • Smoke-тест прошёл: сервер на чистой машине запускается, создаёт .db (4КБ) + -wal + -shm, применяет миграцию, пишет ✓ База данных: SQLite '...'. Никаких внешних зависимостей.
  • Клиент по-прежнему пишет в локальные JSON. Учётки и персонажи из главного меню v0.0.22 продолжают жить в user://accounts.json/user://characters.json. Серверный UDP-протокол для регистрации/выбора персонажа — следующая итерация v0.0.25.
v0.0.23 17 мая 2026

PostgreSQL подключён: серверная схема, миграции, инструкции по установке

  • Что произошло: сервер теперь умеет работать с PostgreSQL. На старте читает server-config.json::database и, если enabled=true, подключается к Postgres, применяет миграции (создаёт таблицы при первом запуске), держит контекст живым. До этого в техстеке стояла галка «✅ Postgres», но реально БД не существовало.
  • Что в БД: две таблицы — accounts (Login + LoginNormalized + PasswordHash SHA-256 + CreatedAt + LastLoginAt) и characters (FK → accounts CASCADE + Name + NameNormalized + Age + Gender + CreatedAt + LastPlayedAt). Уникальные индексы по нормализованным логину и имени.
  • Graceful degradation: если БД выключена в конфиге, нет пароля в env, или Postgres недоступен — сервер не падает, печатает понятную диагностику и продолжает работу без БД. Это сознательное решение, чтобы dev-машины без поднятого Postgres продолжали работать.
  • Клиент пока не подключён. На v0.0.23 учётки и персонажи из главного меню v0.0.22 по-прежнему пишутся в локальные user://accounts.json/user://characters.json. БД готова, но протокол UDP-пакетов для регистрации/выбора персонажа перепишем в v0.0.24 — это отдельная безопасная итерация.
  • Как поставить локально: установить PostgreSQL 16 (winget/официальный msi/Docker), запустить tools\pg-setup.bat — он создаст БД и пользователя, подскажет setx UNIVERSESURVIVAL_DB_PASSWORD, перевести database.enabled = true в конфиге. Полная инструкция — docs/database.md.
  • Технически: NuGet Microsoft.EntityFrameworkCore 9.0.0 + Npgsql.EntityFrameworkCore.PostgreSQL 9.0.2. Новая папка server/Db/: GameDbContext, сущности AccountEntity/CharacterEntity, GameDbContextFactory (для dotnet ef migrations add), DatabaseBootstrap (старт-логика), Migrations/InitialCreate.
  • Чужой Postgres не используется. На стороннем хостинге, где временно стояла только статика сайта, был доступен Postgres соседнего проекта владельца — но трогать чужую инфраструктуру нельзя. Универсальная БД — локальная или собственная.
v0.0.22 17 мая 2026

Главное меню игры: фон, Войти / Регистрация / Настройки / Авторы / Выйти

  • Что видно глазами: при запуске Universe Survival.exe больше не падает сразу в карту — теперь открывается полноэкранное главное меню с пейзажным фоном (трава, песочный берег, синяя вода, серые камни — в стиле игровых тайлов) и пятью кнопками по центру.
  • Войти → экран «Выбор персонажа»: слева список ранее созданных, справа форма создания нового (имя / возраст 10–120 / пол м/ж). После выбора — переход в игру (старая TestMap). В игре Esc возвращает в меню. HUD показывает имя текущего персонажа.
  • Регистрация → логин + пароль ×2 + капча-арифметика «Сколько будет X + Y = ?» с кнопкой ↻ для обновления. Проверяет совпадение паролей, верность ответа, уникальность логина.
  • Настройки → пока заглушка «Здесь пока пусто. Появятся в следующих версиях.» + ← Назад.
  • Авторы → Всесоздатель: TurpisPopulus. Программист, художник, звукорежиссёр, уборщик, ленивец: CLAUDE CODE.
  • Хранение пока локальное. Серверного auth/БД (PostgreSQL) ещё нет, поэтому учётки и персонажи пишутся в user://accounts.json и user://characters.json (на Windows — %APPDATA%\Godot\app_userdata\Universe Survival\). Пароли хешируются SHA-256. Это шаблон под будущий сервер.
  • Фон — снова MCP openrouter-image (Gemini). Сгенерирован с референсом style-references/grass-water-reference.png — тот же визуальный стиль, что и у тайлов. Лежит в client/assets/ui/menu_background.png и используется во всех 5 сценах меню.
  • Технически: 5 новых сцен на Control + 5 скриптов + статический класс SessionState (держит выбранного персонажа между сценами; статика C# живёт до выгрузки DLL — это работает без autoload, которая на свежих C#-скриптах в Godot 4.6 не регистрируется) + локальные хранилища AccountStore/CharacterStore. run/main_scene в project.godot переключён с TestMap.tscn на MainMenu.tscn.
v0.0.21 17 мая 2026

Скилл tile-system для Claude Code: формализован пайплайн тайлов

  • Зачем: архитектура тайлов разрослась — blob47 + base/overlay-листы + два места ComputeMask (shared C# и Godot) + MatchesAsNeighbor-группы + переходы под каждую пару материалов + 5 файлов на каждый новый биом. Без формализации Claude забывает половину при следующей задаче.
  • Что в скилле: архитектурный обзор, чек-лист добавления нового тайла на 9 шагов, дерево принятия решений «какой переход X↔Y хочу», описание трюка с инверсией маски для overlay-листов, грабли (.import-файлы Godot, две копии server-config.json, PowerShell BOM), верификация распределения биомов на тестовой генерации, релиз-чек-лист.
  • Где живёт: .claude/skills/tile-system/SKILL.md. .gitignore подправлен — раньше всё .claude/ было игнорено из-за worktrees Claude Code, теперь .claude/skills/ whitelisted и попадает под git. Сам скилл активируется триггерами в description: «добавь биом», «переход между», «SpriteGen», «blob47», «MapView», «MapEditor».
  • Что НЕ покрывает: персонажа, ресурсные объекты (деревья, камни-как-предметы), NPC, постройки. Каждое из этого — отдельный скилл, который добавим по мере появления функциональности.
v0.0.20 17 мая 2026

Переход трава↔камень: как песок↔трава, только камнем

  • Что видно глазами: на стыке леса и гор теперь рисуется ~5-пиксельная каменная полоса внутри grass-тайла с лёгкой волнистой границей — точно так же, как ~5-пиксельная песочная полоса на стыке трава↔песок, только текстура другая. Stone-тайл сам по себе остаётся однородным.
  • Что было в v0.0.19: мшистый overlay из новой moss_base.png (тёмный мох + земля + галька). Пользователь сказал «плохо смотрится» — слишком инородно. Откатили.
  • Архитектура та же. Тот же общий хелпер SpriteGen.GenerateBlob47Overlay, только теперь питается из stone_base.png: получаем stone_overlay.png — blob47-лист с прозрачным центром и каменными edge-пикселями. Клиент и Map Editor накладывают его на grass-тайл в направлении соседей-Stone через тот же трюк с инверсией маски (Canonicalize(~mask_stone)).
  • Только на одну сторону: на stone-тайле overlay не рисуется (был бы stone-on-stone, невидимый — нет смысла тратить draw-вызов). На grass-тайле в сторону соседа-Stone — рисуется. Это в точности соответствует логике трава↔песок (где sand-on-sand тоже невидим).
  • Удалено: moss_base.png и moss_overlay.png из v0.0.19 — больше не нужны.
v0.0.19 17 мая 2026

Мшистый переход трава↔камень: природное предгорье вместо резкого шва

  • Что видно глазами: в v0.0.18 убрали песок между лесом и горами, но осталась резкая граница «зелёный против серого». Теперь по обе стороны от стыка рисуется мшистая полоса (~10 px) из тёмного мха, земли и мелкой гальки — выглядит как природное предгорье, а не пиксельный шов.
  • Новая AI-текстура moss_base.png через MCP openrouter-image (Gemini). Тот же шаблон, что у других баз: равномерный пиксель-шум без направленных паттернов. Палитра — тёмно-зелёный мох + бурая земля + серые галечные вкрапления. Бесшовно тайлится.
  • Архитектура — overlay-слой. SpriteGen теперь делает общий blob47-overlay-хелпер: из любой edge-текстуры строит 47-ячеечный лист с прозрачным центром и edge-пикселями на «открытых» сторонах. Используется и для water_edges.png (выделено в v0.0.15), и для нового moss_overlay.png. Клиент и Map Editor накладывают этот лист поверх grass-тайла в сторону соседей-камней (и наоборот). Трюк с инверсией маски: overlay рисует пиксели в «открытых» направлениях, поэтому инвертируем neighbor-type-маску перед канонизацией → moss оказывается ровно в направлениях нужных соседей.
  • Что не тронуто: все остальные стыки (трава↔вода, камень↔вода, трава↔песок, камень↔песок) — там работает прежний sand-пиксельный край. Только пара grass↔stone получает мшистый overlay.
v0.0.18 17 мая 2026

Чистый стык трава↔камень: убрана песочная полоса между лесом и горами

  • Что видно глазами: до этого на границе леса и гор появлялась бежевая «песочная» полоса (тот же сэнди-«foothill», что между травой и водой). Это выглядело странно — между лугом и горами в природе нет пляжа. Теперь зелёный клин травы встречается с серым клином камня напрямую, без полосы посередине.
  • Технически — «группа суши». Добавлен метод TileTypeExtensions.MatchesAsNeighbor в shared/World/TileType.cs. Он возвращает true либо когда соседи одного типа, либо когда оба входят в «сухопутную группу» (Grass + Stone). При подсчёте blob47-маски соседи из той же группы считаются «совпадающими» — и в эту сторону не рисуется песочный край. На стыке трава↔камень оба тайла рисуют свою центральную текстуру до самой границы.
  • Что осталось как было: песочные переходы grass↔water (берег), stone↔water (каменистый берег с песком), grass↔sand и stone↔sand. Песок и вода — отдельные «не-сухопутные» группы, для них всё прежнее.
  • Логика в трёх местах: shared/World/TileType.cs::MatchesAsNeighbor — источник истины (сервер), shared/World/WorldMap.cs::ComputeMask — Map Editor, client/scripts/MapView.cs::MatchesAsNeighbor — Godot-клиент (дубликат, поскольку у клиента свой private enum). При добавлении новых компат-групп править оба места.
v0.0.17 17 мая 2026

Биом «каменистый»: серые скалы на высоких точках рельефа

  • Что видно глазами: на самых высоких местах карты появились серые скалистые пятна — горы. В отличие от пустыни, которая «сливалась» с песочными краями, камень — визуально отдельный материал: серый центр + та же сэнди-полоса по краям, что у травы и воды. На стыке трава↔камень получается естественное предгорье (foothill), на стыке камень↔вода — каменистый берег.
  • Технически — высотный порог. Был один порог рельефа (waterLevel — где кончается вода), теперь добавлен второй (stoneLevel — где начинаются скалы). На суше: если высота больше stoneLevel → Stone, иначе разбор по влажности (sand vs grass) идёт как раньше. Stone-проверка стоит **до** влажности, поэтому горы получаются и в пустынях, и среди лесов.
  • Текстура — снова MCP openrouter-image (Gemini). Промпт тот же шаблон, что и для grass/water/sand: равномерный пиксель-шум, без теней и направленных паттернов. Палитра — серые с тёплыми вкраплениями. Файл — tools/UniverseSurvival.SpriteGen/textures/stone_base.png, рядом с предыдущими тремя.
  • Параметры в server-config.json → world.generation: stoneLevel (0.65 по умолчанию; меньше = больше гор, 1 = камней нет). Map Editor получил кисть «⛰ Камень», в статистике карты — процент камня. Полное описание — docs/world-generation.md.
v0.0.16 17 мая 2026

Биом «пустыня»: песок, который не выделяется на карте

  • Что видно глазами: на карте появились крупные континентальные пятна песка — пустыни. Они выглядят ровно так же, как уже знакомый песочный берег между травой и водой, поэтому ничего «инородного» в кадре не появилось — биом просто продолжается из тех же бежевых границ.
  • Технически — второй Perlin-шум. Раньше генератор решал только «вода или суша» по одной карте высоты. Теперь у мира есть и вторая карта — карта влажности (отдельный seed, другой масштаб). На суше: где влажность ниже desertLevel → песок, иначе → трава. Шум влажности крупнее рельефа, поэтому пустыни получаются континентальными пятнами, а не мелкой крошкой. У самой кромки воды пустыни «отжимаются» лёгким коастбустом — чтобы не сливаться с песочным пляжем.
  • Почему пустыня не выделяется визуально. Это было прямым требованием: новый биом не должен «бросаться в глаза». Решение: в SpriteGen сгенерирована новая sand.png по той же схеме blob47, но и базой, и «переходом» там служит одна и та же sand_base.png — поэтому 47 канонических вариантов сливаются в ровный песок, а соседние трава/вода рисуют свой песочный край той же текстурой. На стыке двух тайлов получается визуально один материал.
  • Параметры в server-config.json → world.generation: desertLevel (0.38 — порог влажности, ниже которого суша становится песком), desertNoiseScale (0.035 — крупнее, чем шум рельефа), desertSeedOffset (9173 — другой seed). Map Editor получил кисть «🏜 Песок», в статистике карты — процент пустынь. В Godot-клиенте песок проходим (в отличие от воды). Полное описание — docs/world-generation.md и docs/sprite-style.md §12.
v0.0.15 17 мая 2026

Анимированная вода в Godot-клиенте: живая гладь, статичный берег

  • Что видно глазами: в клиенте поверхность воды теперь медленно «течёт» вниз с лёгким горизонтальным шиммером (8 кадров × ~6 fps, полный цикл ~1.33 сек). Песочные берега и стыки с травой при этом не двигаются — двигается только сама гладь. Раньше вода была полностью статичной картинкой.
  • Архитектура — гибрид «анимированная база + статичный край». Генератор спрайтов (SpriteGen) теперь выдаёт для воды три файла вместо одного: water.png (как раньше — для Map Editor, который не нуждается в анимации), water_edges.png (blob47-лист только с песочными краями, центр прозрачный — «дырка», через которую светится анимированная вода), water_anim.png (горизонтальный стрип из 8 кадров 32×32 — анимированная база). Клиент рисует каждый водный тайл в 2 прохода: сначала текущий кадр базы, сверху — соответствующая blob47-ячейка краёв.
  • Почему такая архитектура: если бы анимировали всю плитку целиком (классический способ — N копий спрайтшита), берег «плыл» бы вместе с водой, что выглядит неправильно (песок течь не должен). Гибрид решает это архитектурно и заодно экономит память (16 КБ вместо 200+ КБ).
  • Под капотом: общий хелпер IsSandPixel(x, y, mask) в SpriteGen — единая точка истины «этот пиксель — берег или вода?» — гарантирует, что граница в обычном water.png и в water_edges.png совпадает побитово. client/build-client.bat теперь всегда вызывает --headless --import перед export (а не только при первой сборке) — без этого новые PNG-ассеты в export-сборку Godot не попали бы. Полный пайплайн анимации описан в docs/sprite-style.md §13.
v0.0.14 17 мая 2026

Bugfix: правки в Admin (размер карты, вайп) теперь реально применяются

  • Симптом был такой: открываешь Admin → ставишь карту 64×64 → жмёшь «Wipe» → перезапускаешь сервер. А сервер стартует со старой картой 128×128 и без вайпа. Будто Admin ничего не сохранил.
  • Причина: у проекта две копии server-config.json — одна в репо-источнике server/, другая рядом с .exe сервера в server/bin/Release/net10.0/. Admin правил первую, а сервер читал вторую. Копия в bin/ обновляется только при пересборке (через <CopyToOutputDirectory>) — поэтому правки «зависали» в репо-источнике.
  • Фикс: ConfigLoader.ResolvePath теперь сначала ищет server/server-config.json подъёмом по дереву каталогов (DEV — работа из репо), а «рядом с exe» оставлен как fallback для prod-деплоя. Admin и Map Editor используют тот же общий поиск (ConfigLoader.FindRepoConfigUpwards()) — все три программы теперь гарантированно работают с одним файлом.
  • Что теперь работает: сценарий «Admin → поменять widthTiles/heightTiles → включить wipeOnNextStart=true + wipeConfirmText='WIPE' → сохранить → перезапустить сервер → получить новую карту нужного размера». Без пересборки сервера.
v0.0.13 17 мая 2026

Процесс разработки формализован: build-all.bat, релиз-чек-лист, актуальная папка go/

  • Одна команда — пересобрать всё. Появился build-all.bat в корне репозитория: за один проход собирает все 4 .NET-проекта (Server, Admin, MapEditor, TestClient) в Release, прогоняет client\build-client.bat (Godot export) и пересоздаёт ярлыки в go/. Поддерживает флаги --skip-godot и --skip-shortcuts.
  • В PROJECT.md появился раздел «Как поддерживать проект (релиз-цикл)» — таблица где какой .exe лежит, что пересобирать после каких изменений, как версионировать, как пушить, как деплоить сайт. В конце — формальный релиз-чек-лист для каждой задачи: собрать → закоммитить → запушить → задеплоить сайт.
  • Папка go/ теперь — главный способ проверки. Зафиксировано правило: после любой моей правки кода пользователь должен иметь возможность зайти в C:\Users\user\Desktop\universesurvival\go\ и запустить любой из 5 ярлыков на свежей сборке.
  • .gitignore дополнен: .claude/ (worktrees Claude Code), /go/ (ярлыки с абсолютными путями — не переносимы), сгенерированный сервером world.usmap.
  • Версия сервера в Program.cs подтянута к 0.0.13 (последние два релиза трогали только клиент/тайлы).
v0.0.12 17 мая 2026

Тайлы травы и воды: AI-текстура + песочные переходы между ними

  • Конец синих квадратных границ. Старые плейсхолдер-тайлы рисовались сплошным цветом, и трава встречалась с водой через резкую синюю полоску с прямыми углами. Теперь у обоих материалов органичная пиксель-арт текстура, а на границе появляется бежевый пляж с волнистым краем — как в эталонном style-references/grass-water-reference.png.
  • 3 базовые AI-текстуры сгенерированы через MCP openrouter-image (Gemini 2.5 Flash Image) и лежат в tools/UniverseSurvival.SpriteGen/textures/: grass_base.png, water_base.png, sand_base.png. Промпт строго требует равномерного шума без направленных паттернов — это нужно чтобы они бесшовно тайлились.
  • Переписан SpriteGen: новая функция GenerateMaterialTextured композитит 47 канонических blob47-вариантов из этих текстур. Алгоритм per-pixel: для каждой точки тайла считается расстояние до 4 сторон + 4 углов, и по маске решается «это базовая текстура или песочный переход». Границы пляжа слегка волнистые (deterministic sin-noise).
  • Тайлы seamless через wrap-blend: каждая 32×32 базовая текстура внутри себя смешивает пиксели у каждой стороны с зеркальной, гарантируя что соседние ячейки в Godot стыкуются без видимой сетки.
  • Map Editor никаких правок не понадобилось — он читает grass.png/water.png из тех же путей, новый стиль подхватился автоматически.
v0.0.11 17 мая 2026

Тестовый клиент карты в Godot: загрузка world.usmap, WASD, плейсхолдер персонажа

  • Игровой клиент теперь показывает мир. Главная сцена Godot грузит сгенерированный сервером world.usmap, рисует видимый кусок мира с blob47-автотайлингом (трава + вода) и спавнит персонажа в точке world.spawnPoint из server-config.json. Камера следует за игроком, зум ×2 (32-пиксельный тайл рендерится как 64×64 — стандарт пиксель-арта).
  • Управление WASD. Скорость ~5 тайлов/сек. Стрелки тоже работают. Вода непроходима; если спавн попал в воду — игрок ставится на ближайшую сушу.
  • Плейсхолдер-персонаж 32×48 — голый одиночный кадр в client/assets/characters/player/idle.png. Один спрайт, без анимаций — потом будем «надевать» одежду поверх.
  • Генератор спрайтов SpriteGen получил CLI с подкомандами tiles / character / all. По умолчанию теперь — справка, чтобы случайно не перезаписать AI-сгенерированные ассеты.
  • Сеть в тестовом клиенте не используется — это чисто визуальная проверка карты. Старая ping/pong-сцена сохранена как scenes/Main.tscn.
v0.0.10 17 мая 2026

Все 5 программ упакованы в работающие .exe, ярлыки для одно-клик-запуска

  • Починен MapEditor — раньше падал моментально при старте с NullReferenceException. Причина: ручной InitializeComponent() перекрывал тот, что Avalonia 12 source-generator делает из .axaml, поэтому все x:Name-поля оставались null. Удалил ручную версию — теперь окно открывается, карта рисуется, кисти работают.
  • Godot-клиент собирается одной командойclient\build-client.bat. Добавлены client/UniverseSurvival.Client.sln (без него Godot отказывался паковать C#-код в .exe) и client/export_presets.cfg (Windows Desktop preset). На выходе ~185 МБ: build\UniverseSurvival.Client.exe + рантайм .NET в подпапке.
  • Скрипт tools/create-shortcuts.ps1 создаёт 5 ярлыков в go/: Server · Admin · Map Editor · Test Client · Godot Client. У каждого правильный WorkingDirectory — Server сохраняет world.usmap рядом с собой, Map Editor его автоматически находит.
  • Подключён AI-генератор спрайтов через MCP.mcp.json регистрирует MCP-сервер openrouter-image с моделью google/gemini-2.5-flash-image. Сгенерирован первый стиль-эталон client/assets/style-references/grass-water-reference.png, который теперь служит reference_images для всех последующих генераций — чтобы держать единый визуальный стиль проекта.
  • Версия сервера обновлена с 0.0.9 до 0.0.10.
v0.0.9 16 мая 2026

Генерация карты, автотайлинг и отдельный редактор карт

  • Сервер сам генерирует мир при первом старте — Perlin-шум с настраиваемыми параметрами (waterLevel, частота, октавы) сохраняется в бинарный world.usmap. Воспроизводимо по seed, масштабируется от 128×128 до тысяч тайлов. По умолчанию сейчас 128×128 для отладки.
  • Два материала: трава и вода, с правильным автотайлингом по схеме blob47 — 47 канонических вариантов углов и переходов на каждый материал. Стыки всегда совпадают независимо от формы материка.
  • Отдельная программа — редактор карт editor/UniverseSurvival.MapEditor/ на Avalonia 12. Открывает/правит/сохраняет .usmap, рисует кистями (ЛКМ — рисовать, ПКМ — стереть), 5 уровней масштаба. Может сгенерировать мир из server-config.json без запуска сервера. Не путать с Admin, который правит настройки.
  • ТЗ для спрайтовdocs/sprite-style.md. Зафиксировано: 2D top-down, пиксель-арт, тайлы 32×32, персонажи 32×48, базовая палитра, освещение сверху-слева, чек-лист для каждого нового спрайта.
  • Генератор плейсхолдер-спрайтов tools/UniverseSurvival.SpriteGen/ — рисует кодом 47 тайлов на каждый материал через SkiaSharp. Не красиво, но технически правильно — пока ждём художника или AI-генератор.
  • Расширена секция world в конфиге — подсекция generation с параметрами шума и путём сохранения карты. Полный справочник полей — в docs/server-config.md и docs/world-generation.md.
  • Полный вайп сервера теперь реально удаляет world.usmap — мир пересоздаётся при следующем старте.
v0.0.8 15 мая 2026

GUI-редактор настроек сервера (Avalonia)

  • Создан отдельный десктоп-проект admin/UniverseSurvival.Admin/ на Avalonia 12 (.NET 10, кроссплатформенно — Windows/Linux/macOS).
  • Окно с тёмной темой, сайдбар по всем разделам: Сеть, Мир, Предметы, NPC, LLM, Геймплей, Админы, Модерация, Обслуживание, Логирование.
  • Под каждым полем — пояснение что оно делает. Полностью редактируемые секции: Сеть, Мир, LLM, Геймплей, Логирование, Обслуживание.
  • Кнопка полного вайпа сервера в разделе «Обслуживание»: модальное окно с ручным вводом слова WIPE — иначе кнопка «Подтвердить» неактивна. Точечно правит JSON через JsonNode, не теряя комментариев.
  • Кнопки «Открыть...», «Сохранить», «Перезагрузить». Индикатор несохранённых изменений. При старте автоматически ищет server-config.json.
  • Общие классы конфига вынесены в библиотеку shared/UniverseSurvival.Shared/ — сервер и редактор используют один тип ServerConfig.
  • Сервер от редактора не зависит — редактор просто читает и пишет JSON.
v0.0.7 15 мая 2026

Единый файл настроек сервера + кнопка полного вайпа

  • Введён server/server-config.json — один файл, через который меняется всё.
  • Разделы: сеть (порт, лимит), мир (размер, seed, биомы, спавн), предметы (характеристики, стак, вес), NPC (HP, урон, лут, LLM-персона), LLM (выбор провайдера и модели — Claude / OpenAI / Ollama), геймплей, админы и роли, баны/мьюты, обслуживание, логи.
  • Полный вайп сервера — кнопка wipeOnNextStart с защитой от случайного нажатия: нужно ещё вписать wipeConfirmText: "WIPE", иначе сервер откажется стартовать. После выполнения флаг сам сбросится и в файл запишется lastWipeAt.
  • В коде — типизированные классы ServerConfig и др. в server/Config/, загрузка через System.Text.Json. Запись маленьких правок (флаги вайпа) — через JsonNode, чтобы не потерять комментарии в файле.
  • На старте сервер печатает сводку: размер мира, число биомов/предметов/NPC, статус LLM, число админов и банов.
  • Описаны планируемые админ-команды для управления онлайном: kick, ban, mute, give, tp, spawn, shutdown, wipe-server и др. — с правами по ролям.
  • API-ключи LLM в файле не хранятся — только имя переменной окружения.
  • Полный справочник полей — docs/server-config.md.
v0.0.6 15 мая 2026

Запущен сайт universesurvival.ru

  • Куплен/привязан домен universesurvival.ru (A-запись на временный сторонний хостинг под статику).
  • Развёрнута статическая страница с историей версий (тёмная «космическая» тема).
  • nginx vhost + Let's Encrypt SSL (apex и www), HTTP→HTTPS редирект.
  • Не задеты соседние сайты на сервере.
v0.0.5 14 мая 2026

Godot-клиент подключён к серверу

  • Полная сетевая связка работает: открывается окно с фоном и статусом, подключается, обменивается ping/pong.
  • Скачана .NET-версия Godot 4.6.2.
  • Создан проект Godot в client/: project.godot, главная сцена Main.tscn, скрипт NetworkClient.cs.
  • LiteNetLib подключён через NuGet.
  • Сборка C#-проекта проходит без ошибок и предупреждений.
v0.0.4 14 мая 2026

Сеть заработала

  • Сервер и клиент успешно обмениваются сообщениями.
  • В сервер добавлена библиотека LiteNetLib (UDP).
  • Сервер слушает порт 9050, принимает до 1000 подключений.
  • Создан временный консольный клиент для проверки.
  • Подключение → ping → pong работает за миллисекунды.
v0.0.3 14 мая 2026

Скелет проекта

  • Созданы папки server/, client/, docs/.
  • Сервер: стартовый .NET 10 проект, запускается командой dotnet run.
  • Выбрана сеть: LiteNetLib (UDP) — рассчитан на 1000+ игроков онлайн.
  • Выбрана БД: PostgreSQL через Entity Framework Core.
  • LLM: будет абстракция ILlmProvider — можно подключить любого оператора.
  • Жанр уточнён: 2D-выживалка с симуляцией мира, развитием и реальной экономикой.
v0.0.2 14 мая 2026

Выбран язык программирования: C#

  • Сервер — на .NET (работает и на Windows, и на Linux).
  • Клиент — на движке Godot 4 (тоже на C#).
  • Преимущество: один язык на всё → общий код между сервером и клиентом.
v0.0.1 14 мая 2026

Запуск проекта

  • Создан репозиторий на GitHub.
  • Подключён Claude Code для разработки.
  • Создан главный документ PROJECT.md и журнал версий CHANGELOG.md.
  • Идёт обсуждение: на каком языке писать сервер и клиент.