Угол Эйлера
12 subscribers
6 photos
4 videos
13 links
О программировании, веб-разработке, разработке игр и многом другом...
Download Telegram
Boost – замечательная библиотека со множеством функциональности, которой так не хватает в стандартных либах C++. И, конечно же, при её сборке есть небольшие нюансы.

Подробности в статье
👍5
This media is not supported in your browser
VIEW IN TELEGRAM
Я уже писал про завершение создания нами (Студия 42) браузерной казуальной игры про доблестную выхухоль Плюха. С процессом было связано много интересных особенностей, изысканий, решений о которых и постараюсь рассказать.

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

Подробнее
🔥3👍2
Очень люблю кодить на Coffeescript вместо новомодного Typescript, но его поддержки становится всё меньше. Сделал свой шаблон, объединяющий всё нужное для разработки веб-фронтендов: Coffeescript, Vue, SASS, Pug, Vite.

Репозиторий

Update: Сделал стильно, модно, молодежно. Теперь можно разворачивать шаблон из консоли стандартным способом npx coffee-vue-spa your-project-name. Ну и пакет получается здесь
2
Одним и существенных моментов экономии бюджета игры про Плюха (про которую писал ранее) было то, что в ней планировалась мини-игра с механикой «Три в ряд». И эта механика была разработана мной задолго до старта работ по Плюху. Очень удачно получилось и то, что саму логику выделил в отдельный модуль и при создании мини-игры мне не пришлось ничего менять, лишь впоследствии добавил немного функциональности.

Сейчас выделил эту механику в отдельный пакет npm (репозиторий), она уже успешно используется в двух местах: для Плюха и для моего варианта.

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

В движке можно менять количество "камней" по ширине и высоте, определять их типы.

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

Подробнее в статье
🔥3👍2
Немного про PHP и управление зависимостями

При разворачивании экземпляра проекта локально для разработки столкнулся с проблемой, что репозиторий одной из них больше не доступен, но её код есть. И есть composer.lock с прода.

Решение.

Прописываем путь к коду недоступного пакекта в composer.json:

{
//...
"require": {
//...
"vendor-name/pack-name": "*"
},
//...
"repositories": {
//...
"vendor-name/pack-name": {
"type": "path",
"url": "libs/vendor-name/pack-name",
"options": {
"symlink": false
}
}
}
}


Переписываем себе composer.lock и обновляем:

composer update vendor-name\pack-name
👍2🔥1
Самоподписанный сертификат для нескольких доменов для локальной разработки

Файл конфигурации создания сертификата (например ssl.conf):

[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no

[req_distinguished_name]
C = RU
ST = Your Region
L = Your City
O = Department
CN = localhost

[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = site1.localhost
DNS.2 = site2.localhost
IP.1 = 127.0.0.1


Следует лишь поменять домены на свои. Потом выполняем генерацию сертика на 10 лет:

openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout server.key -out server.crt -config ssl.conf


Пример для подключения в Apache 2

В httpd.conf:

# добавляем, если нет
Listen 443
# включаем, если выключены
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
LoadModule ssl_module modules/mod_ssl.so


В httpd-vhosts.conf:

<VirtualHost *:443>
DocumentRoot "C:/path/to/your/folder"
ServerName site1.localhost
ServerAlias site2.localhost
ErrorLog "logs/site1-error.log"
CustomLog "logs/site1-access.log" common
SSLEngine on
SSLCertificateFile "C:/path/to/your/server.crt"
SSLCertificateKeyFile "C:/path/to/your/server.key"
<Directory "C:/path/to/your/folder">
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
👍2🔥2
This media is not supported in your browser
VIEW IN TELEGRAM
Отличное средство скелетной анимации для Pixi.js

Вообще, скелетная анимация с инверсной кинематикой в разработке 3-мерных игр является чем-то само-собой разумеющимся. Для меня было абсолютной неожиданностью, что есть подобные удобные средства и для 2-мерного мира. Хотя, это и должно быть ожидаемо.

Есть много решений. Мы выбрали Spine, потому что для рендерера Pixi.js нашей игры про Плюха у него есть среда выполнения и наш художник-аниматор имел опыт работы с ним.

Приятным моментом стало и то, что интеграция оказалось довольно простым делом.

У рантайма довольно много примеров.

Подробнее
👍2🔥2
Lua в DAW Reaper

На днях столкнулся с интересной задачей – программирование контроллера Behringer X-Touch Mini для управления DAW Reaper. API для Reaper-а есть для нескольких языков, в том числе Python, но мне захотелось вспомнить Lua. Требовалось управлять ручкой энкодера на контроллере ползунком в Reaper-е и обратно. Вот что получилось:

local DEVICE_NAME = 'X-TOUCH MINI'
local TARGET_CC = 29
local MIDI_CHAN = 10

local midi_out = nil
local last_vol = nil

-- Отладочный удобный print
function print(...)
local args = {...}
local msg = ''
for i, arg in ipairs(args) do
msg = msg .. tostring(arg) .. '\n'
end
reaper.ShowConsoleMsg(msg)
end

-- Найти X-Touch Mini
function findMidiOut()
local cnt = reaper.GetNumMIDIOutputs()
for i = 0, cnt - 1 do
local _, name = reaper.GetMIDIOutputName(i, '')
if name and type(name) == "string" and name:find(DEVICE_NAME, 1, true) then
return i
end
end
return nil
end

-- Обработка входящего MIDI
function onMidi(msg)
local status = msg:byte(1)
local cc = msg:byte(2)
local val = msg:byte(3)
if not status or not cc or not val then
return
end
if last_vol ~= val then
print(string.format('С устройста: %d', val))
end
last_vol = val
if (status & 0xF0) == 0xB0 and (status & 0x0F) == MIDI_CHAN then
if cc == TARGET_CC then
local track = reaper.GetSelectedTrack(0, 0)
if track then
local min_dB, max_dB = -60, 12
local dB = min_dB + (val / 127) * (max_dB - min_dB)
local vol = 10^(dB / 20)
reaper.SetMediaTrackInfo_Value(track, "D_VOL", vol)
end
end
end
end

-- Проверяем каждый входящий MIDI сигнал
function midiLoop()
local buf
_, buf, _, _ = reaper.MIDI_GetRecentInputEvent(0)
if buf then
onMidi(buf)
end
end

-- Инициализация
midi_out = findMidiOut()
if not midi_out then
reaper.ShowMessageBox("X-Touch Mini не найден для вывода MIDI.\nУбедитесь, что он подключён и виден в Preferences → MIDI Devices.", "Ошибка", 0)
else
local msg = string.format('Найдено устройство: %s', DEVICE_NAME)
print(msg)
end

-- Основной цикл
function main()
midiLoop()
reaper.defer(main)
end

main()


Надо сказать, что задача фидбека громкости из DAW в контроллер выполнить не получилось. API Reaper-а здесь не при чем. Пробовал сторонними способами – эффект нулевой. Есть подозрение, что устройство не принимает фидбек или надо обновлять прошивку.
👍3🔥2
Удобный сервис мониторинга хостов и экземпляров Nginx, в котором была возможность мониторить бесплатно, собирается с 31 января 2026 года прекратить работу.

Поэтому пришлось перевести свои VDS-ки на связку Prometheus/Grafana. Рабочие конфиги для docker-compose:

services:
prometheus:
image: prom/prometheus:v2.54.1
container_name: prometheus
restart: unless-stopped
volumes:
- /<CHANGE_IT>/prometheus:/etc/prometheus
- /srv/prometheus:/prometheus
ports:
- "9090:9090"
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=1y'
- '--storage.tsdb.retention.size=20GB'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
- '--web.enable-lifecycle'
grafana:
image: grafana/grafana:11.2.2
container_name: grafana
restart: unless-stopped
ports:
- "3003:3000"
volumes:
- /srv/grafana:/var/lib/grafana
environment:
- GF_DEFAULT_INSTANCE_NAME=grafana
- GF_SERVER_DOMAIN=<YOUR_DOMAIN>
- GF_SERVER_ROOT_URL=https://<YOUR_DOMAIN_AND_POSSIBLE_PORT>
- GF_SERVER_ENABLE_GZIP=true
- GF_SMTP_ENABLED=true
- GF_SMTP_HOST=<CHANGE_IT>:<CHANGE_IT>
- GF_SMTP_USER=<CHANGE_IT>
- GF_SMTP_PASSWORD=<CHANGE_IT>
- GF_SMTP_FROM_ADDRESS=<CHANGE_IT>
- GF_SMTP_FROM_NAME=<CHANGE_IT>


Экспортеры использовал:

* https://github.com/prometheus/node_exporter - для хоста
* https://github.com/nginx/nginx-prometheus-exporter - для Nginx-а
* https://github.com/timonwong/uwsgi_exporter - для uWSGI

Ну и некоторые другие.
🔥2
Да, наконец-то добрался до тестирования способностей больших языковых моделей. Задал трем облачным моделям одно и то же задание (расписанное довольно подробно, получился очень длинный текст). Провел ревью — косяков масса. Модель поправила, создавая новые. Начал собирать (задание на C++ было) — фейлы. Очень много пришлось допиливать руками. Вывод: ИИ на данном этапе — продвинутые джуны, способные очень быстро исправлять косяки, очень быстро обучаться. Но написать полноценное законченное приложение сложности от средней они пока не в состоянии. Времени потратил изрядно. Думаю, сэкономил, но не критически много. Допускаю, что задание надо было написать еще более подробно. Но здесь уже вопрос: а где значительная экономия?
4
Оказывается каменты надо было включать для канала сразу. После включения у старых сообщений комментарии автоматически не появляются.
👍2
Что-то притомило проходить каждый раз процедуру создания нового проекта в Sublime, поэтому:
@echo off
setlocal enabledelayedexpansion
for %%F in ("%CD%") do set FOLDER_NAME=%%~nxF
set FILENAME=%FOLDER_NAME%.sublime-project
echo {> %FILENAME%
echo "folders": >> %FILENAME%
echo [>> %FILENAME%
echo {>> %FILENAME%
echo "path": ".">> %FILENAME%
echo }>> %FILENAME%
echo ]>> %FILENAME%
echo }>> %FILENAME%
start "" %FILENAME%

Или здесь
👍1🔥1
Как я чистил спам.

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

Первой мыслью было: ну есть же готовые модели ИИ, специально заточенные под это дело. Сразу предположил, что тип задачи из области NLP типа zero-shot классификации. Поискал мультиязычные решения с поддержкой кириллицы, выбрал mDeBERTa-v3-base-mnli-xnli. Провел массу экспериментов — пробовал различные комбинации меток, но точность была далека от приемлемой.

Пришлось избавиться от иллюзий, что модель всё сделает за тебя и предобработать данные. Были:

— удалены дубликаты из БД
— удалены все отзывы, где нет кириллицы
— удалены все отзывы, где содержатся HTTP ссылки

После такой чистки объем данных сразу сократился на 60%. Но точность DeBERT-ы всё-равно не устраивала.

Следующим способом стало применение локальных LLM (хотелось именно локального решения). Сначала выбрал qwen2.5:1.5b, но точность оказалась даже меньше, чем у DeBERT-ы, поэтому поменял на qwen3:30b и только здесь получился хоть какой-то вменяемый результат. Из 100 с небольшим отзывов, помеченных как спам, лишь порядка десятка оказались не спамом — разметил их вручную.

Попутно пришлось порешать проблемы пустых ответов и падении при долгой обработке. В первом случае оказалось, что модель рассуждает много, поэтому увеличил параметр num_predict до 2048. Для долгой обработки выставил таймаут в 240 и keep_alive в 10 минут.
🔥5
Это невозможно не упомянуть! Крайне интересная стата State of JS 2025.
2😁1
Python интерпретатор (пока минимальный, что бы это не занчило), написанный на Rust. Быстрый, говорят.

Репо
🔥2
Вот буквально вчера был на вебинаре Редсофта (не реклама) про оконные функции в SQL. По работе сталкивался с ними (оконными функциями, конечно) крайне редко. Вчера послушал про них, а сегодня они мне тут же и пригодились. Бывает же такое!

WITH ranked_data AS (
SELECT
s.id AS structure_id,
s.name AS structure_name,
to_char(m.measure_date, 'YYYY-MM') AS month_key,
m.measure_date AS sort_date,
p.name AS point_name,
p.z AS current_z,
ROW_NUMBER() OVER (
PARTITION BY s.id, to_char(m.measure_date, 'YYYY-MM'), p.name
ORDER BY m.measure_date DESC, m.created DESC
) AS rn_latest,
FIRST_VALUE(p.z) OVER (
PARTITION by p.name
ORDER BY m.measure_date, m.created DESC
) AS base_z
FROM geo_point p
JOIN geo_measure m ON p.measure_id = m.id
JOIN geo_structure s ON p.structure_id = s.id
WHERE s.status = 'actual' and m.status = 'actual'
)
SELECT
structure_name,
month_key,
sort_date,
point_name,
current_z,
(current_z - base_z) AS delta_z
FROM ranked_data
WHERE rn_latest = 1 and structure_name = '2135'
ORDER BY sort_date, structure_name, point_name
;


Такая красота получилась.
🔥2👍1
Оказывается MySQL довольно быстро сжирает место журналами транзакций, если приложения на Wordpress, подключенные к этому инстансу, довольно посещаемые, а места на диске ограничено.

Выставите binlog_expire_logs_seconds в настройках в нужную вам величину.
👍4
Утвержден C++26
👍2
This media is not supported in your browser
VIEW IN TELEGRAM
Сделал почти полностью готовую игру с механикой Blast в рамках тестового задания к одной из вакансий при поиске работы. Всё полностью моё, кроме картинок. Можно позалипать, как говорится, вилкам: pixxxel.github.io/blast/
🔥3
Forwarded from Банкста
Руководитель OpenAI (ChatGPT) Сэм Альтман заявил, что искусственный интеллект, скорее всего, не приведёт к массовому исчезновению рабочих мест, хотя ранее предупреждал об обратном и говорил, что он уничтожит целые профессии, заберёт 30–40% задач и спровоцирует кризис на рынке труда.

За последнюю неделю позиции ИИ пошатнулись. Владелец франшизы Pizza Hut подал иск на 100 млн долларов из-за того, что доставка с ИИ стала медленнее на 50% и обрушила продажи.

Uber начал сомневаться в окупаемости вложений в ИИ, Starbucks отказалась от нейросети для учёта товаров, а Microsoft стала реже использовать Claude среди сотрудников из-за высокой стоимости.

Ещё раньше Gartner прогнозировала, что к 2027 году закроется более 40% ИИ-проектов из-за больших расходов и сомнительной пользы для бизнеса. @banksta
👍2💯1