Что наша жизнь? Игра

Что делает в отпуске директор группы программерских компаний? То, что не может делать на работе. Программирует. Всласть. 🙂

Мне много лет, я выполз из тьмы и в Transport Tycoon Deluxe я играл ещё в то время, когда эта игра только появилась (это 94-й год).

Оригинальный Transport Tycoon
Оригинальный Transport Tycoon

Я считаю эту игру одним из лучших транспортно-экономических симуляторов всех времён и народов. И не я один. Когда игра устарела нашёлся человек, который её дизассемблировал (!) и переписал на Си. И получил игру с открытым кодом практически идентичную оригиналу. То есть — она могла поднимать game save и использовать спрайты оригинальной игры. Появилась OpenTTD.

OpenTTD
OpenTTD

OPenTTD развивается до сих пор и, в целом, это совершенно фантастический успех опенсорс сообщества. С ней всё хорошо. Кроме одного. Разработчики OpenTTD до сих пор удерживают внутри игры совместимость со старой игрой из 90-х. И это превращает код игры в изрядный треш. Ну то есть, например, состояние клетки игрового поля до сих пор хранится в виде группы бит определённого байта (с именем от m1 до m7, чтобы не было скучно). Не struct { ... int tileType : 4 ... } и type = tile.tileType, нет! — GB(_m[tile].m1, 4, 4), и только так. То есть, по сути, игра написана на ассемблере, обёрнутом в Си.

В общем, у меня уже несколько лет чесались руки переписать это всё на яву и привести в порядок. Заодно и забыть как страшный сон проблемы с переносимостью и адаптеры кода для привязки к разным оконным системам.

Ну и вот. Я решился взять этот вес.

Способ конвертирования кода был избран наиболее прямолинейный. Берём исходник на Си, кидаем его в файл ТоЖеИмя.java и открываем в эклипсе. Сначала купируем истерику компилятора снося все взятия адресов объектов, обращения через * и доступ к полям через ->. Дальше код превращается из полностью красного в пятнистый. Идём по пятнам красного и исправляем.

Чтобы понимать масштабы бедствия — версия, от которой я стартовал — порядка 4 мб исходных текстов, порядка 150 тыс строк. Я сразу отрезал часть кода, которой решил точно не заниматься — музыка, звуки. Save/load (логика реализации этой части невыносима из си) и ещё несколько некритичных и сделанных слишком по-сишному частей.

Читатель догадывается, что эксперимент был скорее успешен, чем нет. Иначе бы этой статьи не было. Но где-то на пятый день непрерывной войны я стал задумываться, реально ли вообще победить. Впрочем, малодушие было пресечено и безрассудство победило.

Несколько слов о том, каковы основные сложности конверсии. Я сознательно взял именно версию кода на Си (проект уже переписан на ++), полагая, что так будет проще. Думаю, что не прогадал.

Булевы и целые

Си не видит разницы между boolean и int. Java строга в этом плане, и пришлось пополнить код сотнями if( -> if( 0 != (... — впрочем. Кое где это потом помогло выловить неприятности.

Беззнаковые целые разных размеров

В Яве нет unsigned. В основном это не проблема, потому что код тянулся ещё с 16-битных времён и оперировал больше байтами и 16-битными словами. Для него обычный int вполне беззнаков в рамках его потребностей. Но были неприятные места. Отмечу, что там где в оригинальном коде применялся uint8, применять явский байт нельзя — он знаковый. Ну или можно, но 0xFF & обязателен (при этом происходит преобразование в int и обрезание верха, так что результат верный). Я не осознал этой проблемы на старте и уже позже. Во время отладки пришлось почти везде по коду искать и убивать переменные и поля типа byte и преобразования к нему. Впрочем, нашлись и обратные примеры — когда требовался именно знаковый байт. Например, смещение спрайта от точки отрисовки хранится в байте со знаком. Хранить можно и в int, но при чтении байта из оригинальной таблицы знак надо сохранить.

Использование оператора запятая

В Яве его тоже упразднили, и никто особо не заметил бы, но в коде OpenTTD пасся какой-то яростный фанат этого оператора. Применял он его не без изящества, оцените:

( bonus += 10, age > 10 ) || ( bonus += 20, age > 5 ) || ( bonus += 40, age > 2 ) || ( bonus += 100, true )

Красиво? Заменяется на switch с fall through. Но есть и идиотские примеры. Типа такого:

if (!tile.IsTileType(TileTypes.MP_RAILWAY) || ((dir = 0, tile.getMap().m5 != 1) && (dir = 1, _tile.getMap().m5 != 2)))

return Cmd.return_cmd_error(Str.STR_1005_NO_SUITABLE_RAILROAD_TRACK);

Их расшивать бывает трудно.

Пойнтеры в параметрах функций для возврата значений

Внезапно, лечатся очень легко.

modify( int[] x ) { x[0]++; }

Аллокация локальных объектов

Поскольку взятия адресов я сношу ещё перед началом работы с кодом, неясно, надо ли аллоцировать локальный объект. Пример:

NPFFindStationOrTileData fstd; ... NPFFillWithOrderData(&fstd, v);

Должно быть

NPFFindStationOrTileData fstd = new NPFFindStationOrTileData();
...
NPFFillWithOrderData(fstd, v);

На практике проблемы вообще нет, компилятор сообщает о том, что переменная не инициализирована.

Перечислимые типы

Тут у Явы всё отлично, но это отлично неприменимо по-сишному. В Си константы из enum — это целые. И можно flags & FLG_USED и будет всё как надо. В итоге почти все enum были заменены на просто целые константы.

Нет макросов

А в Си их применяют часто. Где-то (в инициализации массивов, например) за макросы сошли статические методы — если метод можно посчитать при компиляции. Ява принимает его при инициализации массивов. Но, к примеру, конструкцию BitOps.SB(tile.getMap().m2, 0, 4, new_ground); , которая позволяет модифицировать биты у произвольного поля, пришлось переделывать повсеместно.

Работа с памятью

Янус Полуэктович прошел к себе в кабинет, на ходу небрежно. Одним универсальным движением брови ликвидировав всю сотворенную мною кунсткамеру.

Ну, понятно, что вызов free() мы просто удаляем. Но из-за отсутствия сборки мусора программы на Си местами вынуждены жутко извращаться в плане управления аллокацией и это местами заставляет программиста извращаться ещё жутче в коде. Чтобы построить такие структуры. Которые можно собрать при ручном управлении памятью. Любимый хит — указатель something **next, который смотрел на поле next последней собранной структуры. Чтобы потом из более позднего вызова туда в прошлое впилить указатель на свежесозданный объект.

Все такие вещи я детально анализировал, сносил под корень и заменял на нормальные списки. Хеш-таблицы и прочие человеческие инструменты работы со структурами данных. Иногда это упрощало код кардинально. Раз в 10.

В целом сам процесс доведения кода для безошибочного с точки зрения компилятора Ява был муторным, но на 90% — элементарным. Трудно было с goto. Применялись они редко, но иезуитски — код на 10 страниц с пятью вложенными циклами и switch/if, и три перехода. Куда-нибудь на вторую страницу внутри цикла. Местами это решалось выносом части кода в функции (там, где переход был применён для повторного использования кода). А местами требовалась очень серьёзная перереботка. Иногда приходилось просто добавлять булевы переменные для виртуального обхода части кода. Увы, в правильности всех подобных конверсий я пока не уверен.

Всё, устал писать. Продолжу позже. Репозиторий — https://github.com/dzavalishin/jdrive, если интересно — присоединяйтесь. Если вызовет интерес — напишу ещё. На сегодня код собирается, многое работает, но играть пока нельзя. Не только из-за отсутствия save/load. Из всех видов транспорта, пожалуй, ближе всех к завершению — корабли. Но об этом — в следующей статье.

После выпуска iOS и iPadOS 14.6 некоторые пользователи начали сообщать о том. Что их устройства очень быстро разряжаются. Один из пользователей пишет о быстром разряде iPhone 11 Pro и Apple Smart Cover.

Сразу после обновления заряд iPhone 11 Pro упал до 2%. А заряд Apple Smart Cover снизился до 15%. Обычно заряд смартфона остаётся полным после обновления. А Apple Smart Cover разряжается до 20% после 15-часового рабочего дня без подключения к сети. Состояние аккумулятора сохраняется на уровне 100% в течение более года. Значок батареи Smart Cover больше не отображается на главном экране при подключении или отключении.

iPhone 11 и iPhone 12 некоторых пользователей начали быстро разряжаться и перегреваться

Согласно сообщениям на форумах. Проблема затрагивает все устройства. Независимо от состояния конкретной батареи. Ещё один пользователь заявил. Что iPhone 12 Pro не только стал разряжаться быстрее. Чем обычно. Но и начал перегреваться при запущенном Safari. Аналогичные сообщения поступили от владельцев iPhone 12 mini.

Да, я обновил свой iPhone 12 mini с 14.2 до 14.6 пару дней назад. Утром я пользовался смартфоном в течение нескольких минут. Как вдруг заряд упал почти до нуля. А сам смартфон сильно нагрелся.

Если проблема действительно связана с программным обеспечением, Apple может выпустить патч для iOS 14.6 или же отложить исправление для iOS 14.7, которая в настоящее время находится в стадии бета-тестирования.

Читайте так же:

  • LG продаёт невыпущенные смартфоны Velvet 2 Pro и LG Rollable по смешным ценам своим сотрудникамLG продаёт невыпущенные смартфоны Velvet 2 Pro и LG Rollable по смешным ценам своим сотрудникам Компания LG недавно объявила о закрытии подразделения смартфонов, поэтому пользователи так и не получили модели Velvet 2 Pro и LG Rollable. Однако производитель успел выпустить ограниченное количество телефонов. Как сообщают осведомлённые источники. Сотрудники LG в Южной Корее имеют […]
  • Новости интернет-рекламы за апрель 2021 годаНовости интернет-рекламы за апрель 2021 года Новый тип цели в Яндекс.Метрике, автоматическое применение рекомендаций в Google Ads. Появление пикселей и видео в конструкторе сайтов ВКонтакте — какие еще изменения произошли в сфере digital за последний месяц? Главные новости интернет-маркетинга за март и апрель эксперты компании […]
  • Google Ads приглашает принять участие в опросе на тему UXGoogle Ads приглашает принять участие в опросе на тему UX Исследователи пользовательского опыта Google Ads ищут рекламодателей для участия в опросе. Направленном на улучшение UX. Our user experience (UX) researchers are looking for participants of all levels to share feedback on Google Ads. If you’re interested. Turn on Market Research email […]
  • Поиск контентаПоиск контента Вы когда-нибудь беспокоились о дублировании контента? Это может быть что угодно: какой-нибудь шаблонный текст на вашем веб-сайте. Или описание товара на Вашей странице электронной коммерции, которое вы позаимствовали у первоначального продавца. Или, может быть, цитата. Которую вы […]