Skip to content

Mod

ll/api/mod/ Β· Common

Overview

The Mod module manages mod lifecycle, metadata (manifest), directory structure, and provides access to mod-specific resources like loggers, config directories, and data directories.

Headers

Header Description
ll/api/mod/Mod.h Mod instance and lifecycle
ll/api/mod/Manifest.h Mod metadata structure
ll/api/mod/ModManager.h Base mod manager
ll/api/mod/ModManagerRegistry.h Registry of all mod managers
ll/api/mod/NativeMod.h Native (DLL) mod implementation

Key Classes

Mod

Represents a loaded mod with lifecycle callbacks and resource access.

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
namespace ll::mod {
class Mod {
public:
    enum class State { Enabled, Disabled };

    State getState() const;
    Manifest const& getManifest() const;
    std::string const& getName() const;
    std::string const& getType() const;

    std::filesystem::path const& getModDir() const;
    std::filesystem::path const& getDataDir() const;
    std::filesystem::path const& getConfigDir() const;
    std::optional<std::filesystem::path> getWorldDataDir() const;
    std::optional<std::filesystem::path> getWorldConfigDir() const;
    std::filesystem::path const& getLangDir() const;
    std::filesystem::path const& getResourceDir() const;
    std::filesystem::path const& getBehaviorDir() const;

    io::Logger& getLogger() const;

    bool isEnabled() const;
    bool isDisabled() const;

    void onLoad(CallbackFn func) noexcept;
    void onUnload(CallbackFn func) noexcept;
    void onEnable(CallbackFn func) noexcept;
    void onDisable(CallbackFn func) noexcept;
};

std::filesystem::path const& getModsRoot();
}

Manifest

Mod metadata loaded from manifest.json.

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
namespace ll::mod {
struct Dependency {
    std::string                             name;
    std::optional<data::VersionRequirement> version;
};

struct Manifest {
    std::string                                entry;
    std::string                                name;
    std::string                                type;
    std::optional<std::string>                 platform;
    std::optional<bool>                        passive;
    std::optional<data::Version>               version;
    std::optional<std::string>                 author;
    std::optional<std::string>                 description;
    std::optional<SmallStringMap<std::string>> extraInfo;
    std::optional<SmallDenseSet<Dependency>>   dependencies;
    std::optional<SmallDenseSet<Dependency>>   optionalDependencies;
    std::optional<SmallDenseSet<Dependency>>   conflicts;
    std::optional<SmallDenseSet<Dependency>>   loadBefore;
};
}

Dependency entries keep the existing manifest JSON shape:

JSON
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
  "dependencies": [
    { "name": "RequiredMod", "version": "^1.2.3" }
  ],
  "optionalDependencies": [
    { "name": "OptionalIntegration", "version": ">=2.0.0 <3.0.0" }
  ],
  "loadBefore": [
    { "name": "LateInitializer" }
  ]
}

Omitting a dependency's version accepts any declared version, including an unversioned target. If a range is present, the target must declare a matching version. See Data for the complete range syntax. A bare full version such as 1.2.3 temporarily keeps the legacy meaning >=1.2.3 <2.0.0 and emits a migration warning; use =1.2.3 for an exact match.

Dependency Semantics

Manifest field Load ordering Load failure Disable and unload protection
dependencies Dependency first Blocks the dependent Yes
optionalDependencies Matching available dependency first Does not block the consumer Yes, when bound at load time
loadBefore Declaring mod first Does not create a dependency No

loadBefore is only a startup and bulk-operation ordering constraint. It never prevents either mod from being disabled or unloaded independently. Dynamic loading does not reorder already loaded mods; if a new mod asks to load before an already loaded target, LeviLamina logs a warning and continues. Optional dependencies are bound only when their consumer loads, so loading an optional provider later does not retroactively add lifecycle protection.

Usage

Accessing Mod Instance

C++
1
2
3
4
5
#include "ll/api/mod/NativeMod.h"

ll::mod::NativeMod& getSelfMod() {
    return ll::mod::NativeMod::current();
}

Using Mod Resources

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include "ll/api/mod/Mod.h"

void setupMod(ll::mod::Mod& mod) {
    auto& logger = mod.getLogger();
    logger.info("Mod name: {}", mod.getName());

    auto configPath = mod.getConfigDir() / "config.json";
    auto dataPath = mod.getDataDir() / "database.db";
    auto langPath = mod.getLangDir() / "en_US.json";
}

Lifecycle Callbacks

C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include "ll/api/mod/Mod.h"

void registerCallbacks(ll::mod::Mod& mod) {
    mod.onLoad([](ll::mod::Mod& self) -> bool {
        self.getLogger().info("Mod loading");
        return true;  // Return false to abort load
    });

    mod.onEnable([](ll::mod::Mod& self) -> bool {
        self.getLogger().info("Mod enabled");
        return true;
    });

    mod.onDisable([](ll::mod::Mod& self) -> bool {
        self.getLogger().info("Mod disabled");
        return true;
    });

    mod.onUnload([](ll::mod::Mod& self) -> bool {
        self.getLogger().info("Mod unloading");
        return true;
    });
}

Directory Structure

Each mod has the following directory structure:

Text Only
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
mods/
└── MyMod/
    β”œβ”€β”€ manifest.json          # Mod metadata
    β”œβ”€β”€ MyMod.dll              # Mod binary
    β”œβ”€β”€ config/                # Config files (getConfigDir())
    β”œβ”€β”€ data/                  # Persistent data (getDataDir())
    β”œβ”€β”€ lang/                  # Translations (getLangDir())
    β”œβ”€β”€ resources/             # Resource packs (getResourceDir())
    └── behavior/              # Behavior packs (getBehaviorDir())

worlds/
└── Bedrock level/
    └── plugins/
        └── MyMod/
            β”œβ”€β”€ config/        # World-specific config (getWorldConfigDir())
            └── data/          # World-specific data (getWorldDataDir())
  • Config β€” Use getConfigDir() for config file location
  • Data β€” Use getDataDir() for database storage
  • I18n β€” Use getLangDir() for translation files
  • IO & Logger β€” getLogger() returns the mod's logger