Log of Alprog
1.19K subscribers
98 photos
91 links
加入频道
Нетерпеливый подписчик возразит мне, что отличный вокабуляр это вовсе не проблема и я зря драматизирую; но язык определяет сознание, и в случае геймдева неизбежно приводит к натягиванию совы на известно что. Приведу пример поубедительнее. Если вы загуглите уровень зарплат в геймдеве, вы скорее всего найдёте опросы, где разработчики разбиты на Frontend, Backend и Fullstack. Привычное разделение в IT, но которое совершенно бессмысленно в геймдеве. Тут сразу несколько проблем.

Прежде всего, поставьте себя на место разработчика игрового движка синглплеерной игры. С одной стороны сервера нет, так что он работает, получается, над клиентом. Записываем во Frontend. С другой стороны, он не пишет саму игру, а для скриптеров его API выступает вполне себе Backend’ом. Наконец, можно заключить, что есть элементы того и другого, и выбрать вариант Fullstack. С равной вероятностью можно занести себя куда угодно. Подобные неоднозначности возникают и со многими другими геймдев-специализациями, если задуматься. В итоге такой опрос не показывает ничего, кроме погоды в унитазе составителя.

Но даже если представить, что мы договорились, кого в какую группу относить, это всё равно бессмысленные надкатегории, которые ничего не значат. В вэбе и мобильных приложениях Front и Back это естественным образом выделившиеся специализации, но в геймдеве такого разделения не произошло. Generalist-программисты у нас разделяются на геймплейщиков, графических, движкописателей/туловиков, иногда отдельно интерфейсщиков. Из геймплейщиков можно отдельно выделять скриптеров; из графических тех.артистов; движковых можно дальше дробить на физику, звук, АИ, сеть и т.п., но это не принципиально. Я не претендую на полноту классификации, но главное, что за категориями «графический программист» или «программист UI» стоят реальные вакансии и карьеры людей, которые двигались по этому пути, а Frontend-программист в геймдеве — это просто искусственная категория. И игровой код, и ИИ, и физика могут выполняться как на клиенте, так и на сервере, но различать их по этому признаку плохая идея.

Это ведь нетрудно задуматься, что некоторые привычные процессы или подходы просто неприменимы в геймдеве; что существует специфика работы. Но львиную долю набежавших энтепрайзников не посещает такая мысль даже на уровне терминологии. Теперь представьте, насколько часто в ваших внутренних процессах происходят спотыкания о всевозможные нюансы, если ваш HR тратит время разработчика на рассказы про вашу продуктовость; PM не видит перед собой геймплейного и графического программиста, а оперирует в голове выдуманными категориями; а программист интерфейсов требует ТЗ за два месяца и по шаблону мобильных приложений. И это только внешние очевидные проявления, а дьявол в мелочах и повседневном опыте.

Если слышите, что кто-то бездумно тащит в наше ремесло чужую терминологию, гоните его, насмехайтесь, зовите земляным червяком.
А давайте поболтаем?
#лайт
Знаете чего не хватает, когда все месяцами работают удалённо? Технической болтовни из-за мониторов. Нормальное гиковское чувство рассказать о том, какой крутой пейпер прочитал вчера ночью, и как здорово раст уделывает кресты на синтетических тестах. Не уныло бросить ссылку в чат, а голосом раскидать императивщику за тайп-дедукшен здорового человека.

Вы уже догадываетесь к чему я клоню? Если вы из тех, кого хайп вокруг трендов раздражает, как блики солнечного света на дисплее, вам впору зашторивать окна. Речь пойдёт о clubhouse.

Мне прислали инвайт пару дней назад и я немного помониторил, если там жизнь в плане игростроя. Оказалось, что есть! Днём, разумеется, никого нет, но по вечерам там появляется «геймдев курилка», в которой всегда на удивление много людей. Причём не левого народу: если вы в индустрии не первый год или тусили пару раз на конференциях, то без труда отыщите кучу знакомых лиц на аватарках. Но, к сожалению, тон разговоров в курилке, как это часто бывает, захватили основатели студий, инвесторы и менеджеры. Эдакий дискурс старого DTF (ещё до смены владельцев). Вклиниваться в эти высокие разговоры о free to play не очень интересно; да и это совсем не то, чего просит душа простого технаря. А запрос на свою комнату, мне кажется, есть.

Я предлагаю собраться втроём или вчетвером, каждому заранее прочесть/изучить по одной интересной статье или пейперу, и рассказать каким-нибудь воскресным вечером друг другу, почему этот подход классный (или отстой). Это может быть новый proposal в С++, какая-нибудь фича из kotlin, статья про новый метод рейтрейсинга или же и вовсе эзотерический язык программирования. Что угодно. Расширим кругозор друг друга, а может чего интересного узнаем от аудитории.

Чем это отличается от подкаста?
Я давно хотел попробовать провести что-то вроде подкаста, но не был уверен насчёт формата. В своё время, когда заводил блог, я выбрал телеграмм, потому что рассудил, что это наиболее вменяемая платформа для технических блогов в наше время. Никому мои посты не интересны настолько, чтобы заходить на какой-то мой личный сайт или мониторить на Medium. А когда это всё происходит внутри платформы Telegram, куда люди и так заходят время от времени, аудитория не пропадает, и мои посты кто-то видит и обсуждает, даже если я пишу раз в месяц. Так же и с clubhouse. Там уже есть какой-то движ и люди туда заходят каждый день, так что почему бы и не поболтать?

Кроме того, традиционный подкаст предполагает более строгое отношение к контенту, хотя бы потому, что сохраняется в вечность. Он нацелен на то, что его будут слушать и после. Здесь же после прямого эфира ничего не остаётся. Это менее обязывающая форма подкаста, зато более динамичная и с живым общением со случайными слушателями. А после обсуждения и пост на тему проще писать.

Чем тогда это отличается от созвона в дискорде?
Тем, что в дискорде не бывает случайной аудитории. Никто не узнает, что на каком-то никому неизвестном сервере собрались три калеки и что-то обсуждают. Никто не подключится, не задаст вопросов и не вклинится в разговор. Здесь же мы можем создать комнату в clubhouse заранее и расписание увидит много людей. Кто-то поставит в календарь, а кто-то подключится в прямом эфире и «поднимет руку», чтобы ему тоже дали слово. Это может быть гораздо более интересный движняк, чем разговор на пустом сервере.

Так и чё?
Один я ничего не сделаю. Нужно ещё хотя бы пара человек, кому интересно читать/изучать что-то новое для себя, и кому интересно это обсудить в таком формате. Пишите в комментариях, кому идея кажется хорошей. Ну и о том, как вас достали разговоры о clubhouse из всех утюгов, тоже туда пишите. Не держите в себе :)
Патетичная речь о том, что я покидаю Encased

Encased — это очень важный для меня проект. В конце 2017 года, во время моего мексиканского саббатикала после работы в одной инди-команде, я вписался в новый геймдев-стартап. Фактически, мои старые университетские друзья позвали меня писать «русский Fallout». Это выглядело именно так, как звучит. Даже хуже: это сегодня словосочетание «русский Fallout» уже немного реабилитировано, а на тот момент это было чистым ругательством. Словом, очередные «корованы».

Но меня тогда прельщала возможность работать удалённо (да-да, были времена, когда это воспринималось привилегией, а не наказанием), попробовать себя лидом большого хардкорного проекта, а также у меня ещё жутко свербел гештальт по написанию ролевой игры. Олдфаги форума gamedev.ru наверняка помнят мой эпичный тред «маленькое и скромное РПГ», которое я ещё со школьной скамьи два года пилил на Visual Basic. Так вот ещё с тех пор мне хотелось снова залететь в эту же историю, но уже профессионально.

Удивительное дело, но наша команда не развалилась через два месяца. Мы просто фигачили лучшее, что могли, и мало-помалу у меня сперва исчезал скептицизм по отношению к нашему детищу, а затем я и вовсе по-настоящему прикипел к нему душой. Я фигачил над Encased 3 года и 4 месяца от первой строчки кода, от архитектуры, собственной сериализации и компонентной системы; к кикстартеру, на котором у меня случился нервный срыв; через собеседования новых сотрудников, прожатие F5 на странице отзывов и через девгаммы к раннему доступу; с переносом сроков, сменой издателя, портированием и ещё со множеством эпичных историй, которые без пива не рассказывают; в ту точку, где мы находимся сейчас — в пред-релизный пятый патч.

Сейчас я уверен в проекте, как никогда. Мы действительно делали «‎игру-мечты». Я не просто закрывал таски, но оказал большое влияние на то, как в итоге реализованы многие механики, боёвка, интерфейс, да и в целом управление в игре. Потому что мне было не пофиг. И наверное сейчас я впервые верю, что у нас действительно получается игра, которая посоревнуется с лучшими мировыми.

И в этом месте я покидаю Encased.

С самого начала проекта я жил в двух реальностях. В одной реальности я делал самый офигенный и важный проект в своей карьере, а в другой — жил не совсем своей жизнью. Меня захантили на проект немного хитростью: при обсуждении стартапа в качестве возможного движка рассматривался Unreal, а когда я уже морально настроился работать, оказалось, что бизнес готов вести дела только на Unity. Пришлось переступить через себя и вернуться к нелюбимому Unity. Но вообще мой настоящий гендер, как многие знают, — это крестовик с движкописательным уклоном. Плюс быть лидом, конечно, круто; но когда нет более крутых коллег, начинаешь замыкаться в собственных соплях, что не очень круто. Мне пока ещё рано замыкаться — хочется покачаться ещё хотя бы пару лет. Не говоря уж о планах эмиграции.
Короче говоря, ещё в самом начале проекта я пообещал себе, что не буду просиживать штаны на поддержке и полировке, если полный релиз будет постоянно откладываться (о том, что с крупными долгостроями так бывает, я понимал изначально). Сделаю всё основное — и уйду. И вот я наконец-то сдержал обещание (хотя и так затянул сильно дольше, чем рассчитывал).

Я оставляю проект со спокойной душой, потому что он в хорошем состоянии. И не смотрите, что сейчас недавние отзывы на стиме красные. Это только последние штук 30 и только из-за нашего долгого молчания. Но вопреки крикам хейтеров, которые утверждают, что мы ничего не делали чуть ли не с прошлого августа; игра всё это время активно разрабатывалась. Игра действительно уже проходится от начала до конца и технически полностью готова. Я закончил архитектуру и все важнейшие подсистемы, а также «вырастил» нового лида, который шарит в моём коде уже чуть ли не как я сам, и который с лёгкостью дотащит проект. Собственно, уже даже в этом патче все самые сочные изменения были внесены под его руководством; а я последние два месяца лишь поглядывал за процессом из полу-саббатикала. Ну а теперь я полностью ушёл.

Посмотрите свежий дневник разработки в шапке поста, чтобы узнать в каком состоянии игра на самом деле. Поиграйте в патч 5, который выходит сегодня на Steam, чтобы убедиться, что это не монтаж. Но только сильно не увлекайтесь — полноценное прохождение стоит отложить до релиза. Это в самом деле хорошая игра получается.
Я вас всех немножко обманул

В прошлом сообщении я писал, что покидаю Encased, но это оказалась не совсем правда. Уходить-то я уходил, но ненадолго (всего на 2 месяца). Видимо, такие проекты просто так не отпускают. Планы, сроки и даты сложились таким образом, что у меня образовалось идеальное окно, чтобы вернуться и довести проект до релиза. В конце июня я вернулся и работал над Encased практически вплоть до настоящего момента.

Ужасно рад, что так сложились звёзды, что мне всё-таки удалось работать над проектом от первой строчки кода и до версии 1.0. Которую я сегодня и хочу вам представить от имени всей команды.

Пару часов назад Encased 1.0 вышел в релиз в Steam, Gog и Epic Games Store. Бегите скорее покупать, если ещё этого не сделали:
https://store.steampowered.com/app/921800/

Что же касается моих планов, сроков и дат, о которые я упоминал выше; то со вчерашнего дня я работаю в Paradox Development Studio. Чем я там занимаюсь, я, естественно, не расскажу. Но это ровно та компания, в которую я хотел устроиться. Может быть отдельным постом как-нибудь расскажу, почему я хотел именно в Швецию, и почему именно в Paradox. Но это будет в другой раз. А сегодня мы празднуем выход Encased! Ваше здоровье, господа! Skål!
Кажется я поехал кукухой

Одна из моих попутных целей недавнего переезда и перемен в жизни — это восстановить work-life баланс, чтобы иметь возможность заниматься своими проектами. Если не считать периодические командировки в Питер, то с декабря 2016 года я всё время работал на удалёнке. Мои личные пет-проекты заглохли ровно с того же момента. Прежде всего потому что я на работе занимался достаточно увлекательными вещами. Но вторая, и, пожалуй, главная причина — это наглухо поехавший график. Когда нет чёткого физического разделения между рабочим временем и личным — всё время рабочее. Конечно, это не означает, что ты всё время работаешь: есть время и на отдых и на свои дела. В будние дни ты отвлекаешься и прокрастинируешь гораздо больше, чем в офисе; но потом нагоняешь ночью и на выходных. И это проблема. У тебя нет твоих личных двух часов перед сном, когда можно что-то поделать для себя и не чувствовать при этом, что это в ущерб работе. Потому что всегда есть, что нагонять в рабочем проекте. Всегда есть пару часиков, которые хорошо бы добить в этом спринте.

Вот почему при выборе нового места мне крайне важно было выйти именно в офис. Благо, в Швеции ⅔ населения уже вакцинированы и народ постепенно возвращается в офисы, так что с этим проблемы нет. Я также прикупил основную мебель в квартиру и разгрёб первичную бюрократию, так что у меня и в самом деле появилось время на свои проекты.

Разумеется, первым делом я стряхнул пыль со своего движка Judy. Ну как движка. Там только редактор скриптов с плеером и дебагером, самопальная рефлексия для С++, да зачатки рендера. Но концепт эстетически приятный. Центральную роль в нём играет Lua, которая выступает одновременно и скриптовым языком, и форматом для конфигов, сцен, файлов сохранений и сетевых сообщений. Не самое быстрое решение, но оно быстрым быть и не собиралось: упор у меня был на удобные тулзы, а не скорость. Игры предполагалось писать всё равно относительно маленькие.

Вот только за последние 5 лет немножко изменились мои требования к движку мечты. Во-первых, IDE повсеместно теперь поддерживают Language Server Protocol, и писать скрипты без возможностей хотя бы EmmyLua нынче уже не комильфо, как говорится. Во-вторых, на старте Encased я сильно вложился в проектирование системы сериализации. Это многократно нам окупилось, а также по мере развития сильно повлияло на мои взгляды по теме. Теперь я хочу что-то подобное встроенное в скриптовый язык. Причём из коробки. Ясное дело, я не мог не пересмотреть немного концепцию Judy.

Меня всё ещё кидает из стороны в сторону, потому что я хочу слишком много всего и сразу, да и отказываться от Lua, которая уже подключена и работает, тоже жалко; но последние 2 дня я размышляю над своим языком. Пока только формулирую основные Design Goals, но одновременно прикидываю к носу, как конкретные фичи будут технически исполнены.

Писать свой скриптовый язык это особый уровень безумия. Даже по меркам движкописателей. Я прекрасно понимаю, что это адски объёмная задача и вряд ли вообще что-то путное получится, но похоже, что кукуха моя уже поехала и остановить будет сложно. Все нормальные названия для языка уже заняты, поэтому пусть пока будет рабочее — Ku-ku language. О том, чем я занимаюсь на работе всё равно больше рассказывать нельзя, так что ближайшее время блог будет в основном про Ku-ku.
Ku-ku design goals

Вот я и накатал Design Goals для моего скриптового языка мечты Ku-ku.

Писал изначально на английском. Думал сначала перевести на русский для этого канала, но быстро понял, что любой, кому будет хоть капельку это интересно, должен по идее без труда справиться с техническим английским текстом. Так что пусть будет пока так.

Так или иначе, одно лишь пространное описание того, чего же я хочу от языка, даже без конкретных деталей, получилось весьма внушительным. Как я собираюсь это всё разрабатывать по вечерам и не лопнуть — ума не приложу.

Но это будет чуть позже. А пока я очень жду ваших комментариев по тому, что есть. Заряжайте холивар по каждому пункту. Дайте мне жару, как следует!
Вы точно понимаете корутины?
#код
Много-много лет назад, когда я первый раз пришёл на настоящую работу программировать за настоящие деньги, меня научили корутинам. Ну, не то, чтобы прям научили. Просто там был проект с корутинами, и мне волей-неволей пришлось вникнуть, что это за yield такой непонятный. Новый мир, зазиявший передо мной, в корне перевернул моё тогдашнее представление о том, как можно писать геймплейный код. Я и до сих пор это воспринимаю, как одну из важнейших ментальных ступенек для программиста.

Речь сейчас идёт о тех диких временах, когда воевали с луками и стрелами против боевых машин самописными движками на плюсах. Я тогда успел немного походить из конторы в контору, но везде обнаруживал несчастных грустных людей, не вкусивших плодов корутин. Многие и слов таких не знали. И я нёс знания в простой народ. И жизнь людская преображалась на этих проектах.

Но эти времена давно прошли. Теперь каждый знает, что такое StartCoroutine() в Unity. Все к ним привыкли, и никого ими не удивишь; но я заметил, что самое крутое назначение корутин все ещё ускользает от многих в наше время.

Новички считают, что это просто удобный способ делать задержки. Каждый второй, делающий тестовое про самолётик в DarkCrystalGames, делал таймаут выстрелам через запуск корутины и WaitForSeconds(). Мой внутренний эстет при чтении такого кода всегда бьёт себя по лицу, но это вкусовщина. А проблема в том, что эти люди, как правило, только таким использованием и ограничиваются. Ничего более интересного, чем задержки, на корутинах и не пишут. Какое неуважение.

Но этого поста не было бы, если бы проблемы с пониманием возникали только у новичков. Опытные сеньористые ветераны порой думают о корутинах, как о неком легковесном способе организации мультипоточки.

Нет, нет и ещё раз нет! Мультипоточка тут вообще ни при чём!

Недопонимание происходит из-за того, что к одной и той же вещи можно прийти с двух сторон. Можно с одной стороны постепенно облегчать мультизадачность и повышать безопасность и прийти от потоков к так называемым Green Threads. А можно вообще идти с другой стороны, пытаясь в однопоточном приложении улучшить читабельность колбеков и стейт-машин, и прийти внезапно к тому же самому. В этом случае результат скорее назовут корутинами. Разница между получившимися вещами с точки зрения функционала будет весьма условна, но отличается мотивация и назначение. Если вы познакомились с корутинами не с той стороны, то эта статья для вас.
Идём со стороны многопоточки

Для начала позанудствуем (если не осилите, пропускайте этот раздел) и вспомним, что многозадачность у нас бывает вытесняющей и кооперативной. Вытесняющая — это обычные потоки. У нас есть несколько физических процессоров и несколько потоков выполнения. В каждый момент времени один поток выполняется на одном процессоре, каждый поток имеет свой собственный стек, а операционная система часто-часто переключает потоки на конкретном процессоре (подменяя стек и регистры процессора). В общем, всё как мы любим: никаких сюрпризов.
Сразу обозначим, что всякие фреймворки на основе Job’ов или Promise’ов, если они приводят к созданию новых настоящих тредов, являются обёрткой над этим же типом многозадачности.

Но есть и другой тип — кооперативный. Это когда переключение контекстов происходит не по воле ОС, а когда сами нити выполнения решили. Те самые yield. На каком-то абстрактном академическом уровне уже в принципе можно заявить, что это и есть корутины, но давайте посмотрим на кооперативную многозадачность чуть повнимательнее.

Представители этого класса могут называться по-разному: фиберы, зелёные треды, горутины. Главное, что каждый такой фибер хранит свой собственный стек, подобно потокам. И если физические процессоры постоянно переключаются между потоками, то потоки уже в свою очередь переключаются внутри себя между фиберами. То есть эти два типа многозадачности могут стакаться и использоваться вместе.

Почему же фиберы/зелёные треды называют легковесными?
- переключения фиберов происходят реже и только тогда, когда они сами решили (например, раз в несколько секунд, а не много-много раз в секунду);
- так как точки переключения заранее известны, то нужно сохранять меньшее количество регистров процессора;
- те фиберы, которые было решено выполнять на одном потоке, не требуют синхронизации между собой.

Это неплохой бонус в сравнении с потоками, но на деле это не так уж и легковесно. При стандартном размере стека в 1 MB, запуск 1024 фиберов приведёт к выделению, как минимум, 1 GB памяти. Это нормально для Green Thread, но совершенно недопустимо для корутин. Для концепции корутин не должно быть проблемой и десять тысяч.

Очевидное решение — это делать динамические стеки, расширяющиеся по требованию. Так делают, например, горутины. И это уже чертовски близко к stackful-корутинам (например тем, что есть в boost). Но разница в мотивации. Горутины всё равно почти всегда используются в контексте многопоточности. Как правило, запускается несколько полноценных потоков по числу ядер процессора, а уже поверх них сотни лёгковесных горутин. Все эти свистопляски были для того, чтобы облегчить накладные ресурсы в изначально многопоточном приложении. Замасштабировав их с небольшого числа рутин до сотен и тысяч без изменения кода.

Фиберы и зелёные треды тоже применяются обычно в тех контекстах, когда речь идёт, так или иначе, о многопоточных приложениях. Будь то портирование с другой платформы или нечто похожее. А корутины это вообще не про это.
Идём со стороны стейт-машин

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

Итак, забудьте пока про ужасный и суетливый мир мультипотока. Переместимся на время в спокойное безопасное место. Вы в своём однопоточном приложении. Больше нет никаких состояний гонок, всё выполняется последовательно и предсказуемо.

Вы пишите игру про ваше тотемное животное. Пусть это будет сурикат. Целая колония сурикатов. Все они бегают по Калахари в поисках еды и боевых приключений. Если каждая особь будет обладать маломальскими самостоятельными мозгами (а сурикаты они такие), то классическая имплементация — это, конечно же, стейт-машины. Ну или конечные автоматы, если вам так угодно. То есть вы пишите много-много классов (по одному на каждое возможное состояние машины), и в каждом классе реализуете функцию update(), которая выполняется каждый игровой тик, и возможно приводит к переходу в другие состояния.

Красота? Благодать? В принципе, да. Но не очень. Этот подход, конечно, понравится вашему тимлиду, потому что так ещё деды писали, но всё-таки писать столько служебного кода довольно утомительно. Ведь ежедневная рутина сурикатов так соблазнительно хорошо описывается в терминах одной обычной функции с ветвлениями:

проснуться();
расчистить_вход_в_норку_от_песка();
пока ещё_не_перегрелись_на_солнце {
выбрать_точку_куда_идти();
пока не_дошли {
сделать_шаг_по_маршруту()
если встретили_пищу, то
есть();
если встретили_врага, то
сражаться();
}
}
идти_в_тенёк().

Согласитесь, намного читабельнее и удобнее, чем писать штук 10 классов под это дело. Единственная проблемка в том, что каждая операция здесь потенциально может выполняться больше одного игрового тика (то есть длиться дольше одного кадра; например, из-за анимации), а потому простой функцией здесь не обойтись. Как вы уже догадались, нам просто нужна возможность писать функции с несколькими точками входа. Или, проще говоря, возможность нашпиговать этот код yield()-ами через строчку:

проснуться();
yield расчистить_вход_в_норку_от_песка();
пока ещё_не_перегрелись_на_солнце {
выбрать_точку_куда_идти();
пока не_дошли {
yield сделать_шаг_по_маршруту()
если встретили_пищу, то
yield есть();
если встретили_врага, то
yield сражаться();
}
}
yield идти_в_тенёк().

Такие фаршированные функции называют генераторами. Они могут быть знакомы многим по C#. И по большому счёту это и есть машина состояний, только записанная более удобным синтаксисом; поскольку внутренне это всё разбивается всё равно на несколько маленьких функций от одного yield до другого, а общие переменные выносятся в небольшую структуру, которая передаётся дальше (на манер того, как это происходит с замыканиями). Каждая такая сгенерированная структурка и соответствующая ей функция по сути и образуют наши олдскульные классы состояний конечного автомата.
Но, по правде говоря, прямо функции и классы в терминах конкретного языка виртуальная машина вам генерить не обязана, потому что они наружу не видны, но по смыслу там ровно это происходит. Но вот эта простая смена формы записи и есть та самая ментальная ступенька, о которой я говорил в начале, так как она сильнейшим образом развязывает руки и мозги геймплейному программисту в плане стейт-машин.

Но погодите. Генераторы это ещё не корутины. Код с yield-ами, что я показал выше, в такой форме использовать вообще-то не получится. Дело в том, что внутри указанных функций есть вложенные yield-ы. Поэтому в вызывающем коде придётся крутить циклы. Либо же возвращать вложенный генератор и написать обёртку, которая будет складировать генераторы в стек и разруливать их вызовы соответствующим образом. Это пишется очень легко и так сделаны, например, всем известные Unity-корутины. И вот это уже тянет на некое подобие нормальных асимметричных корутин.

«Асимметричных», потому что стек выстраивает отношения между генераторами по принципу «вызывающий-вызываемый». Но в принципе можно переключаться между генераторами не по логике стека, а как взбредёт в голову. И тогда такие корутины будут называться «симметричными». В том смысле, что отношения между ними симметричные, то есть все на равных правах. Так можно, например, завести параллельные функции, которые передают друг другу управление по принципу пинг-понга. Но это мы опять же уходим в сторону многопоточных историй, а в нашем спокойном и безопасном однопоточном месте такие выкрутасы не особо нужны. Вернёмся к нашим тотемным животным.

У нас всё ещё не полноценные корутины, а симулякр. Если мы захотим сделать yield где-то далеко на глубине стека вызовов в C#, нам хоть и не придётся теперь крутить цикл в каждом месте, но протаскивать yield всё же придётся через все уровни. Если бы корутины были реализованы на уровне языка, как, например, в Kotlin, то мы могли бы ещё немного улучшить жизнь нашим сурикатам, прокидывая yield с любой глубины. Это намного удобнее. Но не идеально, потому что в Kotlin все промежуточные функции придётся пометить как suspend. Тогда компилятор будет обращаться с этими функциями, как с генераторами (для их промежуточного состояния будет создаваться объект), но эти функции тогда будет нельзя вызывать из non-suspend функций. То есть помечая функцию как suspend, мы рискуем, что часть старого кода отвалится.

— А бывают ли вообще идеальные корутины? — спросите вы. — Чтобы вызываться без боли из любого места и не думать о том, какая там была функция.

Да, бывают. Мы наконец-то пришли к stack-full корутинам. Это когда вместо вот этой всей пурги про разрезание функций на много маленьких и создание структурок-состояний, мы просто берём и сохраняем целиком весь стек приложения. Сохраняем со всеми его обычными и волшебными функциями вообще без ограничений.

Я встречал stack-full корутины как минимум 2 раза. Этол boost-овые в С++. Но сами понимаете: С++ сам по себе такой, что «без боли» к нему слабоприменимое понятие. И, конечно же, в Lua. Это мои первые и самые любимые корутины, потому что корутины там, как говорится, first-class-citizen: они yield-ятся в любой момент и с любого уровня, не требуют менять существующий код и никак не мешают вызывать эти же функции вне корутин — одно удовольствие. Идеально для написания толп сурикат.

Но вы заметьте — и это главная мысль поста — куда мы пришли. Мы начали с разговора об однопоточных стейт-машинах, но закончили чем-то, ужасно напоминающим фиберы или горутины. И это то место, где разница между ними, а вместе с тем и понимание, стирается. В какой-то литературе между фиберами и корутинами проводят чёткую черту по наличию планировщика, в какой-то — это синонимы. Но так или иначе у очень многих в головах сидит ассоциация, что корутины это обязательно что-то про многопоточный код и состояние гонки.

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

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

P.S. Но, кстати говоря, в моём будущем языке KuKu, одной из целей я себе ставлю сделать легко сериализуемые корутины прямо из коробки. Далеко не факт, что получится, конечно. Но будем пытаться.
Угадай мелодию из видеоигр

В 2013 я написал небольшую развлекаловку, в которой предлагается угадывать саундтреки из игр. Там больше сотни наиболее знаменитых треков из игр с датой релиза с 1980 по 2014 (киберпанк тогда собирался выйти в 2014, да). Варианты при этом не показываются: нужно самому начать вводить название и выбрать из автодополнения. Допускается угадать хотя бы серию, а не конкретную часть.

Игре уже немножко плохо: что-то не то с кодировкой в некоторых браузерах и после примерно сотни вопросов счётчик проглючивает. Я всё думал обновить, починив и добавив современных игр, но что-то руки так и не дошли. Подумываю вообще в ближайшее время грохнуть этот сайт и переехать на полностью статичный github.io.

Поэтому предлагаю поиграть, пока ещё работает. Оно прикольное. Кстати, по статистике чаще всего отгадывают саундтрек Super Mario Bros. и NFS:Underground.

http://alprog.net/namegametune/