Modding support

Hi guys, what is the best way how to support modding in games created in Flax engine?

Thx! Petr

Yeah, that’s a feature to be done soon (100% sure this year). We already have the nice infrastructure for assets registry and content packages and binary modules (with scripting plug-and-play) so I guess it will be easy to do. Ofc like always: with proper documentation and tutorials.

Ticket on a roadmap: https://trello.com/c/YVcYaplP/198-dlc-support (for DLC but Modding would go similar path)

2 Likes

Hello Wojtek,

btw. great work on Flax Engine, respect from Keen!

Just for test, can you please guide me to methods usable for creating and reading content packages in runtime?

Best!
Petr

Thanks!

So we have a nice Content Storage system here:

Basically, FlaxFile is used in the editor to storage a single asset, FlaxPackage is used when cooking game at runtime and contains multiple assets. Each binary asset uses up to 16 data chunks that are managed by asset itself. Eg., for textures we store different Mip Levels per chunk so we have performant data streaming (in similar way audio clips are split into several chunks for streaming, etc.).

Game Cooker part that processes assets and performs the packaging including assets registry generation:
https://github.com/FlaxEngine/FlaxEngine/blob/master/Source/Editor/Cooker/Steps/CookAssetsStep.cpp

Here is an example script to create a content package:

// Create asset data (write to memory buffer using stream)
MemoryWriteStream stream;
stream.WriteInt32(1); // some data
stream.WriteFloat(2.0f); // more data
stream.WriteString(TEXT("yo!")); // another data

// Setup asset data chunk
auto dataChunk = New<FlaxChunk>();
// BytesContainer docs: https://docs.flaxengine.com/manual/scripting/cpp/common-types.html#datacontainer
dataChunk->Data.Copy(stream.GetHandle(), stream.GetPosition());

// Save asset to file
AssetInitData initData;
initData.SerializedVersion = 1; // version format (see BinaryAssetUpgrader)
initData.Header.ID = Guid::New(); // asset object id
initData.Header.TypeName = TEXT("Game.MyAsset"); // asset full typename
initData.Header.Chunks[0] = dataChunk;
// tip: you can setup asset dependencies, metadata and custom header payload too (see AssetHeader)
const String path = Globals::ProjectContentFolder / TEXT("MyAsset.flax");
if (FlaxStorage::Create(path, initData))
{
    LOG(Error, "Failed to create asset.");
    return;
}

Note it will work in Editor only as the code for packages creation is disabled in game (USE_EDITOR macro is used)

Then, you can implement loading this asset data (eg. at runtime):

// .h
#include "Engine/Content/BinaryAsset.h"

API_CLASS(NoSpawn) class GAME_API MyAsset : public BinaryAsset
{
DECLARE_BINARY_ASSET_HEADER(MyAsset, 1);
public:
    int32 MyData = 0;

protected:

    // [BinaryAsset]
    AssetChunksFlag getChunksToPreload() const override
    {
        // Asset can define which data chunks to preload before calling load() method
        // This improves performance when scene loads thousands of assets
        // Content Streaming makes this preloading parallel.
        // Ofc it's optional and asset can load chunks manually in load() method via LoadChunks
        return GET_CHUNK_FLAG(0);
    }
    LoadResult load() override
    {
        auto chunk = GetChunk(0);
        if (!chunk)
            return LoadResult::MissingDataChunk;

        // Load asset data from memory stream directly from memory chunk
        MemoryReadStream stream(chunk->Data.Get(), chunk->Data.Length());
        stream.ReadInt32(&MyData);
        //..

        // note that load method in 99% cases will be called from Content Loading thread

        return LoadResult::Ok;
    }
    void unload(bool isReloading) override
    {
        MyData = 0;
    }
};

// .cpp
#include "Engine/Content/Factories/BinaryAssetFactory.h"

REGISTER_BINARY_ASSET(MyAsset, "Game.MyAsset", nullptr, false);

MyAsset::MyAsset(const SpawnParams& params, const AssetInfo* info)
    : BinaryAsset(params, info)
{
}

To manipulate assets you can use FlaxStorage that provides API for content chunks extraction and packaging. It can be used at runtime too for asset packages management and access.

Also, we have Asset Registry (Content::GetRegistry() -> https://github.com/FlaxEngine/FlaxEngine/blob/master/Source/Engine/Content/Cache/AssetsCache.h) that contains cached information about assets. It uses file watchers and files searching for assets in editor, in cooked build we have already compiled assets registry with asset paths mapping.

Feel free to ask if you need more help or have further questions. Also, looking into engine code to see how particular things are implemented can give a better overview of it.

I am more familiar to access my own binary files for blittable data, so anticipating for DLC systems to load flax assets (textures, models, prefabs, sounds…) from indepently packaged files in runtime… :slight_smile: