Flyweight паттерн

В данной заметке я опишу структурный паттерн “Легковес” или “Приспособленец” (Flyweight)
Данный паттерн относится к группе Структурных шаблонов.

Рассмотрим пример работы паттерна ниже:


Зачем он нужен? Для экономии оперативной памяти. Соглашусь что во времена повсеместного использования Java (которое потребляет cpu и память просто так), это уже и не так уж важно, однако использовать стоит.
На приведенном выше примере выводится только 40 объектов, но если поднять их количество до 120000, то потребление памяти увеличится соответствующе.
Посмотрим на потребление памяти без использования паттерна flyweight в браузере Chromium:

Без использования паттерна потребление памяти составляет ~300 мегабайт.

Теперь добавим в приложение паттерн и посмотрим потребление памяти:

С использованием паттерна потребление памяти составляет ~200 мегабайт, таким образом мы сэкономили 100 мегабайт памяти в тестовом приложении, в серьезных проектах разница может быть гораздо больше.

Как работает?

В приведенном выше примере мы отрисовываем 40 котиков или для наглядности 120 тысяч. Каждый котик загружается в память в виде png изображения, далее в большинстве рендеров оно конвертируется в битовую карту для отрисовки (фактически bmp), делается это для скорости, так как сжатый png очень долго отрисовывается. Без использования паттерна мы загружаем 120 тысяч картинок котиков в оперативную память и рисуем, а вот при использовании паттерна “легковес” мы загружаем в память одного котика и рисуем его 120 тысяч раз с разной позицией и прозрачностью. Вся магия состоит в том, что координаты и прозрачность мы реализуем отдельно от изображения кота, при отрисовке рендер берет всего одного котика и использует объект с координатами и прозрачностью для корректной отрисовки.

Как выглядит в коде?

Ниже приведены примеры для языка Rise

Без использования паттерна:


Картинка кота загружается для каждого объекта в цикле отдельно – catImage.

С использованием паттерна:

Одна картинка кота используется 120 тысячами объектов.

Где используется?

Используется в GUI фреймворках, например у Apple в системе “переиспользования” (reuse) ячеек таблиц UITableViewCell, чем поднимают порог вхождения для новичков которые не знают про этот паттерн. Также повсеместно используется в разработке игр.

Исходный код

https://gitlab.com/demensdeum/patterns/

Источники

https://refactoring.guru/ru/design-patterns/flyweight
http://gameprogrammingpatterns.com/flyweight.html

Хороший, плохой, мерзкий синглтон

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


Wolf in sheep’s clothing by SarahRichterArt

История происходит в альтернативной вселенной, все совпадения случайны…

Погладь кота на дому с Cat@Home

У каждого человека иногда в жизни возникает непреодолимое желание погладить кота. Аналитики всего мира пророчат что первый стартап создавший приложение по доставке и аренде котиков станет крайне популярным, в недалекой перспективе будет куплен компанией Moogle за триллионы долларов. Вскоре так и происходит – парень из Тюмени создает приложение Cat@Home, и вскоре становится триллиардером, компания Moogle получает себе новый источник прибыли, а миллионы застрессованых людей получают возможность заказать кота на дом для дальнейшего глаженья и успокоения.

Атака клонов

Крайне богатый дантист из Мурманска Алексей Голобородько, впечатлившись статьей про Cat@Home из Фorbes, решает что тоже хочет быть астрономически богатым. Для достижения этой цели, через своих друзей, он находит компанию из Голдфилда – Wakeboard DevPops которая оказывает услуги по разработке ПО, он заказывает разработку клона Cat@Home у них.

Команда победителей

Проект называют Fur&Pure, поручают талантливой команде разработчиков из 20 человек; далее сосредоточимся на группе мобильной разработки из 5 человек. Каждый член команды получает свою часть работы, вооружившись agile-ом и скрамом, команда завершает разработку в срок (за полгода), без багов, релизит приложение в iStore, где ее оценивают 100.000 пользователей на 5, много комментариев о том как прекрасно приложение, как прекрасен сервис (Альтернативная вселенная как-никак). Коты выглажены, приложение выпущено, вроде-бы все идет хорошо. Однако компания Moogle не торопится покупать стартап за триллионы долларов, потому что в Cat@Home уже появились не только коты но и собаки.

Собака лает, караван идет

Владелец приложения решает что пора добавить в приложение собак, обращается за оценкой в компанию и получает примерно минимум полгода на добавление собак в приложение. Фактически приложение будет написано с нуля снова. За это время Moogle добавит в приложение змей, пауков и морских свинок, а Fur&Pur получит только собак.
Почему так получилось? Во всем виновато отсутствие гибкой архитектуры приложения, одним из самых распространенных факторов является антипаттерн проектирования Singleton.

А что такого?

Для того чтобы заказать кота на дом, потребителю нужно создать заявку и отправить ее в офис, где в офисе ее обработают и пришлют курьера с котом, курьер уже получит оплату за услугу.
Один из программистов решает создать класс “ЗаявкаНаКота” с необходимыми полями, выносит этот класс в глобальное пространство приложения через синглтон. Зачем он это делает? Для экономии времени (копеечная экономия получаса), ведь проще вынести заявку в общий доступ, чем продумывать архитектуру приложения и использовать dependency injection. Дальше остальные разработчики подхватывают этот глобальный объект и привязывают свои классы к нему. Например все экраны сами обращаются к глобальному объекту “ЗаявкаНаКота” и показывают данные по заявке. В итоге такое монолитное приложение тестируется и сдается в релиз.
Все вроде хорошо, но вдруг появляется заказчик с требованием добавить в приложение заявки на собак. Команда судорожно начинает оценивать сколько компонентов в системе затронет данное изменение. По окончанию анализа оказывается что нужно переделать от 60 до 90% кода, чтобы научить приложение принимать в глобальном объекте-синглтоне не только “ЗаявкуНаКота” но и “ЗаявкуНаСобаку”, оценивать добавление остальных животных на данном этапе уже бесполезно, справиться хотя бы с двумя.

Как не допустить синглтон

Во-первых, на этапе сбора требований явно указать необходимость в создании гибкой, расширяемой архитектуры. Во-вторых, стоит проводить независимую экспертизу кода продукта на стороне, с обязательным исследованием слабых мест. Если вы разработчик и вы любите синглтоны, то предлагаю одуматься пока не поздно, иначе бессонные ночи и выжженные нервы обеспечены. Если вы работаете с проектом по наследству, в котором много синглтонов, то попытайтесь избавиться от них как можно быстрее, или от проекта.
Переходить с антипаттерна синглтонов-глобальных объектов/переменных нужно на dependency injection – простейший паттерн проектирования в котором все необходимые данные задаются экземпляру класса на этапе инициализации, без дальнейшей необходимости быть привязанным к глобальному пространству.

Источники

https://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons
http://misko.hevery.com/2008/08/17/singletons-are-pathological-liars/
https://blog.ndepend.com/singleton-pattern-costs/

Death Mask дев отчет 1

Новая непостоянная рубрика “дневники разработчиков” или Dev Diary на иностранный манер.
Разработка игры Death-Mask идет полным ходом, за 2019 год добавлен логотип движка игры Flame Steel Engine, экран выбора начальной карты по островам (зеленый, красный, черный, белый) вывод текстур для стен, потолка, пола лабиринта, увеличены размеры игровой зоны.


Карта Города Красной Зоны

Далее планируется добавить 3д модели для окружения, вместо Doom-style спрайтов, в планах добавить модели для оружия, ящиков, врагов, друзей. В геймплее планируется добавить валюту, магазины, возможность покупать части игровой карты с указанием интересных мест с лутом, и возможного нахождения “Маски Смерти”. Также хочу добавить возможность нанимать компаньонов для странствий по киберлабиринту.
Следите за новостями.

Swift 4.2.3 – Ubuntu 18.10

Сборка Swift с необходимыми библотеками для запуска на Ubuntu 18.10. Последняя доступная версия на сайте Apple – для Ubuntu 18.04. Основана на сборке с официального сайта с добавлением библиотек из Ubuntu 18.04. Также добавлен пример скрипта для добавления PATH и LD_LIBRARY_PATH для терминала bash:
http://www.mediafire.com/file/lrs74mvoj3fti03/swift4.2.3.ubuntu.18.10.x86_64.tar.gz/file

Декларативный язык Zakaz

Представляю вашему вниманию чистый декларативный язык программирования – Zakaz. Основная идея нового языка – приложение содержит команды на выполнение, написанные в произвольной форме, которые должны быть выполнены “исполнителями”. Если ни один “исполнитель” не может выполнить команду, то выполнение программы останавливается. Приложения называются техзаданиями (tez) и должны иметь расширение .tez. Синтаксис Zakaz обязывает соблюдать два правила:

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

Пример Hello World.tez:

Show "Hello World" text on screenShow "Zakaz 'tez' example" text on screen

Пример тз которое выводит описание принципа работы и открывающего сайт http://demensdeum.com в браузере Firefox

Show "Show website demo" text on screenShow "You need Firefox installed on your system to run this 'tez', and it should be callable through \"system\" C function" text on screenShow "Also there should be \"FirefoxPerformer\" assigned to Zakaz Runtime, please check manual for more information" text on screenShow website with address "http://demensdeum.com" in Firefox

Запускать пример выше необходимо вместе с “исполнителем” FirefoxPerformer, который способен обработать последнюю команду по выводу сайта через Firefox

./ZakazRuntime openDemensdeumSite.tez FirefoxPerformer

Для имплементации своего исполнителя необходимо реализовать его в виде динамической библиотеки, используя абстрактный класс ZakazRuntime::Performer, и вернуть его вместе с умным указателем из метода глобальной функции createPerformer(). В качестве примера можно использовать реализацию FirefoxPerformer.

Исходный код

https://gitlab.com/demensdeum/zakaz

С++ плагины

В этой заметке я опишу пример добавления функционала в C++ приложение с помощью плагинов. Описана практическая часть реализации для Linux, с теорией можно будет ознакомиться по ссылкам в конце статьи.

Composition over inheritance!

Для начала напишем плагин – функцию которую будем вызывать:

#include "iostream"

using namespace std;

extern "C" void extensionEntryPoint() {
	cout << "Extension entry point called" << endl;
};

Далее соберем плагин как динамическую библиотеку “extension.so”, которую и будем подключать в дальнейшем:
clang++ -shared -fPIC extension.cpp -o extension.so

Напишем основое приложение, которое будет загружать файл “extension.so”, искать там указатель на функцию “extensionEntryPoint”, и вызывать его, печатая ошибки при необходимости:

#include "iostream"
#include "dlfcn.h"

using namespace std;

typedef void (*VoidFunctionPointer)();	

int main (int argc, char *argv[]) {

	cout << "C++ Plugins Example" << endl;

	auto extensionHandle = dlopen("./extension.so", RTLD_LAZY);
	if (!extensionHandle) {
		string errorString = dlerror();
		throw runtime_error(errorString);
	}

	auto functionPointer = VoidFunctionPointer();
	functionPointer = (VoidFunctionPointer) dlsym(extensionHandle, "extensionEntryPoint");
	auto dlsymError = dlerror();
 	if (dlsymError) {
		string errorString = dlerror();
		throw runtime_error(errorString);
 	}

	functionPointer();

	exit(0);
} 

Функция dlopen возвращает хэндлер для работы с динамической библиотекой; функция dlsym возвращает указатель на необходимую функцию по строке; dlerror содержит указатель на строку с текстом ошибки, если таковая имеется.

Далее собираем основное приложение, копируем файл динамической библиотеки в папку с ним и запускаем. На выходе должен быть вывод “Extension entry point called”

К сложным моментам можно отнести отсутствие единого стандарта работы с динамическими библиотеками, из-за этого есть необходимость экспорта функции в относительно глобальную область видимости с extern C; разница в работе с разными операционными системами, связанные с этим тонкости работы; отсутствие C++ интерфейса для реализации ООП подхода к работе с динамическими библиотеками, однако существуют open-source врапперы, например m-renaud/libdlibxx

Исходный код примера

https://gitlab.com/demensdeum/cpppluginsexample

Источники

http://man7.org/linux/man-pages/man3/dlopen.3.htm
https://gist.github.com/tailriver/30bf0c943325330b7b6a
https://stackoverflow.com/questions/840501/how-do-function-pointers-in-c-work

Порхай как Мишель

[Feel the power of Artificial Intelligence]
В данной заметке я расскажу как предсказывать будущее.

В статистике существует класс задач – анализ временных рядов. Имея дату и значение некой переменной, можно прогнозировать значение этой переменной в будущем.
Поначалу я хотел реализовать решение данной задачи на TensorFlow, однако нашел библиотеку Prophet от Facebook.
Prophet позволяет делать прогноз на основе данных (csv), содержащих колонки даты (ds) и значения переменной (y). О том как с ней работать, можно узнать в документации на официальном сайте в разделе Quick Start
В качестве датасета я использовал выгрузку в csv с сайта https://www.investing.com, при реализации я использовал язык R и Prophet API для него. R мне очень понравился, так как его синтаксис упрощает работу с большими массивами данных, позволяет писать проще, допускать меньше ошибок, чем при работе с обычными языками (Python), так как пришлось бы работать с лямбда выражениями, а в R уже все лямбда выражения.
Для того чтобы не подготавливать данные к обработке, я использовал пакет anytime, который умеет переводить строки в дату, без предварительной обработки. Конвертация строк валюты в number осуществляется с помощью пакета readr.

В результате я получил прогноз по которому биткоин будет стоить 8400$ к концу 2019 года, а курс доллара будет 61 руб. Стоит ли верить данным прогнозам? Лично я считаю что не стоит, т.к. нельзя использовать математические методы, не понимая их сущности.

Источники

https://facebook.github.io/prophet
https://habr.com/company/ods/blog/323730/
https://www.r-project.org/

Исходный код

https://gitlab.com/demensdeum/MachineLearning/tree/master/4prophet

Говорит Тесла

В этой заметке я опишу процесс создания генератора цитат.

TL;DR

Для обучения и генерации текста – использовать библиотеку textgenrnn, для фильтрации фраз нужно использовать проверку орфографии с помощью утилиты hunspell и ее библиотеки для C/python. После обучения в Colaboratory, можно приступать к генерации текста. Примерно 90% текста будет абсолютно не читаемым, однако оставшиеся 10% будут содержать толику смысла, а при ручной доработке фразы будут выглядеть вполне неплохо.
Проще всего запустить готовую нейросеть в Colaboratory:
https://colab.research.google.com/drive/1-wbZMmxvsm3SoclJv11villo9VbUesbc(opens in a new tab)”>https://colab.research.google.com/drive/1-wbZMmxvsm3SoclJv11villo9VbUesbc

Исходный код

https://gitlab.com/demensdeum/MachineLearning/tree/master/3quotesGenerator

Источники

https://karpathy.github.io/2015/05/21/rnn-effectiveness/https://medium.com/deep-learning-turkey/google-colab-free-gpu-tutorial-e113627b9f5dhttps://minimaxir.com/2018/05/text-neural-networks/
https://github.com/wooorm/dictionaries (opens in a new tab)” href=”https://minimaxir.com/2018/05/text-neural-networks/” target=”_blank”>https://minimaxir.com/2018/05/text-neural-networks/
https://karpathy.github.io/2015/05/21/rnn-effectiveness/
https://medium.com/deep-learning-turkey/google-colab-free-gpu-tutorial-e113627b9f5d
https://karpathy.github.io/2015/05/21/rnn-effectiveness/ (opens in a new tab)” href=”https://karpathy.github.io/2015/05/21/rnn-effectiveness/” target=”_blank”>https://karpathy.github.io/2015/05/21/rnn-effectiveness/
https://medium.com/deep-learning-turkey/google-colab-free-gpu-tutorial-e113627b9f5d
https://karpathy.github.io/2015/05/21/rnn-effectiveness/https://medium.com/deep-learning-turkey/google-colab-free-gpu-tutorial-e113627b9f5dhttps://github.com/wooorm/dictionaries

” rel=”noopener” target=”_blank”>https://github.com/wooorm/dictionaries (opens in a new tab)”>https://github.com/wooorm/dictionaries

Сколько у тебя там ошибок?

На Hacker News нашел очень интересную статью в которой автор предлагает использовать метод Петерсена-Линкольна, который используется биологами для подсчета популяции птичек, обезьянок и прочих животных, для *барабанная дробь* подсчета багов в приложении.

Баг в естественной среде обитания – Bigfoot Sighting by Derek Hatfield

Метод очень прост, берем двух орнитологов, они находят птичек какого-то определенного вида, их задача – определить размер популяции этих птичек. Найденные птички помечаются обоими орнитологами, далее подсчитывается количество общих, подставляется в формулу индекса Линкольна и мы получаем примерный размер популяции.
Теперь для приложений – метод также очень прост, берем двух QA и они находят баги в приложении. Допустим один тестировщик нашел 10 багов (E1), а второй 20 багов (E2), теперь берем число общих багов – 3 (S), далее по формуле получаем индекс Линкольна:

Это и есть прогноз числа багов во всем приложении, в приведенном примере ~66 багов.

Пример на Swift

Я реализовал тестовый стенд для проверки метода, посмотреть можно здесь:
https://paiza.io/projects/AY_9T3oaN9a-xICAx_H4qw?language=swift

Параметры которые можно менять:

let aliceErrorFindProbability = 20 – процент нахождения багов у QA Alice (20%)
let bobErrorFindProbability = 60 – процент нахождения багов у QA Bob (60%)
let actualBugsCount = 200 – сколько багов в приложении на самом деле

В последнем запуске я получил следующие данные:
Estimation bugs count: 213
Actual bugs count: 200

Тоесть в приложении есть 200 багов, индекс Линкольна дает прогноз – 213:
“Alice found 36 bugs”
“Bob found 89 bugs”
“Common bugs count: 15”

Estimation bugs count: 213
Actual bugs count: 200

Слабые стороны

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

Источники

How many errors are left to find? – John D. Cook, PhD, President
The thrill of the chase – Brian Hayes

Исходный код

https://paiza.io/projects/AY_9T3oaN9a-xICAx_H4qw?language=swift
https://gitlab.com/demensdeum/statistics/tree/master/1_BugsCountEstimation/src

Скрипя шестеренками

Ах муза, как сложно тебя поймать порой.
Разработка Death-Mask, и связанных фреймворков (Flame Steel Core, Game Toolkit и др.) приостанавливается на несколько месяцев, для того чтобы определиться с художественной частью игры, музыкальным, звуковым сопровождением, продумыванием геймплея.
В планах – создать редактор для Flame Steel Game Toolkit, написать интерпретатор игровых скриптов (на основе синтаксиса Rise), реализовать игру Death-Mask для максимально большого количества платформ.
Сложнейший этап пройден – на практике доказана возможность написания своего собственного кроссплатформенного игрового движка, своего IDE, набора библиотек.
Перехожу к этапу создания действительно продуманного, интересного проекта, следите за новостями.

Новостной краулер для iOS

Новостной краулер iOS – приложение ищет текст и выводит результат во время загрузки.
Заложена поддержка больших файлов из коробки (> 200мб), результаты сохраняются в result.log файл.
Простой, продуманный дизайн.
Поддержка регулярок с помощью библиотеки Regex.

Исходный код:
https://gitlab.com/demensdeum/news-crawler

Death-Mask в открытом доступе

С сегодняшнего дня игра Death-Mask уходит в открытый доступ – можете следить за прогрессом реализации игры по ссылке: (Вау!)
[Death-Mask Wild]

На данный момент версия – 0.1 содержит базовое управление на стрелках, wsad, генерация карт, предметов (в том числе и маску смерти!), рендеринг.
Предстоит еще куча работы, и мне очень интересен ваш фидбэк – поэтому можете писать комментарии на странице с wild версией игры.
В финальной версии игра заканчивается после того как игрок найдет предмет – Маску Смерти (Death-Mask)
Приятного теста : )

Авторы ресурсов

Hangar18 утилита индексации исходного кода

Hangar18 – утилита для индексации исходного кода C++, написанная на Rust. Данная утилита будет реализовывать функционал “перейти к определению” в Saber-Plus IDE.
На вход утилите подается абсолютный путь к директории исходного кода, строка декларации которую необходимо найти. На выходе grep-подобный вывод.

Исходный код:
https://gitlab.com/demensdeum/hangar18

Taytay контроль статуса Git репозиториев

Представляю вашему вниманию Taytay – утилиту для контроля статуса репозиторев git для языка Swift. На текущий момент Свифт можно установить на всех мейнстримовых десктопных операционных системах. Для Ubuntu я рекомендую использовать Swiftenv. Завязан Тайтай на утилиту git-cola, но можешь отредактировать исходник и поменять на любую другую программу.

Исходный код:
https://gitlab.com/demensdeum/taytay

Бьемся с Малевичем, черные квадраты OpenGL

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

Сегодня я опишу по какой причине меня посетил черный квадрат, найденные проблемы из-за которых OpenGL ничего не рисует на экране, а иногда и вообще делает окно прозрачным.

Используй инструменты

Для отладки OpenGL мне помогли два инструмента: renderdoc и apitrace. Renderdoc – инструмент для отладки процесса рендеринга OpenGL, просматривать можно все – вертексы, шейдеры, текстуры, отладочные сообщения от драйвера. Apitrace – инструмент для трейсинга вызовов графического API, делает дамп вызовов и показывает аргументы. Также есть великолепная возможность сравнивать два дампа через wdiff (или без него, но не так удобно)

Проверяй с кем работаешь

У меня есть операционная система Ubuntu 16.10 со старыми зависимостями SDL2, GLM, assimp, GLEW. В последней версии Ubuntu 18.04 я получаю сборку игры Death-Mask которая ничего не показывает на экране (только черный квадрат). При использовании chroot и сборке в 16.10 я получаю рабочую сборку игры с графикой.

Похоже что-то сломалось в Ubuntu 18.04

LDD показал линковку к идентичным библиотекам SDL2, GL. Прогоняя нерабочий билд в renderdoc, я увидел мусор на входе в вертексный шейдер, но мне нужно было более солидное подтверждение. Для того чтобы разобраться в разнице между бинариками я прогнал их оба через apitrace. Сравнение дампов показало мне что сборка на свежей убунте ломает передачу матриц перспективы в OpenGL, фактически отправляя туда мусор:

Матрицы собираются в библиотеке GLM. После копирования GLM из 16.04 – я снова получил рабочий билд игры. Проблема оказалась в разнице инициализации единичной матрицы в GLM 9.9.0, в ней необходивно явно указывать аргумент mat4(1.0f) в конструкторе. Поменяв инициализацию и отписав автору библиотеки, я принялся делать тесты для FSGL. в процессе написания которых я обнаружил недоработки в FSGL, их опишу далее.

Определись ты кто по жизни

Для корректной работы с OpenGL нужно в добровольно принудительном порядке запросить контекст определенной версии. Так это выглядит для SDL2 (проставлять версию нужно строго до инициализации контекста):

    SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 2);
SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE );

Например Renderdoc не работает с контекстами ниже 3.2. Хочется отметить что после переключения контекста высока вероятность увидеть тот самый черный экран. Почему?
Потому что контекст OpenGL 3.2 обязательно требует наличие VAO буфера, без которого не работают 99% графических драйверов. Добавить его легко:

    glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

Не спи, замерзнешь

Также я встретился с интересной проблемой на Kubuntu, вместо черного квадрата у меня выводился прозрачный, а иногда все рендерилось корректно. Решение этой проблемы я нашел на Stack Overflow:
https://stackoverflow.com/questions/38411515/sdl2-opengl-window-appears-semi-transparent-sometimes

В коде тестового рендера FSGL тоже присутствовал sleep(2s); Так вот на Xubuntu и Ubuntu я получал корректный рендер и отправлял приложение спать, однако на Kubuntu я получил прозрачный экран в 80% случаев запуска из Dolphin и 30% запусков и терминала. Для решения данной проблемы я добавил рендеринг в каждом кадре, после опроса SDLEvent, как это рекомендуется делать в документации.

Код теста:
https://gitlab.com/demensdeum/FSGLtests/blob/master/renderModelTest/

Поговори с драйвером

OpenGL поддерживает канал связи между приложением и драйвером, для его активации нужно включить флаги GL_DEBUG_OUTPUT, GL_DEBUG_OUTPUT_SYNCHRONOUS, проставить оповещение glDebugMessageControl и привязать каллбек через glDebugMessageCallback.
Пример инициализации можно взять здесь:
https://github.com/rock-core/gui-vizkit3d/blob/master/src/EnableGLDebugOperation.cpp

Не бойся, посмотри как он увеличивается

В данной заметке я расскажу о своих злоключениях с умными указателями shared_ptr. После реализации генерации следующего уровня в своей игре Death-Mask, я заметил утечку памяти. Каждый новый уровень давал прирост + 1 мегабайт к потребляемой оперативной памяти. Очевидно что какие-то объекты оставались в памяти и не освобождали ее. Для исправления данного факта необходимо было реализовать корректную реализацию ресурсов при перегрузке уровня, чего видимо сделано не было. Так как я использовал умные указатели, то вариантов решения данной задачи было несколько, первый заключался в ручном отсмотре кода (долго и скучно), второй же предполагал исследование возможностей дебагера lldb, исходного кода libstdc++ на предмет возможности автоматического отслеживания изменений счетчика.

В интернете все советы сводились к тому чтобы вручную отсматривать код, исправить и бить себя плетями после нахождения проблемной строчки кода. Также предлагалось реализовать свою собственную систему работы с памятью, как это делают все крупные проекты разрабатываемые еще с 90-х и нулевых, до прихода умных указателей в стандарт C++11. Мною была предпринята попытка использовать брейкпоинты на конструкторе копии всех shared_ptr, после нескольких дней ничего дельного не получилось. Была идея добавить логирование в библиотеку libstdc++, однако трудозатраты (о)казались чудовищными.


Cowboy Bebop (1998)

Решение пришло мне в голову внезапно в виде отслеживания изменений приватной переменной shared_ptr – use_count. Сделать это можно с помощью встроенных в lldb ватчпоинтов (watchpoint) После создания shared_ptr через make_shared, изменения счетчика в lldb можно отслеживать с помощью строки:

watch set var camera._M_refcount._M_pi->_M_use_count

Где “camera” это shared_ptr объект состояние счетчика которого необходимо отследить. Конечно внутренности shared_ptr будут различаться в зависимости от версии libstdc++, но общий принцип понять можно. После установки ватчпоинта запускаем приложения и читаем стектрейс каждого изменения счетчика, потом отсматриваем код (sic!) находим проблему и исправляем. В моем случае объекты не освобождались из таблиц-кешэй и таблиц игровой логики. Надеюсь данный метод поможет вам разобраться с утечками при работе с shared_ptr, и полюбить этот инструмент работы с памятью еще больше. Удачного дебага.

Games Vision #3

Третий выпуск непостоянной рубрики об играх Games Vision.

Observer (ПК и консоли, Bloober Team) – киберпанк-хоррор от доблестных поляков. Короткий и очень атмосферный ужастик с Рутгером Хауэром в главной роли. Мне, как фанату киберпанка, в игре понравилось абсолютно все. Не очень сложные загадки, очаровательные глюки главного героя, геймплей со спокойными моментами в перемешку с экшеном, возможность в буквальном смысле копаться в воспоминаниях мертвых, сюжет в стиле Призрака в Доспехах + множество отсылок к Sci-Fi попкультуре. Из минусов – перебор с гличами, иногда кажется что из-за их обилия невозможно играть, также некоторых игроков выводили из себя специфические хоррор элементы, пугающие настолько что они не могли продолжать играть.
Оценка: 8/10

Paradigm (Windows/OS X, Jacob Janerka) – квест который умудряется пародировать и смеяться над всем и сразу. Здесь есть высмеивание СССР, Америки, жанра квестов, глэм-рока, старых приставок, людей, памятников, айтишников, конусов, компьютеров, женщин, детей, родителей, художников, любви, ученых, игровой индустрии, самых игроков – вообщем всего не перечислить. Абсолютно непредсказуемый сюжет, абсурдная атмосфера и арт, не особо сложные загадки. В игре есть редкие баги и вылеты, некоторые моменты и шутки слегка предсказуемы и не оригинальны.
Оценка: 9/10

Late Shift (ПК и консоли, CtrlMovie Ltd) – интерактивный фильм. Мне действительно жаль что в принципе хорошая задумка получила столь плохую реализацию. Плохо все – сюжет, отсутствие звездных актеров, актерская игра, постоянные зависания в версии для ПК, практически нулевая вариативность (иллюзорная). Совершенно непонятно как можно было выпустить игру с таким количеством проблем в 2010-х, ведь по сути это обычный видеоплеер, всю игру можно было разместить в интернете например на youtube, но вместо этого использовали Unity и умудрились сломать даже столь мощный игровой движок. Официальный форум в Steam посвящен фиксам, хотфиксам, воркараундам и т.д. Налицо техническая катастрофа, отсутствие службы поддержки пользователей, все тестирование происходит прямо на игроках. Купленные восторженные обзоры и отзывы.
Оценка: 3/10

Rise Programming Language

Представляю вашему вниманию мой собственный язык программирования под названием – Rise. На данный момент доступен транспайлер из Rise в JavaScript.

Его можно увидеть и попользоваться по ссылке ниже – Rise в JavaScript (диалект ECMAScript 5):
https://gitlab.com/demensdeum/Rise

Также представляю вашему вниманию демо приложение полностью написанное на Rise:

Исходный код Rise Demo Application:
https://gitlab.com/demensdeum/RiseDemoApplication

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

Ядро коррупции

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

Don’t move !
by M-Delcambre

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

На северо-западе Ревил увидел синее свечение о котором говорила ему Алиса, в центре его находится огромное здание, созданное строителями Технолаба. Странное, гротескное нагромождение торчащих каркасов и механических частей будто созданное безумцем, наводило ужас своим видом.

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

Saber-Plus C++ IDE

Начал разработку собственного IDE для С++ – Saber-Plus. Основные идеи нового IDE – быть простым, быстрым и *помогающим* в разработке. На данный момент исходный код доступен по лицензии MIT на GitHub, для работы с UI используется Qt. В дальнейшем планирую перенести всю разработку связанную с C++ на Saber-Plus – точно будет проведена миграция игры Death-Mask. Подробнее по пунктам:

  • Простой – планируется не добавлять больше чем нужно – например не содержать source control клиенты, встроенный терминал и подобные вещи. Функционал сосредоточен только на редактировании кода, анализе ошибок. Код редактора должен быть разбит на простые классы, которые корректно выполняют свою часть работы (Unix-way)
  • Быстрый – касается как кодовой базы IDE так и самого поведения редактора. Все действия в IDE должны быть максимально быстрыми, даже такие зачастую долгие и сложные как создание/импортирование проектов.
  • Помогающий – анализ типичных ошибок при написании, компиляции кода. Исправление ошибок, предупреждений по требованию пользователя. В планах идея добавить анализ сборки приложения на конкретной платформе и вывод справочной информации по установке нужных библиотек, компонентов.

Для сборки редактора для вашей операционной системы, нужно установить  Qt 5 SDK, загрузить код IDE из репозитория, открыть файл Saber-Plus.pro в Qt Creator и запустить сборку:

https://github.com/demensdeum/saberplus

Простой пример TensorFlow

Представляю вашему вниманию простейший пример работы с фреймворком для работы с Deep Learning – TensorFlow. В этом примере мы научим нейросеть определять положительние, отрицательные числа и ноль. Установку TensorFlow и CUDA я поручаю вам, эта задачка действительно не из легких)

Для решения задач классификации используются классификаторы. TensorFlow имеет несколько готовых высокоуровневых классификаторов, которые требуют минимальной конфигурации для работы. Сначала мы потренируем DNNClassifier с помощью датасета с положительными, отрицательными числами и нулем – с корректными “лейблами”. На человеческом уровне датасет представляет из себя набор чисел с результатом классификации (лейблами):

10 – положительное
-22 – отрицательное
0 – ноль
42 – положительное
… другие числа с классификацией

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

import tensorflowimport itertoolsimport randomfrom time import timeclass ClassifiedNumber:__number = 0__classifiedAs = 3def __init__(self, number):self.__number = numberif number == 0:self.__classifiedAs = 0 # zeroelif number > 0:self.__classifiedAs = 1 # positiveelif number < 0:self.__classifiedAs = 2 # negativedef number(self):return self.__numberdef classifiedAs(self):return self.__classifiedAsdef classifiedAsString(classifiedAs):if classifiedAs == 0:return "Zero"elif classifiedAs == 1:return "Positive"elif classifiedAs == 2:return "Negative"def trainDatasetFunction():trainNumbers = []trainNumberLabels = []for i in range(-1000, 1001):number = ClassifiedNumber(i)trainNumbers.append(number.number())trainNumberLabels.append(number.classifiedAs())return ( {"number" : trainNumbers } , trainNumberLabels )def inputDatasetFunction():global randomSeedrandom.seed(randomSeed) # to get same resultnumbers = []for i in range(0, 4):numbers.append(random.randint(-9999999, 9999999))return {"number" : numbers }def main():print("TensorFlow Positive-Negative-Zero numbers classifier test by demensdeum 2017 (demensdeum@gmail.com)")maximalClassesCount = len(set(trainDatasetFunction()[1])) + 1numberFeature = tensorflow.feature_column.numeric_column("number")classifier = tensorflow.estimator.DNNClassifier(feature_columns = [numberFeature], hidden_units = [10, 20, 10], n_classes = maximalClassesCount)generator = classifier.train(input_fn = trainDatasetFunction, steps = 1000).predict(input_fn = inputDatasetFunction)inputDataset = inputDatasetFunction()results = list(itertools.islice(generator, len(inputDatasetFunction()["number"])))i = 0for result in results:print("number: %d classified as %s" % (inputDataset["number"][i], classifiedAsString(result["class_ids"][0])))i += 1randomSeed = time()main()

Все начинается в методе main(), мы задаем числовую колонку с которой будет работать классификатор – tensorflow.feature_column.numeric_column(“number”) далее задаются параметры классификатора. Описывать текущие аргументы инициализации бесполезно, так как API меняется каждый день, и обязательно нужно смотреть документацию именно установленной версии TensorFlow, не полагаться на устаревшие мануалы.

Далее запускается обучение с указанием на функцию которая возвращает датасет из чисел от -1000 до 1000 (trainDatasetFunction), с правильной классификацией этих чисел по признаку положительного, отрицательного либо нуля. Следом подаем на вход числа которых не было в обучающем датасете – случайные от -9999999 до 9999999 (inputDatasetFunction) для их классификации.

В финале запускаем итерации по количеству входных данных (itertools.islice) печатаем результат, запускаем и удивляемся:

number: 4063470 classified as Positivenumber: 6006715 classified as Positivenumber: -5367127 classified as Negativenumber: -7834276 classified as Negative

iT’S ALIVE

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

GitLab:
https://gitlab.com/demensdeum/MachineLearning

Ссылки:
https://developers.googleblog.com/2017/09/introducing-tensorflow-datasets.html
https://www.tensorflow.org/versions/master/api_docs/python/tf/estimator/DNNClassifier

Ломаем Bitcoin

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

Уязвимый центр

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


Принцип блок-чума от Елкина

Для того чтобы блокчейн работал, нужно сделать так чтобы каждый пользователь скачивал последнюю копию базы данных блокчейна, и работал с ней по определенным правилам. К таким правилам относится реализация принципа майнинга биткоина, получение процента от каждой транзакции при подтверждении (transaction fee) передачи средств с одного кошелька на другой. Пользователь не может нарисовать себе 1000000 биткоинов и купить на них что-то, т.к. у других пользователей количество денег на его счету будет неизменным. Также исключен вариант со снятием средств с чужого кошелька только внутри своей базы данных т.к. это изменение не будет отражено у других пользователей биткоина, и будет проигнорировано.
Уязвимость текущей реализации заключается в том что биткоин кошелек находится на сервере github что полностью перекрывает рекламные лозунги о децентрализации. Без загрузки кошелька из единого центра – сайта разработчика, невозможно работать с биткоином, тоесть в любой момент разработчики имеют полный контроль над сетью. Таким образом, сама технология блокчейн является децентрализованной, но клиент для работы с сетью загружается из единого центра.
Сценарий атаки  – допустим в кошелек добавлен код для снятия всех средств и обналичивания на счет третьих лиц, после этого любой пользователь последней версии кошелька потеряет все биткоины автоматически (без возможности восстановления). Сомневаюсь что многие владельцы кошелька проверяют и собирают его из исходного кода, поэтому последствия такой атаки затронут большинство пользователей.

Решает большинство

Блокчейн является децентрализованной p2p сетью, подтверждением всех операций занимаются сами пользователи в автоматическом режиме. Сценарий атаки  – необходимо получить 51% сети для того чтобы игнорировать подтверждения оставшихся 49%, после этого атакующий получает полный контроль над биткоином/блокчейном. Этого можно добиться подключив вычислительные мощности перекрывающие остальных. Этот сценарий атаки известен как 51% attack.

Угадай меня если сможешь

При первом запуске кошелька, компьютер генерирует пару – приватный и публичный ключ для обеспечения своей корректной работы. Уникальность данных ключей крайне высока, однако есть вариант сгенерировать ключи с помощью кодового слова – так называемый “brain wallet“. Человек хранит ключи у себя в голове, ему не нужно делать бекап файла wallet.dat, т.к. в любой момент ключи можно будет перегенерить с помощью данного кодового слова. Сценарий атаки – злоумышленник подбирает или узнает кодовое слово, генерирует пару приватный-публичный ключ и получает контроль над кошельком.

Просто скопируй

Пара приватный-публичный ключ содержится в файле wallet.dat. Любое программное обеспечение имеющее доступ к данному файлу – имеет доступ к кошельку биткоин. Защитой от такого нападения служит добавление кодового слова, которое должен будет помнить и вводить пользователь, для всех операций с кошельком. После добавления кодового слова, злоумышленнику нужно будет иметь wallet.dat и кодовое слово для получения полного контроля.
Также стоит добавить что при вводе кодового слова оно попадает в память компьютера, таким образом любые уязвимости аппаратные и/или программые позволяющие читать *чужую* память позволят прочитать и это кодовое слово вирусному программному обеспечению.

Ошибка системы

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