Четвёртая / Пятая партии / Команды CM сервера — отчёт об исследовании сложности портирования
Объект исследования: исходный код citymania-org/cmclient (ветка vanilla 15.3) Дата исследования: 2026-08-14 Вывод: три блока работ сильно различаются по сложности, приведены оценки и рекомендуемый порядок.
1. Четвёртая партия: зонирование городов (включая growth_tiles сохранение)
Состав функций
| Компонент | Файл | Размер | Описание |
|---|---|---|---|
| Перечисление режимов зонирования | cm_zoning.hpp | 37 строк | 12 режимов оценки (CHECKOPINION/CHECKBUILD/CHECKSTACATCH/CHECKACTIVESTATIONS/CHECKBULUNSER/CHECKINDUNSER/CHECKTOWNZONES/CHECKCBACCEPTANCE/CHECKCBTOWNLIMIT/CHECKTOWNADZONES/CHECKTOWNGROWTHTILES) |
| Логика оценки | cm_zoning_cmd.cpp | 413 строк | По одной функции запроса на режим (возвращает SpriteID палитры), включает алгоритм радиуса TownZone/Tz, проверку покрытия StationFinder, проверку необслуживаемых зданий/промышленности |
| GUI панели зонирования | cm_zoning_gui.cpp | 204 строки | Окно выбора режима для внутреннего/внешнего слоя (глобальное состояние _zoning.inner/outer) |
| Сохранение growth_tiles | cm_saveload.cpp/.hpp | 90 строк | Расширенные поля Town growth_tiles + growth_tiles_last_month (std::map<TileIndex, uint8_t>), сохранение через vanilla SaveLoad handler |
| Расширение данных | extensions/cmext_town.hpp | ~80 строк | ext::Town: growth_tiles ×2 + много полей для серверного геймплея CM (статистика грузов CBTownInfo, отслеживание рекламы/финансов, счётчики роста городов hs/cs/hr) |
| Ежемесячная ротация + триггеры | cm_game.cpp / town_cmd.cpp | — | NewMonth ротация growth_tiles; запись состояния при строительстве/сносе/перестройке домов |
Оценка сложности портирования по пунктам
| Сложность | Серьёзность | Описание |
|---|---|---|
| Переработка системы сохранения | 🟠 Средняя | growth_tiles использует vanilla DefaultSaveLoadHandler + SlSetStructListLength/SlObject, в jrpm новая система sl/ (SlTableHeader/SlObjectSaveFiltered). Требуется переписать через NSL/SLE + XSLF-расширение (новый XSLFI_TOWN_GROWTH_TILES, версия 1), старые сохранения не затрагиваются |
| Расширение структуры Town | 🟢 Низкая | Берутся только две карты growth_tiles (пропускаются CBTownInfo/реклама/финансы — они специфичны для CityBuilder сервера CM); jrpm Town добавляет поля + таблица сохранения в town_sl.cpp |
| Логика оценки | 🟢 Низкая–средняя | В основном чистые запросы (GetTileType/StationFinder/кэш Town/проверка зданий), API jrpm существует; алгоритм радиуса TownZone (squared_town_zone_radius) требует сверки имён полей jrpm |
| Конвейер отрисовки | 🟠 Средняя–высокая | DrawTileZoning нужно встроить в конвейер отрисовки viewport — общий механизм рендеринга подсветки с третьей партией. Без конвейера подсветки придётся строить отдельно (рекомендуется сначала сделать подсветку, затем зонирование) |
| Ресурсы спрайтов | 🟠 Средняя | 12 палитр используют пользовательские спрайты CM_SPR_PALETTE_ZONING_*, в jrpm их нет → требуется замена существующими спрайтами палитр или добавление новых ресурсов |
| Хуки событий | 🟠 Средняя | Запись growth_tiles зависит от шины событий cmclient (event::HouseBuilt/HouseCleared/..., Emit в cm_main.cpp). В jrpm нет такого механизма → добавление хуков непосредственно в town_cmd.cpp в местах строительства/сноса + ежемесячная ротация (IntervalTimer) |
Вывод по сложности: 🟠 Средне-высокая (примерно 1.5–2 специализированных раунда, по 4–6 часов каждый)
- Предварительное условие: настоятельно рекомендуется выполнить после завершения системы подсветки третьей партии (общий конвейер отрисовки)
- Если делать только «раскраску 12 зон, без сохранения growth_tiles»: сложность снижается до 🟡 Средней (без расширения сохранения, примерно 1 раунд)
2. Пятая партия: воспроизведение команд + экспорт/запись
2.1 Воспроизведение команд (cm_command_log + cm_commands + generated)
| Компонент | Размер | Описание |
|---|---|---|
| Слой объектов команд | cm_command_type.hpp + generated/cm_gen_commands (2251+1418 строк) | Каждая vanilla команда обёрнута в программируемый объект (as_company/with_callback/set_auto), включает сериализацию команды в битовый поток |
| Загрузка журнала команд | cm_command_log.cpp (203 строки) | Распаковка lzma + разбор битового потока BitOStream → очередь _fake_commands (счётчик тиков/ожидаемый результат/случайное зерно/CommandPacket) |
| Выполнение команд | ExecuteFakeCommands | Выполнение по порядку счётчика тиков: ExecuteCommand(&cp) (внутренний API vanilla) + проверка случайного зерна/результата (античит), в многопользовательской игре пересылается всем клиентам |
Ключевые различия (определяющие сложность):
| cmclient | jrpm | Влияние |
|---|---|---|
ExecuteCommand(CommandPacket*) | Нет такой функции, ядро команд — DoCommandPInternal(Commands, TileIndex, CommandPayloadBase&, ...) | Требуется написать слой преобразования CommandPacket → DoCommandPInternal 🟠 |
| Поля CommandPacket (vanilla) | CommandPacket существует, но структура другая (GeneralCommandPacket<DynBaseCommandContainer>) | Требуется адаптация отображения полей 🟠 |
GetCommandName | ✅ Существует (command_func.h:166) | 🟢 |
outgoing_queue (пересылка в сети) | Сетевой слой jrpm отличается (OutgoingCommandPacket/ServerNetworkGameSocketHandler::SendCommand) | Требуется переписать логику пересылки для многопользовательского воспроизведения 🟠 |
| Запись (как генерируются .cmd файлы) | Хук записи cmclient зависит от перехвата post() слоя объектов команд | Команды jrpm — это шаблонизированный Post → требуется добавить хук записи в точке диспетчеризации команд 🔴 Основная работа |
| Формат файла | Частный (vanilla ID команды + битовый поток) | ID команд jrpm полностью отличаются от vanilla → файлы воспроизведения несовместимы, формат нужно перепроектировать 🟠 |
2.2 Экспорт / Запись (cm_export.cpp, 536 строк)
| Функция | Описание | Сложность |
|---|---|---|
| ExportOpenttdData | JSON экспорт спецификаций домов/грузов/палитр/информации о двигателях (JsonWriter) | 🟢 Низкая–средняя (независимо, читает структуры Spec и пишет JSON) |
| ViewportExport / ExportFrameSprites | По кадровый экспорт спрайтов области просмотра (зависит от внутренних векторов отрисовки viewport TileSpriteToDrawVector/ParentSpriteToSortVector) | 🟠 Средняя–высокая (конвейер viewport jgrpp отличается, требуется согласование векторных интерфейсов) |
Вывод по сложности: 🟠 Средне-высокая (примерно 2 специализированных раунда)
- Воспроизведение команд — самая большая часть: основная работа = ① добавление хука записи в точке диспетчеризации команд (адаптация шаблонизированной системы команд jrpm) ② слой выполнения CommandPacket → DoCommandPInternal ③ дизайн нового формата файла. Рекомендуется сначала портировать слой объектов команд (основа для чертежей третьей партии, также основа для воспроизведения)
- Экспорт: JSON экспорт данных можно делать независимо заранее (низкая–средняя сложность); запись кадров зависит от согласования viewport (в одной партии с подсветкой)
3. Команды CM сервера (cm_console_cmds.cpp, 289 строк)
Список команд и оценка сложности по каждой
| Команда | Функция | Сложность для jrpm | Примечания |
|---|---|---|---|
cmgamespeed [n] | Изменить скорость игры | 🟢 Очень низкая (~20 строк) | В jrpm уже есть глобальная _game_speed (gfx.cpp:52), не хватает только обёртки команды |
cmstep [n] | Шаг на n тиков | ⏭️ В jrpm уже есть | Команда step (ConStepGame) имеет ту же функцию, пропускается |
cmexport | Экспорт openttd.json | 🟢 Низкая–средняя | Зависит от ExportOpenttdData (см. пятую партию) |
cmtreemap <file> | Посадка деревьев по карте высот | 🟡 Средняя | Чтение карты высот + команда посадки, независимо |
cmreset_town_growth | Очистить записи роста городов | 🟢 Низкая | Зависит от полей зонирования городов |
cmload_commands | Загрузить воспроизведение команд | 🟠 Средне-высокая | Зависит от инфраструктуры воспроизведения команд |
cmstart_record / cmstop_record | Запись кадров | 🟠 Средне-высокая | Зависит от инфраструктуры записи |
cmgamestats | Статистика игровой сессии | 🟢 Низкая | Независимо |
cmgfxdebug | Отладка графики | 🟢 Низкая | Независимо |
Вывод по сложности: 🟢 В целом низкая (0.5–2 часа на команду)
Рекомендуемый порядок: cmgamespeed (10 минут) → cmgamestats/cmgfxdebug (по ~1ч) → cmexport (вместе с экспортом пятой партии) → cmtreemap (~2ч) → cmreset_town_growth (вместе с четвёртой партией) → cmload_commands/cmstart_record (вместе с воспроизведением/записью пятой партии).
4. Общий рекомендуемый маршрут
① Система подсветки (ядро третьей партии, ~2-3 раунда) ← основа отрисовки для чертежей и зонирования
② Слой объектов команд (основа для чертежей третьей партии + воспроизведения пятой, ~1-2 раунда)
③ Чертежи (~1 раунд)
④ Зонирование городов (~1.5 раунда, зависит от ① конвейера отрисовки; growth_tiles через XSLF)
⑤ Воспроизведение команд + экспорт (~2 раунда, зависит от ②; JSON экспорт можно делать заранее независимо)
⑥ Команды CM сервера (вставляются между партиями, gamespeed/step можно сделать сразу)5. Пункты, готовые к немедленной реализации (не требуют крупных проектов)
- Команда
cmgamespeed(~20 строк, изменение_game_speed) stepуже есть → не требуетсяcmgamestats/cmgfxdebug(по ~1ч, независимо)- JSON экспорт
cmexport(~2ч, независимо) cmtreemap(~2ч, независимо)
Эти 5 пунктов составляют примерно полдня работы и могут быть реализованы как «нулевая партия» параллельно с третьей/четвёртой/пятой партиями.