Skip to content

Четвёртая / Пятая партии / Команды CM сервера — отчёт об исследовании сложности портирования ​

Объект исследования: исходный код citymania-org/cmclient (ветка vanilla 15.3) Дата исследования: 2026-08-14 Вывод: три блока работ сильно различаются по сложности, приведены оценки и рекомендуемый порядок.


1. Четвёртая партия: зонирование городов (включая growth_tiles сохранение) ​

Состав функций ​

КомпонентФайлРазмерОписание
Перечисление режимов зонированияcm_zoning.hpp37 строк12 режимов оценки (CHECKOPINION/CHECKBUILD/CHECKSTACATCH/CHECKACTIVESTATIONS/CHECKBULUNSER/CHECKINDUNSER/CHECKTOWNZONES/CHECKCBACCEPTANCE/CHECKCBTOWNLIMIT/CHECKTOWNADZONES/CHECKTOWNGROWTHTILES)
Логика оценкиcm_zoning_cmd.cpp413 строкПо одной функции запроса на режим (возвращает SpriteID палитры), включает алгоритм радиуса TownZone/Tz, проверку покрытия StationFinder, проверку необслуживаемых зданий/промышленности
GUI панели зонированияcm_zoning_gui.cpp204 строкиОкно выбора режима для внутреннего/внешнего слоя (глобальное состояние _zoning.inner/outer)
Сохранение growth_tilescm_saveload.cpp/.hpp90 строкРасширенные поля 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) + проверка случайного зерна/результата (античит), в многопользовательской игре пересылается всем клиентам

Ключевые различия (определяющие сложность):

cmclientjrpmВлияние
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 строк) ​

ФункцияОписаниеСложность
ExportOpenttdDataJSON экспорт спецификаций домов/грузов/палитр/информации о двигателях (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. Пункты, готовые к немедленной реализации (не требуют крупных проектов) ​

  1. Команда cmgamespeed (~20 строк, изменение _game_speed)
  2. step уже есть → не требуется
  3. cmgamestats / cmgfxdebug (по ~1ч, независимо)
  4. JSON экспорт cmexport (~2ч, независимо)
  5. cmtreemap (~2ч, независимо)

Эти 5 пунктов составляют примерно полдня работы и могут быть реализованы как «нулевая партия» параллельно с третьей/четвёртой/пятой партиями.