Технические заметки
19 subscribers
57 photos
15 videos
30 links
Здесь я делюсь техническим опытом, который приобретаю каждый день на работе и дома.

Сетка: https://set.ki/Nheuhoc
LinkedIn: linkedin.com/in/magrega
Download Telegram
В общем, мне подсказали, что это base64. И после расшифровки данные выглядят так:
nۆo86w}:m7 o4y4~彺s4׷wٮ\s7mZn { "id10beta": -1.0, "score5": 987345.0, "score5beta": 0.0, "score9beta": 0.0, "iddlc1": -1.0, "scorerush": 0.0, "id7beta": -1.0, "scoredlc2": 0.0, "score2beta": 0.0, "score1": 591587.0, "iddlc3": -1.0, "score7": 1458799.0, "score9": 1685073.0, "score3": 1051614.0, "idbeta": -1.0, "scorekrampus": 0.0, "idtest": -1.0, "id4": 100963.0, "id2": 290938.0, "id2beta": -1.0, "idrushbeta": -1.0, "score7beta": 0.0, "id5beta": -1.0, "id9beta": -1.0, "id6": 41591.0, "id8": 23348.0, "scoredlc3": 0.0, "scorerushbeta": 0.0, "id10": 13239.0, "username": "AIW", "score4": 1706292.0, "score10": 569536.0, "id6beta": -1.0, "score3beta": 0.0, "score2": 1099632.0, "scoredlc1": 0.0, "id": 376937.0, "idrush": -1.0, "score4beta": 0.0, "iddlc2": -1.0, "score8beta": 0.0, "score8": 1312925.0, "score10beta": 0.0, "score6": 2200470.0, "id5": 62428.0, "id4beta": -1.0, "id8beta": -1.0, "score1beta": 0.0, "scorehc": 0.0, "id3beta": -1.0, "score6beta": 0.0, "id9": 14421.0, "id7": 27085.0, "scoretest": 1051614.0, "id3": 175886.0 }�

Как мы видим, это таблица рекордов из моего сохранения. Логика такая:
В начале всей строки есть рандомный кусочек, назовем его ID — nۆo86w}:m7 o4y4~彺s4׷wٮ\s7mZn . Затем идет JSON с данными сохранения, в данном случае количество очков. Все это шифруется в base64.

На старте игры проверяется ID. Если он совпадает - игра загружает сейвы, если нет, то всё трётся, делаются новые файлы. Таким образом мы можем взять старые сейвы, в них после расшифровки заменить ID на новый и добавить в игру, не теряя, таким образом, свой прогресс.

Понравилось ли мне тратить время на то, чтобы в этом разобраться? Безусловно. Хотел ли бы я этим заняться опять? Конечно, нет.

Сказать честно, я не понял для чего разрабы так запарились и не позволили просто менять файлы сейвов, как в любой другой человеческой игре, но было прикольно узнать, что такое бывает. Я даже запилил гайд в стим.

#steam #textorcist #guide #base64
👍1🔥1😁1
Был у меня давным-давно красивый инстаграм. Я в него заливал только самые эстетичные, на мой взгляд, фотки. Вел я его довольно давно, лет с 20, там была целая хронология становления моей персоны. К сожалению, с наступлением 2022 года то ли активизировались боты в инсте, то ли еще какой-то блудняк произошел, и в инсте стали блочить аккаунты, особенно с РФ, направо и налево.

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

По истечению года мой аккаунт окончательно удалили с серверов. Я зарегался под своей почтой, будто в первый раз, но больше никогда туда ничего не постил.

Потому я решил, что буду, может, по воскресеньям какие-то фотки тут выкидывать, чтобы показать миру как классно я живу.

#instagram
4👏2🥰1
К лучшему или нет, но зима заканчивается, а рабочие обороты увеличиваются. Времени на посты становится меньше, но я приготовил кое-что прикольное.

Я купил себе как-то часы Mi Band 8 на смену своим пожилым Mi Band 2, доставшимся мне от жены. Хотелось просто добавить цвета с кастомными циферблатами к подсчету шагов и калорий. На них нашелся циферблат, который мне невероятно сильно понравился. От картинки чувствовался такой осенний, немного хеллоуинский вайб с примесью какой-то диснеевской сказки. Но был у них баг один, дни недели отставали на один. В понедельник показывали воскресенье.

У восьмой версии часов оказался какой-то совершенно непонятный алгоритм счета калорий, который насчитывал мне по сожженных 400 ккал пока я просто сижу за компом. Решил вернуть. Взял 7 версию, на которой старый алгоритм подсчета, меня это устроило, но того циферблата на них не было. Ну что ж, полез курить форумы.

Накурился и узнал такую вещь:
Файлы циферблатов идут в формате .bin, но по сути они открываются обычным ZIP-архиватором. А внутри...
🎉 JavaScript 🎉

Вы не ослышались, ребята! Внутри .json-файлы манифестов, куча ассетов в видео картинок и javascript-код.

Потому, смачно похрустев пальцами, я полез исправлять циферблат и пересобирать его для 7 версии Mi Band. Обратно всё собирается программой ZMake, точнее, адаптируются картинки под ZeppOS.

Такие вот дела.

#javascript #zeppos #miband
🔥4
Про баланс работа-жизнь.

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

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

Так случилось, во-первых, потому что кроме работы я ничем не занимался, а, во-вторых, потому что интенсивность такой рабочей жизни свела на нет почти всё беззаботное дружеское взаимодействие и стало своеобразным фильтром общения, оставив меня наедине с моими амбициями.

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

Всё хорошо в меру, ребятки.

#personal
2
Я придумал аудиосообщения в телеграм

Когда я учился в 7-8 классе школы, я оооочень активно переписывался смсками. Тогда у Теле2 был классный тариф, в котором смски стоили то ли 5, то ли 10 копеек, что позволяло продолжительно чатиться.

Одни вечером я гостил у соседа, общался и пил чай. Телефон без конца вибрировал сообщениями, отвечая на которые можно было бы потратить кучу времени (все помнят клавиатуры с Т9?).

И мне тогда пришла в голову мысль, которую я озвучил вслух. Мне показалось, что было бы классно иметь возможность записать голос и отправить его как сообщение. Сосед посмотрел на меня как дебила и сказал: "Так просто позвони..."

И где мы сейчас находимся? 😲
Не знаю, как Паша выудил у меня из ума эту мысль, но жаль, что я не запатентовал ее тогда.

А лет 5 назад, кстати, я подумал о том, как меня достали проводные мышки, клавы и наушники. Как было бы круто сделать все беспроводным, но при этом не иметь необходимости это всё заряжать. Например, мышки и клавиатуры могли бы заряжаться от коврика (типа MagSafe). То есть, пока периферия на коврике - она заряжается, не надо пихать в нее никакие провода. В любой момент можно ее взять с коврика и работать уже от аккумулятора.

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

Прикольно было увидеть, что идею с ковриком реализовали. Про наушники не знаю, но раз я об этом подумал, значит скоро появится. 😎

А вас посещали передовые идеи?

#telegram #wireless #idea
❤‍🔥1🔥1🤯1
Важность понимания сложности алгоритмов

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

В какой-то момент стали поступать жалобы от пользователей на то, что приложение вылетает на андроид-устройствах. Сами устройства довольно старые: RFID-считыватели на Android 6 и Android 14. После диагностики поняли, что суть в том, что тупо кончается оперативная память и приложение падает. Сразу оговорюсь, что к падению приложения причастен был и ряд других проблем, но в рамках темы рассмотрим только алгоритм.

Суть: в приложении есть функционал выгрузки части базы данных на устройство для оффлайн работы. При выгрузке из БД (у нас couchdb 1.7.2 😢) тянется 130000 документов. Пару секунд приложение пыжится, потом ломается. Предлагаю рассмотреть изначальный код:
obj_arr.forEach(obj => {
if (obj.m_type === OT_BAG) {
obj.art_count = obj_arr
.filter(x => (x.m_type === OT_ART) && (x.container === obj.id))
.length;
}
});

Как мы видим, внутри цикла forEach на каждой итерации вызывается filter, который проходит по тому же массиву. У нас квадратичная сложность вычисления O(n²).

Для тех, кто не в теме, приведу пример: представим, что у нас есть массив, в котором 1000 элементов. Проходя по нему одним циклом мы имеем линейную сложность вычисления O(n), потому что мы пройдем по всем элементам без исключения один раз. В данном случае n - количество элементов в массиве (1000). Количество операций равно количеству элементов.

А теперь представим, что проходя по одному элементу, мы вызываем еще один цикл, который проходится по тому же массиву (цикл в цикле). Сколько будет операций? На каждую операцию будет тоже O(n) вычислений. То есть, 1000 * 1000 = 1000000 операций. То есть, O(n * n) = O(n²).

Однако, если мы не будем вызывать цикл в цикле, а найдем способ вытащить один цикл из другого (а еще лучше, воспользуемся хеш-таблицами 😉), то наша сложность снова станет линейной. И вместо миллиона операций на массив мы вернемся к 1000, что является тысячекратным упрощением.

В общем, решили мы это так:
let bag_art_count = {};

rows.forEach(r => {
const value = r.value || {};
if (value.m_type === OT_ART && value.container) {
bag_art_count[value.container] =
(bag_art_count[value.container] || 0) + 1;
}
});

let obj_arr = rows.map(r => {
const obj = Object.assign(
{ _id: r.value.tag.substr(0, 24), id: r.id },
r.value
);

if (obj.m_type === OT_BAG) {
obj.art_count = bag_art_count[obj.id] || 0;
obj.is_suited = (obj.art_count === 0);
}

return obj;
});

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

#javascript #algo #android
🔥31
У нас довольно непростая история с доступами во внешнюю сеть на работе. Если еще в рамках ежедневной работы что-то можно погуглить, но если нужно посветить виртуалкой в инет для простоты работы, то тут начинаются разнообразные этапы согласований, которые могут отнять время и желание вообще что-либо делать.

Очередная такая ситуация возникла, когда мы запустили виртуальную машину, чтобы развернуть на ней копию прода для тестов. У нас очень много чего завязано на SSL-сертификатах, но в нашей ситуации получать домен и светить айпишником в инет не было реализуемым вариантом. Я до этого с подобным не сталкивался, потому начал думать, как решить, чем сейчас и поделюсь.

Если кратко, то мы выпустили самописный сертификат на айпи адрес в локальной сети. По сути, работает точно так же, как сертификат от Let's Encrypt. Отличие лишь в том, что нужно вручную устанавливать этот сертификат в операционную систему. Вот процедура:

1) Создаем чистую папку для работы, чтобы не запутаться в файлах.
mkdir -p /root/certs
cd /root/certs


2) Создаем «Главный ключ» и «Главный сертификат». Этим сертификатом мы будем подписывать все остальные.
openssl genrsa -aes256 -out rootCA.key 4096
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 3650 -out rootCA.crt

Итог: Появятся rootCA.key (секретный, никому не давать) и rootCA.crt (публичный, его будем ставить на устройства, которые будут ходить на наш сервер).

3) Создается файл настройки с расширением .conf примерно такого содержания:
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = req_distinguished_name
req_extensions = req_ext
x509_extensions = v3_req

[req_distinguished_name]
C = RU
ST = Moscow
L = Moscow
O = CompanyGroup
CN = 101.148.223.60

[req_ext]
subjectAltName = @alt_names

[v3_req]
subjectAltName = @alt_names

[alt_names]
# List all DNS names and IPs here
DNS.1 = app.mycompany.ru
IP.1 = 101.148.223.60
IP.2 = 190.118.127.12
IP.3 = 127.0.0.1

В [alt_names] мы прописываем адреса, по которым будем ходить на сервер, для которого выпускается сертификат. Нас интересует IP.1 - адрес сервера в локальной сети.

4) Генерим ключ сервера и запрос на подпись:
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout /etc/nginx/ssl/server.key \
-out /etc/nginx/ssl/server.crt \
-config cert_config.cnf

Появятся файлы server.key (секретный ключ) и server.csr (запрос).

5) Подписываем запрос нашим корневым сертификатом:
openssl x509 -req -in server.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial \
-out server.crt -days 3650 -sha256 -extfile server.conf -extensions req_ext

Появится server.crt.

6) На этом этапе я добавил созданные сертификаты server.crt и server.key в nginx и перезапустил службу.

По сути с сертификатами всё. Теперь просто берем rootCA.crt и устанавливаем его на устройства, с которых планируется работа с сервером. Я устанавливал на Android и на Windows 11.

#ssl #dev #debian
👏3🔥2❤‍🔥1💯1
Сегодня расскажу про проброс портов в VScode. Удобная фишка, когда разрабатываешь сайт и его нужно показать кому-то. Позволяет не делать никакой деплой, все делается в рамках среды разработки.

Через сервера Microsoft Azure делается туннель, который ведет на вашу машину и ваша разработка становится доступны из внешней сети.

Делается просто:
Например, допустим, у нас просто вёрстка HTML-файлов с помощью встроенного Live Server на порту 5500.

В VScode в терминале жмем Порты. Если терминал закрыт, можно открыть с помощью Ctrl + `, либо Вид - Терминал.

Жмем Перенаправить порт. Вводим порт 5500. Расширение попросит залогиниться - делаем.

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

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

На скринах можно посмотреть как это все выглядит.

#vscode #portforwarding
2
Когда я был молод, 10 лет назад, я уехал работать преподом английского в Таиланд. Такой поворот судьбы стал результатом хронической усталости от работы, на которой количество получаемого не совпадает с количеством отданного. То был, пожалуй, один из первых важных уроков, предоставленных мне жизнью.

Урок заключался в том, что изменения, которые мы ждем увидеть, находятся гораздо ближе, чем нам кажется, и вся сложность лишь в том, чтобы договориться с самим с собой оставить проигрышные условия и начать заново. В своем отчаянии я бросился в гугл искать все частные школы Таиланда и закидывать их имейлами со своим резюме. Через месяц я нашел школу. Через 2 месяца я уже был там. Настолько сильно я хотел поменять что-то, лишь бы было не так, как сейчас.

Из Таиланда я вернулся полным решимости вносить изменения дальше и, как только получил диплом, собрал вещи и уехал в Москву, откуда начался весь мой профессиональный рост.

#personal #thailand
2