Cіпласпластик
542 subscribers
163 photos
35 videos
2 files
260 links
🇺🇦 Про айті та дотичні теми загалом, ну й трохи про C++.

Мої емоджі:
https://t.me/addemoji/AdaptiveDevIcons
https://t.me/addemoji/VehicleBrands
Download Telegram
Давно я не писав нічого про 💻. На жаль і самою мовою теж, шо вже поробиш.

Але трапилася стаття: Індія розробила свій власний «вітчизняний» 32-бітний процесор Vikram для ракет і супутників. І найцікавіше тут те, що під нього вони розробили власний набір інструментів для програмування на Ada й уже працюють над компілятором для 💻. Всі-то звикли вже, що сішний тулсет пишеться найпершим, а тут навпаки!

Воно й зрозуміло. Якби я програмував щось для космічних супутників, то теж би на C дивився в останню чергу. Ada дозволяє писати значно виразніший і безпечніший код з формальною верифікацією коректності (і при цьому мова не створює відчуття якоїсь незавершеності, як деякі сучасні «конкуренти»). Короч, Ada — ваш бро.

До речі, у TIOBE вона зараз на 13-му місці, а в PYPL на 12-му, що значно вище, ніж більшість з вас могла очікувати. Ada обійшла, наприклад, усілякі Ruby й Kotlin, а залежно від індексу ще й Go або PHP.

Буквально нещодавно ще виходила стаття про Ada українською: там і про відновлення популярності мови, і про українську спільноту, і про miltech. Гляньте, якщо цікаво.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥15👍2🏆2👀1
Коли роками пишеш один проєкт, особливо у великій команді, то багато речей лишаються «за кадром», бо в тебе є твій шматочок коду й твоя задача, а що там поза ними — вже не так цікаво. Або ж цікаво, але часу розбиратися нема. Там свої спеціалісти приведуть усе до ладу.

А коли команда до десяти людей і проєкти невеличкі, ще й стартують частіше, ніж раз на пʼять років, то треба їх якось початково сетапити, нерідко власноруч. А якщо це не «повноцінні» проєкти, а прототипи — то критично важливо це ще й робити швидко, бо прототипи можуть писатися по декілька штук на місяць/тиждень/день (а потім іти у смітник 🥲).

І для себе я відокремив мінімальний набір найважливіших штук, які намагаюся налаштувати зі старту:
1. Білд однією командою.
2. Лоґування.
3. Конфігурація.
4. Тести.

З першим усе наче зрозуміло, бо я вже неодноразово писав у попередніх дописах.

Друге — це прям must have. Я зазвичай не користуюся зневаджувачем з низки причин. По-перше, у C++ з ними поганенько. По-друге, колись давно компонувальник просто не міг зібрати дебажний білд нашої програми для телефону, бо він був 32-бітний, а в нас там ліби по 800+ мегабайт (шаблони розгорнулися ггг). Тож я звик користуватися лоґами. І бажано, щоб це був не cout або print, а нормальне лоґування з часом тощо. А якщо ще й у файл пише, то взагалі збс. І щоб для користування не треба було навколо танцювати, а достатньо було додати один #include або import у будь-який файл.

Щодо третього можу сказати таке: будь-яка програма, що стає більшою за пару-трійку екранів коду, починає в тому чи іншому вигляді потребувати конфігу. Особливо якщо це прототип, бо мета будь-якого прототипу — це перевірити гіпотези й погратися з варіантами. Тому аби згодом не витрачати час на виведення певних змінних у конфіг, я сетаплю його й користуюся від самого початку. До речі, не важливо, в якому саме вигляді той конфіг є — важливо, щоб він був хоч в якомусь.

Тести… Їх народ не любить писати. І я не любив і досі не люблю. Але згодом я збагнув, що не люблю цього робити не тому, що це якось складно, нецікаво або не дуже потрібно, а тому, що якогось хєра це завжди дуже марудно! Обовʼязково треба поприсідати, щось кудись прокинути, десь ввімкнути, прописати тощо. Отже: сетапимо все з самого початку, робимо порожній тест, який нічого не перевіряє, але він є — і вже люди зможуть далі робити за зразком. (Це як перлина: щоб вона сформувалася, треба якусь пилинку, навколо якої потім вже нашаровується перламутр). А далі замість того, щоб додавати в програму якесь тимчасове лоґування проміжних значень, запускати програму, дивитися, чи правильне там значення, чи ні — замість усього цього просто робимо тест, який робить те саме: перевіряє проміжні (чи які вам треба) значення! Зручно й швидко 🙂

І коли насетапив щось подібне вже 5–10 разів, то починаєш замислюватися, як би цей процес прискорити: мутиш собі якісь шаблони проєктів, використовуєш певну структуру тек, одні й ті самі бібліотеки тощо.

Добре, коли сама мова тобі допомагає це робити. Наприклад, в тому ж Nim 👑, памʼятаю, тести дуже легко писати. Прямо в тому самому файлі навалюєш їх — і все працює. Або в Ruby ♦️ та Crystal 🔮 теж класно.

А днями дізнався про прикольну фішку мови Chapel: там є конфігураційні сталі та змінні! Буквально додаєш config перед оголошенням
config const steps = 42;

як отут 👆, і бем! — жодних додаткових рухів не треба. Тепер можна або ключем значення передавати: myapp --steps=100500 — або через конфіг-файл. Так, цукор, але люблю таке. (А от у сусідньому чаті, де я це побачив, чувак якийсь підгорів з того config і доводив, що це кепська фіча. Хз.)

Насправді окрім цих чотирьох пунктів є ще пʼятий, який я намагаюся завжди мати — так званий hot reload. Але це вже точно окрема історія.
Please open Telegram to view this post
VIEW IN TELEGRAM
15👍4👀1
Ну добре, годі тягнути кота за хвіст. Я вже писав, як ми втомилися від Qbs, писав про інші системи, які ми спробували. І врешті-решт я зупинив свій вибір на Xmake.

Як і Premake, ця система базується на 💻, але на цьому спільні риси закінчуються. Чи закриває Xmake усі мої потреби? Абсолютно ні. У ньому купа недоліків. Попри це він задовольняє деякі з моїх критеріїв, які мені критично важливі, і я вважаю, що цей компроміс того вартий.

В Xmake уже є підтримка збирання 💻-програм, але вона вкрай кепська: якоюсь мірою працює для умовного Hello World, але не для повноцінної програми. Нам довелося написати декілька власних правил — і наскільки ж це легше робити, ніж у Qbs! Хоча прихованих проблем теж удосталь. За той місяць, протягом якого ми вʼяло колупаємо Xmake, я вже зарепортив декілька багів і створив низку запитів на фічі.

Шила в мішку не втаїш, тому одразу зазначу один з найпомітніших нюансів щодо розробки Xmake… Це китайський продукт 🙂 Тому на ґітгабі нерідко можна побачити тікети на кшталт отакого: xmake在mingw下无法编译. Не кривитиму душею — це трохи лякає. Не хотілося б, щоб воно збирало програму разом зі вбудованим бекдором 😅 З іншого боку декілька багів, які я знайшов, я сам же й виправив. А ті, шо не виправив, автор пофіксив сам протягом доби-двох. Отже, до цього взагалі жодних нарікань: сирці зрозумілі, виправляти не складно, на ревʼю тільки валідні коментарі — все швидко й адекватно.

Опис білда на Xmake переважно декларативний, але можна (інколи треба) й імперативні скрипти писати. Є підтримка різних компіляторів і навіть мов, є якась підтримка модулів з C++20, усілякі там віддалені й розподілені білди, кому треба, створення пакетів з готовими програмами тощо.

Одна з найвагоміших переваг Xmake — я нарешті зміг позбутися всратого Conan, бо в Xmake є вбудований менеджер пакетів з централізованим сховищем, але й створити власне дуже легко — це просто 💻-репозиторій. Я вже створив окреме для наших бібліотек. До речі, якщо пакет збирається чимось іншим, наприклад, тим же сімейком, то це не проблема — все легко інтегрується.

Інша вагома перевага: можна створити пакет з кастомними правилами для збирання. Саме це я й намагаюся робити, бо мене замахало їх копіпастити між проєктами. Потім в одному щось покращив, в інший не переніс, і навпаки… Дуже дратує. А тепер я можу просто додати залежність на правила — і жодного головного болю від ґіт-сабмодулів або ще чогось. Ну, принаймні в теорії, бо на практиці того ідеалу, що в мене в голові, я ще не досяг.

Зараз я вже бачу, що багато з того, що мені треба, в Xmake відсутнє. Не було правил для автоматичної генерації Qt-ресурсів, qmldir-файлів, компіляції шейдерів (це ми написали все), немає підтримки InnoSetup, AppImage або Flatpak, macOS DMG тощо. З іншого боку я нарешті бачу шлях, як це написати власноруч, не чекаючи, що хтось з розробників системи збирання це зробить.

З документацією все теж складнувато: багато важливих і корисних штук там взагалі не описані. Зате сирці читати доволі легко, адже мови, простішої за Lua, ще не вигадали.

Звісно, є відчуття, що я проміняв одну мутну систему на іншу. Типу, чого б уже просто не взяти CMake 🤮‽ Але у підсумку мені здається, що якраз-таки цей вибір був дуже прагматичним. І попри купу недоліків, мої потреби Xmake закриває краще.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🥴2🤣1
Писав же нещодавно про важливість на самому початку проєкту закласти можливість використовувати в ньому конфіги й логування.

Для C++ на жаль бібліотек, які б мені реально подобалися, для цього нема.

А от на Python 💻 у мене є пара улюблених. Закладаюся, ви про них знаєте, але якщо ні, то прошу 🙂

Перша — це Dynaconf. Вона дуже фічаста. Читає конфіги й із файлів, і зі змінних оточення, і бозна-звідки ще. Підтримує купу різних вхідних форматів для налаштувань: TOML 💻, YAML 💻, JSON 💻 тощо. Я зазвичай користуюся YAML, бо руками його писати найприємніше. (І шо ви мені зробите, га‽). Також прикольна фішка з шарами — постійно нею користуюся. (Це як у VS Code 💻, де є сталі глобальні налаштування, є користувацькі і є проєктні. І кожні з них нашаровуються на попередні й перевизначають деякі поля. Знаєте ж?) Так от цим зручно користуватися, коли, наприклад, при розробці ви підʼєднуєтеся до якихось локальних серверів, вмикаєте додаткове логування, якісь ще фічі, API-токени читаєте з локального файлу, а в продакшні використовуєте віддалені сервери зі своїми ендпоїнтами, вимикаєте різні «дірки» для дебагу, токени берете десь з Vault тощо. Один раз налаштували профілі, а потім між ними перемикаєтеся — зручно.

Друга ліба — це structlog. Взагалі зазвичай коли кажуть про структуровані логи, то мають на увазі, що замість звичайного тексту в журнал записуються JSON-обʼєкти, причому часто не в локальний файлик, а в спеціалізований сервіс на кшталт Elastic. Але в мене таких юзкейсів нема.

Насправді ж ідея в тому, що ваш логер оперує не текстом, а обʼєктами. А це значить, що можна робити купу прикольних штук! Наприклад, можна додавати щось у контекст, і цей контекст прикріпиться до повідомлення. У мене ось є тестовий бот для тґ, в якому окремий middleware прикріплює в контекст повідомлення інформацію про команду, яка прийшла від користувача (якщо це саме запит з командою):
async def add_command(self, event, data) -> None:
match data:
case {'command': CommandObject(prefix=prefix, command=command)}:
structlog.contextvars.bind_contextvars(command=f'{prefix}{command}')

Ну й низка інших схожих штук. (Нижче є зняток екрану).

Якщо ви весь час оперуєте обʼєктами, то це значить, що можна побудувати ланцюжок додаткових обробників. Це приблизно як в нормальному (тобто 🆕) шелі, де ви через жолоб переливаєте з однієї команди в іншу не просто потік байтів, а структуровані дані. Завдяки цьому повідомлення, що логуються, легко додатково якось обробити: агрегувати в них щось, пофільтрувати, змінити структуру — будь-що. Наприклад, уявіть ситуацію, що при розробці ви хочете в терміналі бачити якомога більше даних в логах: різну зневаджувальну інформацію, параметри функцій, тіло запитів тощо. Але в продакшні цього робити не можна, бо логи у вас пишуться в якусь базу, і туди можуть випадково потрапити приватні користувацькі дані. Якщо логер у вас текстовий, то доведеться якісь милиці ставити, а якщо структурований, то ви легко можете написати окремий фільтр, який ті дані видалить або замаскує.

В якийсь момент ті обʼєкти все-таки доведеться перетворити в щось. Тому кожний ланцюжок обробників закінчується штукою, яка рендерить обʼєкт у конкретний формат. Оце, наприклад, стандартний для виводу в термінал:
processors = [
structlog.contextvars.merge_contextvars,
structlog.processors.add_log_level,
structlog.processors.StackInfoRenderer(),
structlog.dev.set_exc_info,
structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S", utc=False),
structlog.dev.ConsoleRenderer()
]

І нижче на знятку — те що отримуємо на виході (там кольорові штуки в кінці повідомлень — це контекстні змінні, які додаються моїм кодом автоматично! Дуже зручно).

Якщо знаєте якісь прикольніші ліби або аналогічні для інших мов, то напишіть мені в коментарі, будь ласка )
Please open Telegram to view this post
VIEW IN TELEGRAM
7👍7🤣2
Стикнулися тут з проблемою: текст для перекладу гри — це просто здоровезний список рядків у довільному порядку без жодного контексту. Виходить, що перша фраза в діалозі може бути з аідійшкою #23456, а наступна вже #76543 (тобто між ними 50 тисяч інших текстових шматків). Отже, дуже складно зробити переклад узгодженим — купу енергії потребує.

Але ж у самій-то грі ці рядки якось повʼязані в суцільний діалог! А значить, є шанс цю інформацію звідти видобути. Цим я й зайнявся.

Про сам процес розкопування ресурсів гри я згодом ще розповім, а зараз скажу лише, що мені це вдалося. Половина справи зроблена.

А інша половина — це відображення цих даних. Тож сів і зробив інструмент для перегляду діалогів. Про фічі вже розповів у відосі, а тут напишу про технічну складову.

У вебі я не тямлю, але знайомий топовий чувак @marktanashchuk порадив мені SvelteKit, і мені норм зайшло. Замість ноди я взяв Bun, бо він принаймні швидко працює, до того ж підтримує 🕸 з коробки.

Майже все написав мені 🐈 Copilot (з Claude 4). За три дні на це пішла приблизно половина місячної норми токенів 😆 Закласти фундамент проєкту було найскладніше. ШІ-шка традиційно згенерувала дохуїльйон коду, зробивши низку некоректних припущень. Довелося це все видаляти й іти дрібнішими кроками. Згодом в пригоді став 💻-сервер для DevTools, завдяки якому можна ШІ-агенту прям сказати: «Піди й сам подивися, що за лайно ти наробив», — а він іде, дивиться й виправляє (а завдяки хот-релоаду й перевіряє одразу).

Всі діалоги насправді експортовані в JSON Canvas, який виявився відкритим форматом. Рендер графа робиться через D3 в SVG з домішками HTML через <foreignObject>. Потім з цього збирається статичний вебсайт, який я через GitHub Actions розгортаю на Cloudflare Pages.

Завдяки цьому всьому тулза не залежить від конкретної гри. На прикладі у відео діалоги з першої Baldur's Gate, яка звісно вже давно перекладена. З 18 МБ вхідних даних на виході отримав 56,5 МБ сайт, тобто роздуло його втричі. Зате не вимагає виконання жодного коду на сервері взагалі.

На перших порах розробки, коли мій рендер був ще такий собі, використовувати JSON Canvas було дуже зручно, адже будь-який експортований діалог можна було переглянути в тому ж Obsidian. Зараз вже бачу, наскільки їхня спека мене обмежує. Думаю, згодом, може, зроблю власну надбудову.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11🔥4😱1👀1
Колись вже писав був статтю про локалізацію текстур у грі. На той момент треба було чік-чік і в продакшн, тому ми там чимось щось кудись експортували, моєю тулою обробили, потім назад якось запхали — доволі незручний процес.

Згодом зʼявився настрій то все покращити. А для цього треба розібратися, як у ресурсах гри що зберігається. На щастя основну роботу зворотної розробки вже проробили модери й навіть документували структуру файлів, тож мені лишалося цим скористатися.

Отже, в старих іграх на рушії Infinity Engine — Baldurʼs Gate, Planescape: Torment тощо — структура приблизно така: є key-файл (зазвичай називається chitin.key) і є тека data з низкою BIF-файлів (не дуже багато — штук 80, наприклад). Але фактично рушій працює з конкретними ресурсами: маю на увазі всілякі скрипти, зображення, описи анімацій, діалогів, звуки тощо. От їх якраз купа. Наприклад, у першій Baldurʼs Gate їх 37341 штука.

Але жорсткі диски в компах раніше були доволі повільні, а інколи й доволі малі, тому в певних випадках доводилося грати зі вставленим CD, який ще повільніший. Ну а файлові системи на вінді й зараз не фонтан у сенсі швидкодії. Тому читання такої кількості дрібних файлів напряму з жорсткого диска — це вирок. Натомість краще мати меншу кількість великих файлів. Саме так розробники й зробили (та й досі роблять).

Отож key-файл — це бінарний формат, який описує всі ресурси, а також де саме вони лежать. А лежать вони в BIF-файлах. Кожний BIF — теж бінарний: фактично архів, у який напаковані різні ресурси, тобто файли й тайлсети. Розробники їх згрупували якось для зручності.

Я вже мільйон разів писав, як мені подобається Kaitai Struct 🏗, тож не обійшлося без нього й цього разу. Писати бінарний парсер по готових спеках — якесь дивне задоволення, трохи медитативний процес навіть. Сидиш ото, пиришся в hex-редактор, щоб переконатися, що ніде не сплутав big-endian з little-endian — кайф! Хоча цього разу парсери конкретно для key та BIF сів писати мій дружбан.

А мені ж треба було вже працювати з конкретним типом ресурсів. В ідеалі я хотів би читати їх прямо з key+bif-файлів так само як це робить гра, але на той момент єдиним виходом було експортувати їх вручну іншим інструментом в локальну файлову систему. Постає питання: як написати код так, щоб потім не треба було його адаптувати?

Я пишу на 🦶 і спочатку думав абстрагувати якось процес читання в якийсь Reader абощо, а потім передавати його у свої обробники всюди. Але це якось кволо. До того ж якщо подумати, будь-який файл — це вже Reader, а точніше io.Reader. Тож гіпотетично можна написати свою імплементацію. Правда, виявилося, що для Kaitai одного io.Reader замало — треба ще io.Seeker, щоб можна було читати з будь-якого офсета.

Тут я натрапив на лібу spf13/afero, яка ніби трохи спрощувала все це. Тож я просто завʼязався на інтерфейс afero.Fs усюди у своєму коді, і на той момент працював з локальною файлухою. А згодом написав свою «віртуальну» файлову систему, яка дає змогу працювати з усіма запакованими ресурсами гри «прозоро» для користувача, хоча насправді я читаю їх прямо з BIF-файлів без проміжного видобування. І вуаля: підмінив одну фс на іншу, а воно досі працює 😎

Повільнувато трохи тільки 😅 Наприклад, видобування ~1200 файлів на 25 МБ загалом у мене на M1 Max ішло 2,5 хвилини 😂 Довелося зайнятися профілюванням, яке, маю зазначити, в Go дуже легко додати в програму! Посиділи трохи з тим же друганом, знайшли найболючіші місця, і тепер видобування всіх ресурсів на майже 2 ГБ загалом триває близько 12 секунд.

У підсумку скажу (знову) пару слів про Go 💻: я ще починаючи з торішнього Advent of Code писав, що мова мені не подобається. І в принципі це досі так. Вона якось деревʼяно відчувається, немає в ній наче фану зовсім. Але наскільки ж легко й зручно в ній доводити справу до кінця! От просто сідаєш і пишеш без виїбонів, а воно потім працює, так ще й компілюється в один бінарь для будь-якої системи. І тулінг зручний. Можливо, це наразі єдина розробка від ґуґла, яку я поважаю.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6