Обновления

Dagon 1.5.0

Добавлены расширения dagon:network и dagon:security, реализующие, соответственно, сетевой интерфейс на базе UDP/ENet и безопасный транспортный протокол.
Функции для работы с кубическими картами из dagon.graphics.texproc вынесены в отдельный модуль dagon.graphics.cubemap.
Метод Application.takeScreenshot теперь не использует сборщик мусора.

dlib 1.6.0

Добавлен новый пакет dlib.time с реализацией кроссплатформенного интерфейса к системным функциям времени-даты. Временные метки (DateTime) имеют точность до целых секунд и предназначены для относительно редких событий (логирование, онлайн-переписка, календарные события и т.д.). Для более точного измерения времени следует использовать наносекундный счетчик в std.datetime.stopwatch, либо специализированные таймеры внешних библиотек (например, таймер SDL).
Добавлен новый модуль dlib.container.sorting с реализациями алгоритмов сортировки выбором и вставками. Они лучше всего подходят для сортировки небольших массивов нечасто обновляющихся данных в реальном времени. Например, если массив уже отсортирован, то функция сортировки вставками insertionSort будет в сто раз (!) быстрее, чем std.algorithm.sort, что идеально для игр.
Методы встроенного мьютекса Mutex помечены как @nogc.

dagon:network — асинхронный UDP-клиент для игр

В Dagon грядет расширение для создания сетевых игр! Я решил интегрировать библиотеку ENet и написать поверх нее легковесный клиент, при помощи которого игра сможет взаимодействовать с UDP-сервером. Причем это работает через менеджер событий и асинхронную систему обмена сообщениями (dagon.core.messaging), о которой я писал в соответствующем посте. Это дает сразу два преимущества: сетевой клиент не блокирует игровую логику (поскольку работает в собственном потоке), и гарантируется порядок отправки сообщений (сообщения добавляются в SPSC-очередь).

Выглядит это так:

import dagon.ext.network;

class TestScene: Scene
{
    MyGame game;
    NetworkClient netClient;

    this(MyGame game)
    {
        super(game);
        this.game = game;
    }

    override void afterLoad()
    {
        // Инициализация игры...

        netClient = New!NetworkClient("GameClient",
            game.networkManager, "XXX.XXX.XXX.XXX", 1234, this);
        netClient.run();

        eventManager.messageBroker.enabled = true;
    }

    override void onMessage(
        int domain, string sender, string message, void* payload)
    {
        if (sender == "GameClient")
        {
            logInfo("[Server] ", message);
        }
    }

    override void onKeyDown(int key)
    {
        send("GameClient", "Hello, World!");
    }
}

class MyGame: Game
{
    NetworkManager networkManager;

    this(uint w, uint h, bool fullscreen, string title, string[] args)
    {
        super(w, h, fullscreen, title, args);
        networkManager = New!NetworkManager(this);
        currentScene = New!TestScene(this);
    }
}

Реализация полностью потоко-безопасна — хост и пир ENet, ассоциированные с NetworkClient, изолированы в рамках потока, и цикл enet_host_service у него тоже свой собственный.

На данный момент NetworkClient работает только со строками (как, собственно, и сам брокер сообщений). Для поддержки бинарных данных, видимо, придется чуть изменить архитектуру. Кроме того, хочется, чтобы было из коробки шифрование, так что буду копать в сторону AEAD-алгоритмов.