𝐑𝐨𝐨𝐭𝐓𝐨𝐨𝐥 𝐁𝐥𝐨𝐠
194 subscribers
570 photos
48 videos
5 files
60 links
ニャン
Download Telegram
С НОВЫМ ГОДОМ!

Я хочу от всей души поблагодарить всех вас, кого встретил, и кто остался со мной
Хочется вот просто обнять всех! И сказать вот что:

Атом - Бро, мы с тобой сколько уже прошли? Брат за брата как 2 солдата - и в снег, и в град, и в Демо, и в Релиз

Денчик - тот самый, мой любимый 🤪 😋. Люблю, уважаю, и просто рад что встретил тебя

мдпка - жжешь! Просто в соло поднялся и в музыке, и в C#. А вообще - ты красавчик, растешь на глазах, и мотивируешь меня

Оранж - Ну просто наш крепкий политик и специалист в монолитности кода)) Рад что встретил тебя, дружище!

Матвейка - Любимый кот Шредингера, хотя стоит тебе просто определится с языком программирования и все пойдет в гору

Котомаг - И ссоримся, и миримся, и скидываем друг-другу ДЗ по Алгебре и арты по ZZZ - Все стабильно, а стабильность - залог успеха

И одна милая девушка - Что нашла меня под мое грустное настроение перед моим ДР, и теперь которую я рад менторить по С++. Я рад что встретил тебя


Еще раз обнимаю всех вас, ребятки ;)

if(std::time(nullptr) > 1767214800)
std::cout << "New Year 2026!";
❤4❤‍🔥1💯1🎄1
This media is not supported in your browser
VIEW IN TELEGRAM
Решил тоже поднять IQ и начал изучать сетевой код на UE4 (💀)

Эта всякая синхронизация, RPC, владения событий, кто прав а кто виноват - бррр, что за жесть

Я же сам не любитель асинхронности, а тут такое...

Будто пытаешься писать на JavaScript не имея доступа к гайдам - Все такое расплывчитое, не понятное. Но главное что все всё знают и кивают головой, ага
❤6😁1
а еще ловит даже на парковке
❤4🤣3🤩1👀1
А теперь к действительно актуальным вопросам

https://habr.com/ru/articles/983596/
❤4👀2
This media is not supported in your browser
VIEW IN TELEGRAM
А знаете.. в моменте у меня просто щелкнуло и я понял как работает мультиплеер в Анриле - и на деле это довольно таки интересный подход!
❤4❤‍🔥1🔥1
В чем суть:
В концепции Анрила хост/сервер - Держит весь мир и является источником правды. Клиенты же - синхронизируются с сервером. Это называется репликация

Да, клиенты могут отправлять ему команды, изменять данные, но последнее слово - за сервером

И в С++ (да и в блюпринтах) для UPROPERTY() существует такой параметр как Replicated который говорит, что переменная - реплецируемая, то есть, ее итоговое состояние будет жестко синхронизировано с сервером

И помните мой первый пост про мультиплеер, где был диссинхрон игрока и все жестко лагало? Это был результат моего "одиночного" мышления перед мультиплеером

Я тогда только изменял локальные данные игрока, не понимая, что изменения локальных реплецируемых данных - плохой ход, ведь Анрил все равно вернет их значения так, как это является на сервере (ведь он источник истины а все остальные бесы хвхвхв)

Так вот что я понял - работа мультиплеера проста до безумия если быть в потоке с Анрилом!

Когда клиент, например, хочет присесть - выполянется локальное действие Crouch (bIsCrouch = true) И происходит отправка команды серверу, который у себя обновляет состояние переменной bIsCrouch для нашего игрока

И в чем соль - если не просить сервер обновить переменную, то на сервере bIsCrouch так и останется false, затем в новом пакете данных (между сервером и клиентом) будет диссинхрон нашего локального bIsCrouch = true и пакетного bIsCrouch = false, а так как макрос UPROPERTY(Replicated) обязует Анрил синхронизировать переменные, то произойдет неприятный пролаг, рассинхронизация и пошло поехало, покатилось побежало...

А благодаря тому способу, теперь мы сами по себе (локально) выставляем bIsCrouch = true и просим сервер обновить его bIsCrouch на true. А потом в следующем пакете данных приходит новый bIsCrouch = true, который и так равный локальному bIsCrouch и все будет в потоке - Сервер будет хранить истину для других клиентов, и мы получаем бесплатный и быстрый отклик

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

Как-то так

Thinking in Four Dimensions. Think Cold.
❤3👍3❤‍🔥1🔥1🤯1
А часики то тикают...
😁3💯1😭1
Forwarded from Shoko - UI Framework (Root Tool)
Черт возьми, оно работает...
знали бы вы мое волнение сейчас

Когда я только начинал проектировать Shoko, то много внимания уделял про архитектуру и кроссплатформенность до уровня Embedded

И знаете? Оно работает!

Слева - Raspberry Pi 3B (1GB RAM) + LCD Shield (Linux)
Справа - Мой ПК (Windows 11)

И абсолютно не меняя код (только конфиг), я могу переключаться между платформами, ха

Compile-time UI начинает жить!
🎉4❤2💯1
Если бы массивы были искусством, то это был бы Black-White Array

Емае, как же я кайфую от алгоритма, просто блаженство

https://habr.com/ru/articles/984184/
❤2🔥1
Моя честная реакция когда в школе упомянули Указательное местоимение:
🤣5❤1
This media is not supported in your browser
VIEW IN TELEGRAM
❤3🤩2🔥1
This media is not supported in your browser
VIEW IN TELEGRAM
❤1
This media is not supported in your browser
VIEW IN TELEGRAM
Я просто сейчас в диком сегфолте

Ведь реально.. Это мини-версия статичной рефлексии из С++26!

Которую я случайным образом повторил на С++17

Вот до чего доводит долго сидеть в Райдере по 10 часов 💀
❤4👻2
Forwarded from Shoko - UI Framework (Root Tool)
Что ж, вот и первая неожиданная технодемка Shoko с тестом FShokoRenderer и FShokoPlatformRenderer!

И... Да, она работает, и прекрасно выполняет свою функцию: быстрый рендер + легкий перенос на любую платформу

Конкретно данный билд собран на базе RayLib

Скачать: Shoko - Compile-time UI Framework [Rendering Tech Demo]

А также: Исходный код фреймворка

p.s. кнопки лишь нарисованы, это не виджеты, сорьки :(
❤3❤‍🔥1🤯1💯1
Рефлексия там, где ее не должно быть

Немного ночной пост о том, как я в Shoko реализовал собственную статическую рефлексию на С++17
❤‍🔥2🤩1
Начнем с того, что рефлексия - способность программы видеть себя, знать какие в ней существуют поля, методы, иерархия. Обычно все это достигается тяжелым рантаймом, как пример: Unreal Engine (крайне тежелая рефлексия), Qt (псевдо, через Meta-Object System), LVGL (примитивная, Сишная), Godot (Ага, он тоже на С++)

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

Unreal Engine:
UPROPERTY()
bool bValue = true;

^ UPROPERTY() - Маркер для UHT (Unreal Header Tool), который запускается перед компиляцией, парсит код и генерирует те самые файлы *.generated.h с метаданными. Это классическая рантайм-рефлексия

Qt:
class User : public QObject {
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
};

^ Q_PROPERTY - макрос-указатель для утилиты MOC, которая создает таблицу мета-объектов для рантайме. По сути это тоже рантайм-рефлексия

LVGL:
enum {
MY_PROP_VALUE = LV_PROPERTY_ID(LV_PROPERTY_ID_TYPE_INT, 1),
MY_PROP_COLOR = LV_PROPERTY_ID(LV_PROPERTY_ID_TYPE_COLOR, 2),
};

^ А тут вообще макросный ад: Большое раскрытие макроса LV_PROPERTY_ID + каждое свойство должно быть пронумеровано (А если их там 20+? 40? Шанс ошибиться - как 2 пальца об асфальт). И все же - все данные будут храниться в рантайме = рантайм-рефлексия
❤4💯1
Теперь Shoko

Когда я начинал писать свой Compile-time фреймворк, то столкнулся с проблемой: мне нужна рефлексия, чтобы понимать "что за обьект лежит передо мной"

Идея была тупо стырить собственный Root Header Tool из своего движка Root Engine (его вообще хоть кто-нибудь помнит?), впихнуть его как дополнительный процесс перед сборкой, и итогом получить тяжелую, но рабочую, рантайм-рефлексию

"Но подождите, это же Compile-time фреймворк!" - Так и крикнул я себе и бросил идею с внешними инструментами. Что тогда? Регистрация переменных через макросы? Нууу..... я слишком ленив :]

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

====== GUTID ======

GUTID - Global Unique Type Identifier - Глобальный идентификатор, который хранит уникальный номер типа виджета прямо в его начале. По сути - это просто 1 байтовое число uint8. Существуют 2 версии GUTID: LocalGUTID и StaticGUTID

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

StaticGUTID - Статическая переменная, присущая каждому классу, то есть, для SButton можно легко получить StaticGUTID с помощью SButton::StaticGUTID, а также StaticGUTID помечен как static constexpr - что значит он не идет в рантайм

Это дает мне возможность узнать GUTID класса, просто обратившисть как SButton::StaticGUTID. Но как сделать обратно?

====== SHOKO_GENERATED_BODY ======
#define SHOKO_GENERATED_BODY(Class)  \
static constexpr GUTID StaticGUTID = __COUNTER__; \
friend Class GetClassByGUTIDPrivate(GUTIDReflectionFlag<StaticGUTID>);

Знакомьтесь, мой макрос SHOKO_GENERATED_BODY для каждого класса (UE-style), принимающий на вход имя текущего класса, и генерирующее под него StaticGUTID и... GetClassByGUTIDPrivate ?

По порядку:
__COUNTER__ - Это макрос, который вместо себя ставит число и увеличивается на 1, то есть поставив макрос __COUNTER__ 3 раза в коде, мы получим подстановку 0, 1 и 2. Причина - это моя гарантия, что ни хэш, ни какие другие "примерные алгоритмы" не пересекут StaticGUTID моих типов. А также такая последовательность сыграет нам на руку

friend Class GetClassByGUTIDPrivate - старейший (еще до стандартизации С++) прием, когда обьявленная friend-функция вдруг неожиданно "впрыскивается" из класса в глобальное пространство. Конечно комитет кормит нас, поэтому убрал эту лазейку, но оставил ее для ADL (Argument-Dependent Lookup) - механизм С++, который позволяет компилятору искать функцию в тех пространствах имен, в которых используются аргументы - Просто держите себе в голове, что на вход она принимает GUTIDReflectionFlag<T>, а на выходе ничего не возвращет (без реализации), но возвращает тип класса

GUTIDReflectionFlag<T> - просто шаблонная структура, принимающая на вход GUTID. Но в чем подвох - GUTIDReflectionFlag<4> это НЕ ТОЖЕ САМОЕ что GUTIDReflectionFlag<5> - Это совершенно 2 разных типа! В этом и есть секрет победы!

Давайте закрепим пример для класса SButton:
static constexpr GUTID StaticGUTID = 4;
friend SButton GetClassByGUTIDPrivate(GUTIDReflectionFlag<4>);


Теперь... Мы можем получить класс просто зная GUTID, а как?

====== GetClassByGUTID() ======
Взглянем на это:
template<GUTID InGUTID>
auto GetClassByGUTID() { return GetClassByGUTIDPrivate(GUTIDReflectionFlag<InGUTID>{});
using WidgetType = decltype(GetClassByGUTID<InGUTID>());


Сейчас попробуем узнать тип виджета (WidgetType) просто имея GUTID = 4:
Когда мы пишем GetClassByGUTID()<4>, то компилятору нужно подставить auto под определенный тип. А как? Он начинает искать функцию GetClassByGUTIDPrivate с параметром GUTIDReflectionFlag<4>, чтобы узнать что она возвращает - но... неудача, он не находит в публичном пространстве нужную ему функцию с этим принимающим параметром :-(
❤3🔥1