# Бекпорт rental-предметов — изменения для тестировщиков Воскрешение аренды (rental) из pre-RE eAthena: предмет с абсолютным временем истечения (`expire_time`), по истечении удаляется; привязан к персонажу. Возвращает к жизни 98 box-предметов, которые звали `rentitem` и были нейтрализованы в `{}` (коммит b0d885b). План: `doc/rental_backport_plan.md`. ## ⚠️ Деплой (ОБЯЗАТЕЛЬНО) 1. **Применить миграцию БД** `dumps/migrations/10-add-item-expire-time.sql` — добавляет колонку `expire_time` в `inventory`, `cart_inventory`, `storage`, `guild_storage`. `IF NOT EXISTS` → безопасно при повторном `dumps.sh update`. На существующих сейвах default `0` = НЕ аренда (никаких изменений у игроков). 2. **Пересобрать ОБА сервера вместе** (`make sql`): в `struct item` добавлено поле `expire_time` — это кросс-серверная структура (char↔map). Старый map против нового char (или наоборот) = рассинхрон протокола. Всегда деплоить map+char одной сборкой. 3. Легаси `sql-files/main.sql` НЕ обновлялся (он мёртв — `TYPE=MyISAM` не работает в MariaDB 11.8). Активна схема `dumps/`. --- ## Фаза 1 — Фундамент: поле `expire_time` + персистентность + схема (СДЕЛАНО) **Что изменилось (без изменения геймплея — поле всегда 0, пока аренда не выдана):** - `src/common/mmo.h` — `struct item` += `unsigned int expire_time` (0 = не аренда). - `src/char_sql/char.h` — `struct itemtmp` (staging для всех save-путей) += `expire_time`. - `src/char_sql/char.c` — `compare_item` сравнивает `expire_time`; inventory/cart copy-loop'ы и load'ы + `memitemdata_to_sql` (SELECT/matcher/UPDATE/INSERT) переносят `expire_time`. **Важно:** matcher теперь сравнивает `expire_time`, иначе две разные аренды одного nameid могли перепутаться при сохранении. - `src/char_sql/int_storage.c` — storage и guild-storage copy-loop'ы + load'ы += `expire_time`. - `dumps/tables/{inventory,cart_inventory,storage,guild_storage}.sql` — колонка в CREATE (fresh install). - `dumps/migrations/10-add-item-expire-time.sql` — ALTER для существующих БД. **Верификация (здесь):** `make sql` — 0 новых ворнингов, все 3 бинарника собраны/линкуются. Runtime-логин-тест невозможен без клиента → **на тестировщиках**: **Как протестировать Фазу 1 (тестировщики):** 1. Применить миграцию, пересобрать оба сервера, запустить кластер. 2. Залогиниться персонажем со старыми сейвами → инвентарь/карт/склад грузятся как раньше (ничего не сломалось, `expire_time`=0 у всех существующих предметов). 3. (Полный тест аренды — после Фазы 2, когда появится buildin `rentitem`.) **Откат:** реверт коммита фазы; колонка `expire_time` может остаться (default 0, безвредна). ## Фаза 2 — Движок аренды + buildin `rentitem` + пакеты (СДЕЛАНО) **Что изменилось:** - `src/map/map.h` — `map_session_data` += `int rental_timer`. - `src/map/pc.c` + `pc.h` — движок аренды: `pc_inventory_rentals` (скан инвентаря: истёкшие удаляет + шлёт 0x299, активным шлёт 0x298 и ставит таймер), `pc_inventory_rental_clear`, `pc_inventory_rental_add`, callback `pc_inventory_rental_end`. Хуки: init `rental_timer` и вызов `pc_inventory_rentals` в `pc_authok` (логин), регистрация callback в `do_init_pc`. Аренда **не стакается** (свой слот), rental-usable **не расходуется** при использовании (эффект применяется, предмет остаётся до истечения), аренду **нельзя дропнуть** (`pc_candrop`). - `src/map/clif.c` + `clif.h` — `clif_rental_time` (0x298: «предмет исчезнет через N сек») + `clif_rental_expired` (0x299: удаление из инвентаря). Без PACKETVER-гейта; длины уже в `packet_db.txt`. - `src/map/script.c` — buildin `rentitem ,` (`"vi"`): выдаёт предмет с `expire_time = time(NULL)+seconds`, шлёт 0x298, ставит таймер. **Верификация (здесь):** `make sql` EXIT=0, все бинарники линкуются; **0 ворнингов** на rental-строках (прочий warning-шум — пред-существующий x64 int↔pointer/strncpy). Runtime — на тестировщиках. **Как протестировать Фазу 2 (тестировщики, нужен клиент):** 1. NPC/`@`-скриптом или предметом вызвать `rentitem ,60` (1 минута) → предмет появляется, приходит сообщение об оставшемся времени. 2. Подождать минуту → предмет удаляется автоматически, приходит уведомление. 3. Релог во время аренды → таймер восстанавливается (благодаря Фазе 1), остаток времени корректен. 4. Rental-экип можно надеть; rental-usable при использовании даёт эффект, но не расходуется. **Откат:** реверт коммита фазы. ## Фаза 3 — Bind-семантика: аренда привязана к персонажу (СДЕЛАНО) Аренду (`expire_time != 0`) нельзя вывести с персонажа — иначе она «утекает» или замораживает истечение. **Что изменилось (везде добавлен `expire_time`-гвард):** - `src/map/trade.c` — `trade_tradeadditem`: нельзя положить аренду в окно обмена (msg 260). - `src/map/clif.c` — `clif_selllist`: аренда не показывается в списке продажи NPC. - `src/map/storage.c` — `storage_additem` (обычный склад) И `guild_storage_additem` (гилд-склад): аренду нельзя положить в склад (msg 264). **Запрет обычного склада добавлен сверх eAthena** — иначе «парковка» аренды в склад заморозила бы таймер (он сканит только инвентарь) = эксплойт. `compare_item` теперь учитывает `expire_time` (разные аренды не сливаются). - `src/map/vending.c` — `vending_openvending`: аренду из тележки нельзя выставить на вендинг. **НЕ гардим** (как eAthena): refine аренды (работает), картинку (rental usable из Фазы 2). **Верификация (здесь):** `make sql` EXIT=0, 0 ошибок/ворнингов на Фаза-3 строках. **Как протестировать Фазу 3 (тестировщики, нужен клиент):** 1. Получить аренду (`rentitem`), попытаться: обменять / продать NPC / выбросить / положить в склад (обычный и гилд) / выставить на вендинг → каждое **запрещено** (сообщение/отказ). 2. Две разные аренды одного предмета (разный остаток) не сливаются в один стак. **Откат:** реверт коммита фазы. ## Фаза 4 — Восстановление 98 box-предметов + регенерация item-SQL (СДЕЛАНО) **Что изменилось:** - `db/item_db.txt` — 98 box-предметам возвращён оригинальный скрипт `{ rentitem ,; }` (был `{}` после нейтрализации b0d885b). Аренда: 604800 сек = 7 дней, 2592000 = 30 дней. - `dumps/forge/restore-rental-scripts.py` — reproducible-ресторер (берёт оригиналы из 25f3be3, инжектит в Script-колонку; идемпотентен). 98 recovered / 98 restored. - `dumps/migrations/A-item_db.sql` — регенерирован (`dumps/forge/items_db-to-sql.sh`): 6215 строк, 98 rentitem-записей (для SQL-импорта; активный сервер — TXT). **Верификация (здесь):** restore 98/98; SQL-spot-check корректен. C не менялся (бинарники с Фазы 3). Boot-парс item_db (0 script error) — на тестировщиках (rentitem зарегистрирован в Фазе 2 → ошибок быть не должно; «было ~133, стало 0» — теперь 0 сохраняется И предметы рабочие). **Как протестировать Фазу 4 (тестировщики):** 1. Старт map-сервера → в логе **0 script error** (предметы 13915/13951/… парсятся). 2. Использовать box-предмет (напр. 13915 Love_Angel_Box) → выдаётся арендный предмет на 7 дней, приходит уведомление об остатке; через срок — исчезает. **Откат:** реверт коммита фазы (вернёт `{}`). --- ## ИТОГ: rental-бекпорт завершён (Фазы 1-4) Аренда работает end-to-end: поле `expire_time` (struct+DB+персистентность) → движок (таймер/истечение/ пакеты 0x298/0x299) + buildin `rentitem` → bind-семантика (привязка к персу) → 98 предметов рабочие. Под клиентом 2007 апгрейд НЕ требовался. **Сделано в концепции** (pre-12). **Не делалось** (вне концепции — нужен клиент 2008/2010+): `setfont`, `searchstore`, `buyingstore` — остаются `{}`. `rentitem2` не нужен (rAthena-only). `bonusautoscript` — 0 вызовов, отложено. **Деплой:** см. раздел «⚠️ Деплой» вверху (миграция №10 + пересборка обоих серверов).