Сообщество - Лига программистов

Лига программистов

2 311 постов 11 973 подписчика

Популярные теги в сообществе:

6

Продолжение поста «Как написать DNS прокси для себя и настраивать через REST API»1

Серия Программирование

Новое

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

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

Теперь при запуске схема базы обновится автоматически и не потребуется прописывать `docker compose exec ...`

Самое крупное изменение - SOA записи. Теперь при создании доменной зоны можно ввести refresh, retry и expire параметры, а при запросе SOA будет возвращать соответствующий ответ. Т.е. вручную не нужно прописывать SOA записи в зоне, но нужно прописать `ns.<zone>.` записи для корректной работы (но об этом позже).

При удалении доменной зоны теперь не остается в базе "висячих" записей.

Еще обновил работу кэширования и теперь удаленные доменные зоны не висят в кэше.

Настроил CI/CD процессы и автоматизировал выгрузку релизов в DockerHob. А еще поправил запуск тестов в пайплайнах, т.к. они по факту вообще не запускались 😬

Запланировано

В первую очередь хочу добавить автоматическое создание ns. записей при создании зоны т.к. SOA ответы будут уже содержать такие данные (не очень хорошо, что их по факту нет).

Не все приложение сейчас покрыто логами а если и покрыто, то в разнобой (где-то через пакет log а в других местах используется slog).

К логам, я считаю, стоит добавить и возможность мониторить сервис, поэтому добавлю метод /metrics для возможности собрать метрики в Prometheus. Выделю отдельный мультиплексер для возможности разделения доменных имен метрик и REST, чтобы можно было не светить метриками во внешние сети.

Дополнительно

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

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

upd: ссылка на репо https://github.com/vesh95/dnso для тех кому интересно взглянуть

Показать полностью
3

Маленький веб

Серия Мы не пишем в техподдержку

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

Проект "Sandbird' в действии.

Проект "Sandbird' в действии.

Ода миниатюризации

Есть много причин, по которым миниатюрные девушк.. ээ реализации программ заслуживают внимания:

  • обучение — врядли получится разобраться как устроен вебсервер, перелопачивая исходники монстров вроде Apache или Nginx, спасет только миниатюрная реализация;

  • основа для собственных проектов — большой объем чужого исходного кода под капотом вашего проекта будет висеть гирей и отвлекать ресурсы на поддержку, в отличие от чего-то маленького и простого;

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

Разумеется есть определенные риски использования таких «наколенных» библиотек, связанные с неполной реализацией, безопасностью, работой под нагрузкой и так далее.

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

Еще с опытом приходит понимание, что любая программа — не более чем инструмент а реальную опасность всегда представляют живые люди, а не тупые машины.

Тестовое окружение

Не стал опять заморачиваться с *BSD, чтобы в третий раз не описывать специальную прослойку epoll-shim, позволяющую быстро и более-менее безболезненно портировать серверный софт с линукса. На этот раз в качестве тестового окружения выступает обычная Ubuntu Linux 25.10, хотя и с немного нестандартным ядром.

Компилятором выступит штатный же GCC, без изысков:

gcc version 15.2.0 (Ubuntu 15.2.0-4ubuntu4)

Sandbird

https://github.com/rxi/sandbird

Начнем погружение с весьма практичного проекта:

A tiny (~800sloc) embeddable HTTP server written in C89, compatible with Linux, OSX and Windows.

800 строк на чистом С, причем наиболее портабельного стандарта С89, c поддержкой Windows, Linux и MacOS — отличный набор для реального применения, например в качестве встроенного вебсервера для WiFi-роутера.

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

Вот так выглядит сборка примера:

cd example
gcc hello.c ../src/*.c -I../src -std=c89 -pedantic -Wall -Wextra -o hello

Так выглядит код тестового приложения с использованием этой библиотеки:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "sandbird.h"

static int event_handler(sb_Event *e) {
if (e->type == SB_EV_REQUEST) {
printf("%s - %s %s\n", e->address, e->method, e->path);
sb_send_status(e->stream, 200, "OK");
sb_send_header(e->stream, "Content-Type", "text/plain");
sb_writef(e->stream, "Hello world");
}
return SB_RES_OK;
}

int main(void) {
sb_Options opt;
sb_Server *server;

memset(&opt, 0, sizeof(opt));
opt.port = "8000";
opt.handler = event_handler;
server = sb_new_server(&opt);

if (!server) {
fprintf(stderr, "failed to initialize server\n");
exit(EXIT_FAILURE);
}
printf("Server running at http://localhost:%s\n", opt.port);

for (;;) {
sb_poll_server(server, 1000);
}
sb_close_server(server);
return EXIT_SUCCESS;
}

Важный нюанс:

сервер не использует многопоточность, все клиентские обработчики работают в одном потоке.

зато с неблокирующими сокетами:

..
static void set_socket_non_blocking(sb_Socket sockfd) {
#ifdef _WIN32
u_long mode = 1;
ioctlsocket(sockfd, FIONBIO, &mode);
#else
int flags = fcntl(sockfd, F_GETFL);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
#endif
}
..

webs

https://github.com/nicholascok/webs

Следующий интересный и даже в чем-то уникальный проект, с лаконичным описанием:

a simple websocket server library.

Реализует с помощью ~700 строк на С89.. серверные вебсокеты!

Вебсокеты это чаты, это «live-streaming», это например отображение логов в реальном времени и тому подобные динамические штуки.

Так это выглядит в действии:

Статику отдает пример из предыдущего проекта.

Статику отдает пример из предыдущего проекта.

Так выглядит сборка тестового приложения:

gcc -c *.c examples/test.c -Wall -Wextra -Wpedantic -Wno-overlength-strings -std=c89
gcc -o webs *.o -lpthread

Как нетрудно догадаться по -lpthread, тут уже используется многопоточность на базе POSIX threads:

..
static void* __webs_main(void* _srv) {
webs_server* srv = (webs_server*) _srv;
webs_client* user_ptr;
webs_client user;

for (;;) {
user.fd = __webs_accept_connection(srv->soc, &user);
user.srv = srv;
if (user.fd >= 0) {
user_ptr = __webs_add_client(srv, user);
pthread_create(&user_ptr->thread, 0, __webs_client_main,
user_ptr);
}
}
return NULL;
}
..

К сожалению этот интересный проект по большей части прототип, иллюстрирующий как можно на коленке без внешних библиотек реализовать серверную сторону вебсокетов на чистом С.

В случае реального использования, столь вольное использование malloc для обработки входящих пакетов быстро превратится в проблему:

..
/* deal with normal frames (non-fragmented) */
if (WEBSFR_GET_OPCODE(frm.info) != 0x0) {
/* read data */
if (data) free(data);
data = malloc(frm.length + 1);
..

Так выглядит сокращенная версия тестового сервера вебсокетов, c минимумом обработчиков:

#include "../webs.h"

int myFuncZ(webs_client* self) {
printf("server %ld: (id %ld) connected!\n",
self->srv->id, self->id);
webs_send(self, "greetings, salutations!");
return 0;
}
int myFunc2(webs_client* self) {
printf("server %ld: (id %ld) disconnected!\n",
self->srv->id, self->id);
return 0;
}
int main(void) {
webs_server* server1 = webs_start(7754);
if (!server1) {
printf("failed to initialise a server.\n");
return 1;
}
server1->events.on_open = myFuncZ;
server1->events.on_close = myFunc2;

webs_hold(server1);
webs_close(server1);
return 0;
}

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

Повторюсь, что весь этот проект, несмотря на всю свою интересность — сырой прототип и тащить в прод подобный код без переработки, "as-is" точно не стоит.

Собирается только для Linux и только с помощью gcc.

Прототип как он есть.

cpp-httplib

https://github.com/yhirose/cpp-httplib

Это уже куда более зрелая библиотека:

A C++ header-only HTTP/HTTPS server and client library

11к строк кода на C++11 и несколько зависимостей от внешних библиотек:

pthreads, brotli, OpenSSL, zlib

Реализуют весьма продвинутый HTTP/HTTPS сервер и клиент, причем фактически в одном файле. Есть поддержка сборки как на Linux так и Windows, причем для второй есть готовый проект для Visual Studio.

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

Так выглядит сборка тестового сервера, использующего эту библиотеку:

g++ -o server -O2 -std=c++11 -I.. -Wall -Wextra -pthread \
server.cc -DCPPHTTPLIB_OPENSSL_SUPPORT -lssl -lcrypto \
-DCPPHTTPLIB_ZLIB_SUPPORT -lz -DCPPHTTPLIB_BROTLI_SUPPORT \
-lbrotlicommon -lbrotlienc -lbrotlidec

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

Еще думаю, заблудиться в 11к строчках кода и одном единственном файле будет проблематично даже у нейросети не самых опытных «плюсолюбов».

Так выглядит серверный «Hello, world!»:

#include <httplib.h>
using namespace httplib;

int main(void) {
Server svr;

svr.Get("/hi", [](const Request & /*req*/, Response &res) {
res.set_content("Hello World!", "text/plain");
});

svr.listen("0.0.0.0", 8080);
}

Код в лучших традициях Django или Rails, тут есть маршрутизация запросов и привязка к URL — в данном случае обработчик связывается с урлом /hi.

Вот так выглядит обработка загрузки файлов с помощью формы и POST-запроса:

#include <fstream>
#include <httplib.h>
#include <iostream>
using namespace httplib;
using namespace std;

const char *html = R"(
<form id="formElem">
<input type="file" name="image_file" accept="image/*">
<input type="file" name="text_file" accept="text/*">
<input type="submit">
</form>
<script>
formElem.onsubmit = async (e) => {
e.preventDefault();
let res = await fetch('/post', {
method: 'POST',
body: new FormData(formElem)
});
console.log(await res.text());
};
</script>
)";

int main(void) {
Server svr;

svr.Get("/", [](const Request & /*req*/, Response &res) {
res.set_content(html, "text/html");
});

svr.Post("/post", [](const Request &req, Response &res) {
const auto &image_file = req.form.get_file("image_file");
const auto &text_file = req.form.get_file("text_file");

cout << "image file length: " << image_file.content.length() << endl
<< "image file name: " << image_file.filename << endl
<< "text file length: " << text_file.content.length() << endl
<< "text file name: " << text_file.filename << endl;
{
ofstream ofs(image_file.filename, ios::binary);
ofs << image_file.content;
}
{
ofstream ofs(text_file.filename);
ofs << text_file.content;
}

res.set_content("done", "text/plain");
});

svr.listen("localhost", 1234);
}

Так это выглядит в действии:

Разумеется подобный функционал — не откровение, особенно на 2026й год, но блин:

11к строк кода на всю красоту!

Теперь переходим к миниатюрным реализациям протокола FTP — для передачи файлов, если кто вдруг забыл.

tiny_ftpserver

https://github.com/adamwym/tiny_ftpserver

Вот такой «игрушечный» проект:

a tiny FTP server written in C++

Всего 1.5к строк на С++11 от безвестного китайского автора реализуют полнофункциональный FTP/FTPS‑сервер — с chroot, поддержкой локальных и анонимных юзеров и всем прочим.

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

В работе:

Проект использует известную библиотеку libnet, о чем автор забыл сообщить и которую необходимо установить до сборки:

apt install libnet1-dev

Сама сборка происходит с помощью обычного cmake:

mkdir build && cd build
cmake ..

Реализация FTP-сервера тут максимально классическая — используется chroot а использование 21 и 20 портов зашито в код:

..
int socketfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_port = htons(21);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
..

Все это означает, что запускать сервер придется от суперпользователя:

sudo ./tiny_ftpserver ../tiny_ftpserver.conf

У этого проекта есть один неожиданный нюанс:

на самом деле это.. студенческая работа.

И подобных проектов на Github оказалось великое множество. Так что далеко не везде в 2026м году забыли что такое высшее техническое образование, что не может не радовать.

fineFTP Server

https://github.com/eclipse-ecal/fineftp-server

Следующий интересный и весьма редкий проект:

FineFTP is a minimal FTP server library for Windows and Unix flavors.

~2k строк на С++14 реализуют FTP-сервер в виде.. библиотеки!

В этом и есть основная фишка этого проекта — возможность встроить FTP (причем сервер а не клиент) в ваше собственное приложение.

Так выглядит код примера, с внедрением FTP-сервера:

#include <fineftp/server.h>
#include <thread>

int main() {
// Create an FTP Server on port 2121. We use 2121 instead of the default port
// 21, as your application would need root privileges to open port 21.
fineftp::FtpServer ftp_server(2121);

// Add the well known anonymous user. Clients can log in using username
// "anonymous" or "ftp" with any password. The user will be able to access
// your C:\ drive and upload, download, create or delete files. On Linux just
// replace "C:\\" with any valid path. FineFTP is designed to be cross-platform.
ftp_server.addUserAnonymous("C:\\", fineftp::Permission::All);

// Start the FTP Server with a thread-pool size of 4.
ftp_server.start(4);

// Prevent the application from exiting immediately
for (;;) std::this_thread::sleep_for(std::chrono::milliseconds(100));
return 0;
}

Зачем и для чего такое может быть нужно — другой вопрос, все же обычно работу с файлами в конечном приложении (например обновление прошивки) стараются реализовать в виде клиента и через HTTP/HTTPS.

Но с точки зрения использования все отлично работает:

Это хорошая, взрослая библиотека, с несколькими коммитерами, с историей разработки, которая активно поддерживается и развивается.

Старого цирка с chroot и 21м портом тут нет, поэтому все работает без привилегий суперпользователя — на скриншоте выше как раз видно использование нестандартного порта для работы.

Поддерживается сборка для Linux, MacOS и Windows c приоритетом для последней.

Отличный проект, но далеко не последний в сегодняшней подборке.

rcpd

https://github.com/tenox7/rcpd

Этот проект — привет из далекого и славного прошлого:

This is a modern re-implementation of rcp (remote copy protocol) daemon, originally part berkeley r-commands.

Да, это самый настоящий сервер RCP, всего 300 строк на Golang от знаменитого в узких кругах компьютерных реконструкторов автора Tenox.

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

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

Возвращаясь к проекту:

rcpd от Tenox реализует серверную сторону — сервер RCP

К этому серверу подключаются клиенты для загрузки или скачивания файлов. Так выглядит процесс копирования файла, в качестве клиента тут rcp из пакета GNU Inetutils:

Как видите и клиент и сервер запускаются от суперпользователя — такие были времена, RCP использует 514 порт, который нельзя занять без привилегий.

Сборка:

go build -o rcpd .

Кстати в Makefile проекта есть поддержка кроссплатформенных сборок, с весьма богатым выбором:

Стоит добавить, что протокол RCP сам по себе очень простой и не менялся весь период своего существования, что позволяет подключаться к этому RCP-серверу 21 века даже с помощью клиентских программ из 80х и 90х.

За кадром

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

url.c

https://github.com/cozis/url.c/

Парсер строк URL на чистом С и без зависимостей:

This is a small library to parse and manipulate URLs in conformance to RFC 3986 and (most of) the WHATWG specification.

Что умеет:

  • No allocations

  • No dependencies

  • The ability to switch between RFC 3986 and WHATWG with a flag

  • Relative reference parsing and resolution

  • URL normalization

  • Doesn't rely on null-terminated strings

LightFTP

https://github.com/hfiref0x/LightFTP

Еще один интересный проект миниатюрного FTP-сервера:

Small x86-32/x64 FTP Server

Поддерживается сборка под Linux/Windows/Mac, сам проект не мертвый и судя по коммитам — развивается.

uftpserver

https://github.com/cpopp/MicroFTPServer

Весьма специфичная реализация FTP-сервера для встраиваемых систем:

Minimal FTP Server that can run on an ESP8266 with MicroPython

Так я впервые узнал о существовании «микропетона» — давно существующего проекта, с огромным количеством поддерживаемого железа и широким функционалом.

Ну и в качестве финального аккорда:

https://github.com/mtheall/ftpd

FTP Server for 3DS/Switch/Linux.

Реализация FTP-сервера для.. Nintendo Switch!

Это такая игровая консоль для гиков если кто вдруг не в курсе:

P.S.

Статья была опубликована на Хабре, оригинал как обычно в нашем блоге.

Показать полностью 7
2

Ответ ARMS.Studio5 в «"Это задача на пять минут". Самая дорогая фраза в разработке»2

Спасибо за развернутый комментарий.

Кажется, вы как раз подтвердили главную мысль статьи.

В продуктовой разработке практически не бывает задач уровня «сделай кнопку».

Обычно задача звучит примерно так:

«Добавить элемент управления для скачивания отчёта в зависимости от прав пользователя, с аудитом действий, поддержкой мобильной версии, локализацией и существующей системой ролей.»

И вот в такой постановке сама кнопка действительно занимает минут пять. Всё остальное — анализ, интеграция, тестирование, ревью, проверка прав, регрессия и т.д. — занимает часы, а иногда и дни.

Что касается опыта — программирую я достаточно давно. Когда я писал на ассемблере для процессора ВМ80, родители некоторых нынешних комментаторов ещё даже не были знакомы. 🙂

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

4

Ответ на пост «"Это задача на пять минут". Самая дорогая фраза в разработке»2

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

Что вижу я, даже при ооочень малом опыте разработки:
- Криворукий погромист написал сайтик или приложуху, жестко привязав КАЖДЫЙ объект на КАЖДОЙ странице, к самой странице. Вместо того чтоб пользовать единую таблицу стилей, криворук этим самые кнопоцки-окошечки, создавал собственные, на каждую страницу, в итоге у криворука поплыло все и сразу, потому что - а че б нет? )

Нормальный погромист создаст таблицу стилей, в которой пропишет и кнопоцки, и окошечки, и шапочки, и всякие другие прелести, и будет её вызывать, наслаждаясь тишиной и покоем, а тчоб верстка не плыла, сделает окошечки резиновые, и теперь для того чтоб добавить кнопоцку "скачать" у него уйдет всего 10 минут, из которых он 8 будет читать КОММЕНТАРИИ - чё кудой тыкать, и 2 - клацнуть в нужном месте 10 строк.
Ну да, приврал, там надо ещё и добавить всякие всякости, чтоб оно работало, и скачивало,так что нет - не 10, а поболе, но тут не скажу сколько и чего, ибо нет четкого ТЗ).

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

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

Показать полностью
2

«Это задача на пять минут». Самая дорогая фраза в разработке2

Серия Аналитика в разработке

Наверное, каждый разработчик хотя бы раз слышал эту фразу.

— Да там же небольшое изменение.
— Это буквально на пять минут.
— Просто кнопку добавить.

И почти всегда после этих слов начинается история, которая заканчивается совсем не через пять минут.

"Всего лишь кнопку"

Однажды приходит задача.

Нужно добавить кнопку "Скачать".

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

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

Исправили.

Теперь оказывается, что скачивать нечего — API не отдаёт нужный файл. Добавили новый метод.

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

После этого QA находит проблему. Если пользователь открывает страницу из закладок, кнопка исчезает.

Исправили.

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

И конечно же кнопка появилась, но прошёл ДЕНЬ, хотя сама кнопка действительно писалась минут пять.

Код редко занимает большую часть времени

Есть интересное наблюдение.

Когда люди говорят:

"Сделать задачу"

Они представляют именно написание кода. Хотя на практике код — это зачастую лишь небольшая часть всей работы.

Остальное время уходит на:

  • понимание задачи;

  • поиск нужного места в проекте;

  • анализ существующей логики;

  • обсуждения;

  • изменения API;

  • тестирование;

  • исправление найденных ошибок;

  • код-ревью;

  • деплой;

  • проверку в продакшене.

Иногда написание самого решения занимает всего 10–20% времени.

Чем старше проект, тем меньше в нём "пятиминутных" задач

На новом проекте действительно можно быстро что-то добавить.

Но когда системе несколько лет...

...любое изменение начинает цеплять другие части.

Добавили одно поле и сломались отчёты.

Исправили отчёты и перестали проходить интеграционные тесты.

Исправили тесты.

Поменялась сериализация и упал мобильный клиент.

Каждый опытный разработчик знает эффект:

маленьких изменений почти не бывает.

Почему разработчики так не любят фразу "на пять минут"

Не потому что ленятся.

А потому что знают, что оценивается только видимая часть задачи.

Никто заранее не видит:

  • старые зависимости;

  • забытый код десятилетней давности;

  • неожиданное поведение;

  • сторонние сервисы;

  • особенности браузеров;

  • ограничения архитектуры.

Это как попросить строителя:

"Да просто стену передвинь."

Пока не начнёшь двигать — не узнаешь, что она несущая.


Как лучше ставить задачи

Вместо:

Это на пять минут.

Лучше сказать:

Посмотри, пожалуйста, насколько это действительно сложно.

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

Разработчик перестаёт оправдываться и начинает искать решение.


Самое интересное

За годы работы я заметил одну закономерность.

Когда руководитель начинает использовать выражение:

"Ну это же быстро..."

почти всегда именно эта задача неожиданно становится самой дорогой в спринте.

И не потому что кто-то плохо работает. А потому что сложность разработки редко находится в самом коде.

Она скрывается вокруг него.


Код пишется быстро. Долго приходится разбираться со всем, что этот код затрагивает.

Если вам интересны темы управления разработкой, предсказуемости спринтов и инженерных процессов — иногда пишу об этом здесь. Ещё больше материалов собираю на sprint-intelligence.ru

Показать полностью 2
4

Нейросети никогда не заменят программистов

В контексте замены программистов нейросетями говорилось разное. Сначала была популярна версия, что нейросети будут делать то же самое, что сейчас делают живые программисты, но быстрее и дешевле. Что-то типо «нейросети уже заменили джунов, через год та же участь ждёт мидлов». Про то, что джунов «заменили» нейросети, кстати, не правда от слова совсем. Я сам искал свою первую работу как раз в период, когда о нейросетях ещё мало кто знал (за несколько лет до того, как начал набирать популярность chatGPT) и уже тогда от джунов требовались достаточно обширные знания и навыки (может не во всех сферах, но в том направлении, куда я стремился, точно) Я слышал, что мол был период, когда достаточно было знать лишь основы, и брали чуть ли ни всех подряд, но в «этот поезд я не успел».

Вторая версия «замены программистов», что профильные специалисты, без знания программирования (или с минимальными знаниями), просто будут объяснять нейросети, что нужно сделать и те будут писать им готовые программы.

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

Начну немного из далека, но это важно:

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

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

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

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

При чём же здесь программирование?

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

Нейросеть может «заменить программиста» в задачах, которые раньше решались с помощью «ctrl C -> ctrl V» с небольшой доработкой. По сути, нейросеть может «загуглить за вас»(что безусловно важно и сильно ускоряет процесс, уменьшая количество рутины, но всё же на "заменит" это не особо похоже). Но что-то у меня есть сомнения, что программисты зарабатывали сильно выше среднего просто из-за навыка рутинного гугления. Опять же любой «интеллектуальный труд» если он по-настоящему интеллектуальный – это поиск нужной информации, её анализ, принятие решение о том, как решить задачу оптимально. И на каждом этапе огромную роль играет опыт и «здравый смысл».

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

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

Показать полностью
189

Как сделать игру для ZX Spectrum в 2026 (и зачем)

Загрузочный экран игры

Загрузочный экран игры

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

А потом как понял…И сделал игру для дочки. Она несколько раз играла в Lode Runner в эмуляторе, и попросила меня сделать что‑нибудь для нее.


Написано специально для ▶️Timeweb Cloud и читателей Pikabu.


С 1987 года у меня перебывало много компьютеров: «Сура», «БК», «Спектрум», какие‑то древние приставки «Атари» и что‑то еще. ZX Spectrum мне нравился больше всего. Много игр, отличная графика, звук. Даже на бипере!

Его собрал для меня отец, когда мне было лет 11–12. Комплектация была суперской: TR‑DOS, ч/б монитор Корвет, корпус от «Суры» с прекрасной герконовой клавиатурой (это вам не «дубовая» «Дубна»!), два дисковода (тоже от «Суры», емкость дисков 640 КБ, насколько помню).

Естественно, я очень хотел делать игры, но литературу, кроме сборников «500 игр» и книжек по Бейсику, купить или достать было негде. Готовые движки и всякие Wham The Music Box и Laser Basic были уже тогда, но не было знаний, как с ними дружить.

Поэтому, кроме бейсиковских игроподелок с псевдо‑ или «проволочной» графикой, я ничего не успел сделать. Лет в 14 лет мой «Спектрум» сгорел, и мне пришлось пересаживаться на 286 с MS DOS. Я не хотел, честно.

Лет в 15 писал для PC игрушки на Microsoft Quick Basic (порт Defender of the Crown — я тогда не знал, что эта игра написана для персоналок еще в 80-е). У него были классные библиотеки для работы с графикой. Для спрайтов я использовал коды ассемблера. Позже на С++ создал что‑то типа Tapper и прочее очень простое.

Но все же игра появилась 30 лет спустя после того, как я что‑то делал на живом «Спектруме».

Пару недель я изучал движки, редакторы спрайтов и графики, музыкальные программы. О них есть на Хабре статья Михаила Судакова @mikhailsudakov (Яндекс.Музей).

Из всего, что попробовал, выбрал движок Dizzy SE Валерия Сурженко, графический редактор ZX‑Paintbrush и музыкальный редактор Vortex Tracker II Сергея Бульбы. На сайте автора последняя игра датируется 2020 годом.

Авторы DizzyAge (редактор карт в Dizzy SE): Alexandru Simion и Cristina Simion.

Авторы оригинальных игр и владельцы прав на вселенную Диззи: Oliver Twins и Codemasters.

Скриншот сайта <a href="https://dizzy-se.ru/" target="_blank" rel="nofollow noopener">dizzy&#x2011;se.ru</a>

Скриншот сайта dizzy‑se.ru

Мне удалось связаться с автором. Прилагаю его историю целиком.

Обо мне.
Сурженко Валерий Витальевич. В спектрумовских кругах известен как Hippiman. Живу в южном городе Ростове‑на‑Дону. Сейчас работаю Java‑программистом, занимаюсь разработкой высоконагруженных распределённых бэкенд‑приложений.
Spectrum‑ом и программированием вообще заболел ещё в детстве, когда родители подарили мне на день рождения отечественный 48-килобайтный клон. Какой модели он был, к сожалению, не знаю, так как был он самостоятельно собран одним из знакомых отца.

Клон этот до наших дней не дожил, зато рабочей машинкой у меня сейчас выступает ZX Evolution rev.b. Зверь‑машина, для Baseconf которой мной было разработано несколько игр.

О Dizzy SE
Что вообще такое Dizzy? Это неофициальный маскот ZX Spectrum в странах СНГ. Об огромной популярности этого персонажа можно судить по одному только количеству фанатских игр, выпущенных в 90-е — 2000-е. Было даже несколько попыток сделать редакторы для Dizzy‑игр.


Чтобы ответить на вопрос, зачем был сделан Dizzy SE, нужно копнуть немного в историю и в устройство игровых движков.

Разработка каждой новой игры про Dizzy под ZX Spectrum всегда таила в себе целый ряд сложностей. Исторически движки Dizzy оперировали в качестве игровых объектов таким понятием, как «спрайты». Это не то же самое, что спрайты в современном понимании этого слова. Это понятие ближе к современному термину «префаб». В смысле, это заранее описанный объект со всеми характеристиками и единожды заданной графикой.
Этот подход хорош большой экономией памяти, ведь на карте располагаются просто координаты готовых объектов плюс пара параметров, вроде отзеркаливания. И плох тем, что нужно ещё до начала разработки игры чётко знать, какие объекты тебе понадобятся. Это всё‑таки старый 8-битный компьютер, а не современный движок.

Другая сложность — это логика. Всё взаимодействие с объектами, будь то диалог с Дензилом или набор воды в ведро, писалось на ассемблере точно так же, как и весь остальной код. Это позволяло вытворять с игрой что угодно — вплоть до переворачивания мира кверху ногами, как в Dizzy 3, но ассемблерный код есть ассемблерный код.
Ну и третья сложность, самая главная: вся игра от и до писалась на ассемблере, а это влекло за собой обязательное глубокое понимание внутреннего устройства машины, умение работать с памятью, экраном и так далее. Там очень много нюансов. Соответственно, абы кто просто так не мог взять и написать свою игру про Dizzy.
С другой стороны, есть открытый движок DizzyAge для создания игр под Windows. Он использует совершенно другой подход — современный. Вся игра разделяется на две части. Движок — единожды скомпилированный, который нельзя и не нужно изменять, и который берёт на себя всю низкоуровневую работу. И саму игру — графику, карты, скрипты и прочее.
Для построения игрового мира DizzyAge использует систему «брашей». Это некая область тайловой карты с уникальными параметрами, то есть тут нет заранее подготовленных объектов. Если нам нужно использовать для какой‑то области карты половинку камня, а не целый, то мы создаём браш из половинки камня. Либо дублировать одно изображение множество раз.

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

Dizzy SE — это перенос идеологии DizzyAge с IBM PC на ZX Spectrum со всеми вытекающими: подход к построению мира на основе брашей, логика на основе скриптов и полная изоляция от аппаратной начинки компьютера ценой потери некоторой гибкости и быстродействия.

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

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

Кстати, на движке можно делать не только игры про Диззи, но и, например, гонки или все, на что способен скриптовый язык.

Источник: интервью с Валерием Сурженко на idpixel.ru

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

Почему движок и именно этот, а не с нуля:

  • Я вполне понимал на старте объем работы — музыка, загрузочные экраны, спрайты, программная часть. Начал изучать инструменты в январе с большими перерывами, а закончил игру только во второй половине июля.

  • Создать что‑то играбельное на Си или ассемблере с нуля — я бы и за год не справился. Я преклоняюсь перед ребятами, которые это делают. Можно посмотреть, например, работы с конкурса ретро‑игр YRGB

  • У «Спектрума», в отличие от того же Commodore, не было встроенных спрайтов, и уровень программирования для него должен быть действительно высоким. Даже с учетом того, что в Dizzy SE скриптовый язык, для меня это тоже программирование (последние лет 15 я ковыряю только чужие PHP‑коды и это не основная специальность).

  • Мне хотелось сделать несложную игру, которую можно пройти за несколько минут.

  • Dizzy — беспроигрышный вариант. Здесь и подумать, и попрыгать и в диалогах поучаствовать можно.

Я определил такие критерии:

  • Всего один экран.

  • Максимум характерных черт из игр про Диззи.

  • Красивая графика и анимация.

  • Понятный порядок действий без инструкций.

В итоге появилась Snow Trap Dizzy: нужно помочь дедушке Grand Dizzy расчистить огромный сугроб, который завалил входную дверь.

Героев проще представить картинкой (я намеренно взял изображения героев из первых игр):

Экран с меню

Экран с меню

❯ Загрузочный экран

Шаблонный экран движка такой:

Скриншот из шаблонов игры

Скриншот из шаблонов игры

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

Первые потуги

Первые потуги

Скриншот программы графического редактора:

ZX&#x2011;Paintbrush

ZX‑Paintbrush

В итоге в игру вошли не только члены семьи Диззи, а еще летучая мышь и белка.

Так бы заставка выглядела на «настоящем Спектруме»

Так бы заставка выглядела на «настоящем Спектруме»

Я знаю, что белка зимой меняет цвет, но сделать хотя бы серую белку на белом фоне не получится. Думаю, многим читателям известна основная проблема графики в «Спектруме»: 2 цвета на знакоместо 8×8 пикселей при разрешении экрана 256×192 и атрибуты яркости и мигания.

❯ Экран меню

Шаблонный экран с меню такой:

Скриншот из шаблонов игры

Скриншот из шаблонов игры

Я решил переделать его и показать сочетания клавиш, так как непонятно, что такое «классика», и какой синклер — I или II. В итоговый экран меню добавил имя дочки, чтобы ей было приятно.

For Sonya

For Sonya

❯ Тайлы

Исходный файл с тайлами выглядит примерно так (на момент написания статьи я успел его испортить немножко):

Скриншот из шаблонов игры

Скриншот из шаблонов игры

А вот то, что получилось у меня:

Я оставил элементы для камина — камни (перекрасил в серый цвет), огонь, плюс дверь, стену, яйцо‑жизнь.

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

Нарисовал анимацию ночного неба, два тайла для кучи снега и пару вариантов анимации белки с желудями.

Два варианта белки

Два варианта белки

Анимация роста и вращения желудей, которая в итоге не сработала

Анимация роста и вращения желудей, которая в итоге не сработала

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

Самым непростым оказалась анимация дедушки Диззи, которого я решил посадить в кресло‑качалку.

Кстати, есть несколько особенностей работы с тайлами, кроме анимации. Файл с тайлами сделан в формате BMP. Если попробовать работать с ним в ZX‑Paintbrush, слетает таблица цветов, и вы получаете что‑то такое (желтая белка с синим носом):

Потихоньку разбираемся с движком

Потихоньку разбираемся с движком

Поэтому я промучался день, а потом перерисовал исходные тайтлы в Photoshop. Пользоваться им для тайлов и спрайтов в разы удобнее, чем ZX‑Paintbrush.

❯ Спрайты

Здесь есть жесткое ограничение: размеры и последовательность спрайтов изменить нельзя. Цветной фон — прозрачный.

Скриншот из руководства движка

Скриншот из руководства движка

Для плавной анимации летучей мыши я использовал спрайты паука, а вместо капли нарисовал домового Доззи. Он ходит и вздыхает, так как в доме непорядок.

Мои спрайты

Мои спрайты

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

Скриншот из шаблонов игры

Скриншот из шаблонов игры

❯ Карта

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

Скриншот из раздела руководства динамических объектов

Скриншот из раздела руководства динамических объектов

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

Последний вариант (цвет ключа другой, хочется использовать всю палитру):

Самое неприятное, что в случае любой ошибки игра виснет.

Так:

Или даже так:

Примерно такое я увидел, когда сгорел мой комп

Примерно такое я увидел, когда сгорел мой комп

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

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

На этом скриншоте показаны свойства объекта «дупло». Это динамический объект с id=200, материал «воздух» (прозрачный) и свойство — предмет.

В других вкладках находятся технические параметры (в том числе неработающий признак слоя):

Пользовательские данные (номер диалога или скрипт действия):

В данном случае при подборе «дупла» игроком показывается диалог 21:

❯ Скрипты и диалоги

Наверное, это самая долгая часть процесса. Описание движка несколько куцевато, несмотря на примеры. Поэтому до многих мелочей (например, я путал поначалу перерисовку спрайтов и перерисовку объектов) приходилось додумываться самому.

Скриншот интерфейса движка (журнал сборки проекта, редактор диалогов и редактор скриптов):

Всего у меня получилось 23 диалога и 9 скриптов.

Скрипт инициализации игры создается автоматически — в нем я дописал обработчики событий.

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

Дописал скрипт перемещения в начальную точку:

На этом скриншоте показан скрипт проверки окончания игры:

Рассмотрим его подробнее. В нем мы проверяем первое ли это касание, и если да, выводим диалог и отключаем дедушку, чтобы диалог не повторялся.

// ========================================================

// СКРИПТ ПОКАЗА СООБЩЕНИЯ ПРИ ПОПЫТКЕ ДИАЛОГА С ДЕДУШКОЙ

// ========================================================

// Проверяем, был уже диалог или нет

objget(this, O_STATUS) // Забираем статус объекта "Дедушка"

eq(ret, 1, already_activated) // Если диалог был, переходим на метку already_activated

// Первый диалог

redraw_sprites() // В движке спрайты во время диалогов не перерисовываются, видимо, для экономии ресурсов Спекки.

showmessage(2, 8, 14, CL_YELLOW, CL_WHITE, 1) // Показываем сообщение "Дедушка спит, не буди его, пока все дела не переделаешь"

objset(this, O_STATUS, 1) // Отключаем дедушку

set(ret, NO_INVENTORY) // Инвентарь не показываем

redraw_screen() // Перерисовываем экран

goto(exit) // Выход

// ==============================================================

// ПРОВЕРКА УСЛОВИЙ ОКОНЧАНИЯ ИГРЫ - ДВЕРЬ ОТКРЫТА, СНЕГ ПОЧИЩЕН

// ==============================================================

already_activated:

set(j, ret)

// Проверяем дверь

objroomfind(5, 0, 0) // id двери = 5

set(a, ret) // Записываем статус в переменную

objget(a, O_STATUS) // Проверяем статус

ne(ret, 1, door_not_open) // Если дверь не открыта, выходим

// Проверяем снег

objroomfind(99, 0, 0) // id снега = 99

set(a, ret) // Записываем статус в переменную

objget(a, O_STATUS) // Проверяем статус

ne(ret, 1, snow_not_cleared) // Если снего не расчищен, выходим

// Если все условия выполнены

// Дедушку перерисовываем без кресла-качалки

// Спрайты можно позиционировать по пикселям, а вот тайлы - кратно 8

objset(this, O_MAP_X, 0) // 0

objset(this, O_MAP_Y, 29) // 232:8

// Перемещаем Дейзи в дом

objroomfind(150, 0, 0) // ID=150 в комнате (0,0)

set(d, ret)

// Меняем координаты (делим на 8! пиксели)

objset(d, O_X, 18) // Новая X позиция

objset(d, O_Y, 13) // Новая Y позиция

redraw_object(d) // Перерисовываем Дейзи

redraw_screen() // Перерисовываем экран

// Получаем текущее количество монет

gameget(PL_COINS)

set(a, ret) // Сохраняем в переменную A

// Проверяем

eq(a, 0, show_coin_not_found) // 0 монет

gt(a, 0, coin_found) // Больше 0 монет

// Монета не найдена

goto(show_coin_not_found) // Показываем диалог, когда игра завершена, но монета не найдена

coin_found:

redraw_sprites()

showmessage(1, 8, 22, CL_GREEN, CL_WHITE, 1) // "Победа и монета найдена!"

goto(after_coin)

show_coin_not_found:

redraw_sprites()

showmessage(1, 8, 23, CL_YELLOW, CL_WHITE, 1) // "Победа, но монета не найдена"

after_coin:

redraw_screen()

redraw_sprites()

// Задержка 10 секунд

//wait(500)

// Возврат в меню

gameset(PL_GAME_OVER, true)

redraw_screen()

set(ret, NO_INVENTORY)

goto(exit)

// На всякий случай оставлю под ошибки.

door_not_open:

//showmessage(2, 8, 8, CL_YELLOW, CL_WHITE, 1)

goto(exit)

snow_not_cleared:

//showmessage(2, 8, 2, CL_YELLOW, CL_WHITE, 1)

goto(exit)

door_not_found:

showmessage(2, 8, 0, CL_YELLOW, CL_WHITE, 1)

goto(exit)

snow_not_found:

showmessage(2, 8, 1, CL_YELLOW, CL_WHITE, 1)

goto(exit)

coin_not_found:

goto(exit)

Тайлы для дедушки Диззи

Тайлы для дедушки Диззи

❯ Музыка

Про работу с музыкой нужна отдельная статья с обзорами редакторов. Не буду раздувать этот пост и рекомендую почитать Александра Семенова @shiru8bit про ретро‑музыку.

Я сделал простенькую мелодию в размере 5/4 на своей блокфлейте, после чего добавил бас и стандартную «бренчалку» в Vortex Tracker II. Пришлось немного помучаться с орнаментами. В итоге получилось трехголосое фолк‑вступление для игры.

Фото моего блочка Kung

Фото моего блочка Kung

Задержусь только на одном моменте. Я потратил пару дней на изучение того, как работать с музыкой. У процессора AY-3-89XX есть возможность «скользящих» нот. Чем я и воспользовался в самом начале мелодии.

На скриншоте показа команда, которая реализует скольжение от ноты «ре» шестой октавы до «ля» пятой.

Скриншот программы Vortex Tracker II

Скриншот программы Vortex Tracker II

❯ Результат

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

Игра немного тормозит из‑за большого количества анимации. Но уменьшать число движущихся объектов мне не хочется. Тем более, всего один экран.

Теперь я понимаю, почему над играми работало минимум 3 человека. Один отвечал за графику, другой за музыку, третий за код. А что творили мастера, которые делали музыку под бипер...Это космос.

Игру можно пройти за минуту. Хотя я и показал вам, где монетка, попробуйте догадаться, какая роль у домового?

Рекомендую эмулятор Unreal Speccy. А файл с игрой лежит здесь.


Больше интересных статей и новостей в нашем блоге на Хабре и телеграм-канале.

Реклама. ООО «ТАЙМВЭБ.КЛАУД», ИНН: 7810945525
Показать полностью 37 1

Год пилю свой мессенджер в одиночку: хейт на DTF, 0 пользователей и почему я всё равно делаю обновление

Всем привет!

Мне хотелось создать что-то своё, и последние 12 месяцев я в соло разрабатываю мессенджер SendCore.

Основная идея проекта простая: сделать удобный, лёгкий чат, где нет душной цензуры и правил и где всё работает максимально прозрачно. А чтобы в приложении не было скучно, я встроил туда умных ИИ ботов (KimidAI например) — с ними можно пообщаться на любые темы, задать вопрос или использовать их прямо в группах (да, можно).

С чем я столкнулся за этот год:

Попытки рассказать о проекте на других площадках закончились... мягко говоря, провалом. Меня заминусили, обвинили в «рекламе вирусов» и закидали комментариями в стиле «Зачем это нужно, если есть Телеграм и Дискорд?». В итоге пользователей сейчас ровно 0, а приложение стоит пустое.

Но сдаваться не хочется, поэтому сейчас я готовлю большое обновление UI, полностью переработал дизайн и раздел поиска, добавил кастомизацию тем чата, допилил работу ИИ помощника

Зачем я это пишу?

Мне очень нужны живые тестеры и честный взгляд со стороны. Если вам интересно понажимать кнопки, потестить ИИ бота, пофлудить в чатах или просто дать жесткую критику по дизайну или идее — буду искренне рад каждому!

Зачем я это пишу?

Мне очень нужны живые тестеры и честный взгляд со стороны. Если вам интересно понажимать кнопки, потестить ИИ-бота, пофлудить в общем чате или просто дать жесткую критику по дизайну/идее — буду искренне рад каждому! :))

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

Показать полностью 2
Отличная работа, все прочитано!

Темы

Политика

Теги

Популярные авторы

Сообщества

18+

Теги

Популярные авторы

Сообщества

Игры

Теги

Популярные авторы

Сообщества

Юмор

Теги

Популярные авторы

Сообщества

Отношения

Теги

Популярные авторы

Сообщества

Здоровье

Теги

Популярные авторы

Сообщества

Путешествия

Теги

Популярные авторы

Сообщества

Спорт

Теги

Популярные авторы

Сообщества

Хобби

Теги

Популярные авторы

Сообщества

Сервис

Теги

Популярные авторы

Сообщества

Природа

Теги

Популярные авторы

Сообщества

Бизнес

Теги

Популярные авторы

Сообщества

Транспорт

Теги

Популярные авторы

Сообщества

Общение

Теги

Популярные авторы

Сообщества

Юриспруденция

Теги

Популярные авторы

Сообщества

Наука

Теги

Популярные авторы

Сообщества

IT

Теги

Популярные авторы

Сообщества

Животные

Теги

Популярные авторы

Сообщества

Кино и сериалы

Теги

Популярные авторы

Сообщества

Экономика

Теги

Популярные авторы

Сообщества

Кулинария

Теги

Популярные авторы

Сообщества

История

Теги

Популярные авторы

Сообщества

Недвижимость и ремонт

Теги

Популярные авторы

Сообщества