Skip to content

Commit

Permalink
Lua mod ini file configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
StephenCWills authored and AJenbo committed Jan 8, 2025
1 parent affd7e8 commit 4931cc6
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 17 deletions.
25 changes: 17 additions & 8 deletions Source/lua/lua.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "lua/modules/audio.hpp"
#include "lua/modules/log.hpp"
#include "lua/modules/render.hpp"
#include "options.h"
#include "plrmsg.h"
#include "utils/console.h"
#include "utils/log.hpp"
Expand Down Expand Up @@ -193,6 +194,20 @@ sol::environment CreateLuaSandbox()
return sandbox;
}

void LuaReloadActiveMods()
{
// Loaded without a sandbox.
CurrentLuaState->events = RunScript(/*env=*/std::nullopt, "devilutionx.events", /*optional=*/false);
CurrentLuaState->commonPackages["devilutionx.events"] = CurrentLuaState->events;

for (std::string_view modname : sgOptions.Mods.GetActiveModList()) {
std::string packageName = StrCat("mods.", modname, ".init");
RunScript(CreateLuaSandbox(), packageName, /*optional=*/true);
}

LuaEvent("LoadModsComplete");
}

void LuaInitialize()
{
CurrentLuaState.emplace(LuaState { .sol = { sol::c_call<decltype(&LuaPanic), &LuaPanic> } });
Expand All @@ -212,9 +227,6 @@ void LuaInitialize()
// Registering devilutionx object table
SafeCallResult(lua.safe_script(RequireGenSrc), /*optional=*/false);

// Loaded without a sandbox.
CurrentLuaState->events = RunScript(/*env=*/std::nullopt, "devilutionx.events", /*optional=*/false);

CurrentLuaState->commonPackages = lua.create_table_with(
#ifdef _DEBUG
"devilutionx.dev", LuaDevModule(lua),
Expand All @@ -224,16 +236,13 @@ void LuaInitialize()
"devilutionx.audio", LuaAudioModule(lua),
"devilutionx.render", LuaRenderModule(lua),
"devilutionx.message", [](std::string_view text) { EventPlrMsg(text, UiFlags::ColorRed); },
// These packages are loaded without a sandbox:
"devilutionx.events", CurrentLuaState->events,
// This package is loaded without a sandbox:
"inspect", RunScript(/*env=*/std::nullopt, "inspect", /*optional=*/false));

// Used by the custom require implementation.
lua["setEnvironment"] = [](const sol::environment &env, const sol::function &fn) { sol::set_environment(env, fn); };

RunScript(CreateLuaSandbox(), "user", /*optional=*/true);

LuaEvent("GameBoot");
LuaReloadActiveMods();
}

void LuaShutdown()
Expand Down
1 change: 1 addition & 0 deletions Source/lua/lua.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
namespace devilution {

void LuaInitialize();
void LuaReloadActiveMods();
void LuaShutdown();
void LuaEvent(std::string_view name);
sol::state &GetLuaState();
Expand Down
46 changes: 46 additions & 0 deletions Source/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1787,6 +1787,52 @@ bool PadmapperOptions::CanDeferToMovementHandler(const Action &action) const
ControllerButton_BUTTON_DPAD_RIGHT);
}

ModOptions::ModOptions()
: OptionCategoryBase("Mods", N_("Mods"), N_("Mod Settings"))
{
}

std::vector<std::string_view> ModOptions::GetActiveModList()
{
std::vector<std::string_view> modList;
for (auto &modEntry : GetModEntries()) {
if (*modEntry.enabled)
modList.emplace_back(modEntry.name);
}
return modList;
}

std::vector<std::string_view> ModOptions::GetModList()
{
std::vector<std::string_view> modList;
for (auto &modEntry : GetModEntries()) {
modList.emplace_back(modEntry.name);
}
return modList;
}

std::vector<OptionEntryBase *> ModOptions::GetEntries()
{
std::vector<OptionEntryBase *> optionEntries;
for (auto &modEntry : GetModEntries()) {
optionEntries.emplace_back(&modEntry.enabled);
}
return optionEntries;
}

std::vector<ModOptions::ModEntry> &ModOptions::GetModEntries()
{
if (modEntries)
return *modEntries;

std::vector<std::string> modNames = ini->getKeys(name);
std::vector<ModOptions::ModEntry> &newModEntries = modEntries.emplace();
for (auto &modName : modNames) {
newModEntries.emplace_back(modName);
}
return newModEntries;
}

namespace {
#ifdef DEVILUTIONX_RESAMPLER_SPEEX
constexpr char ResamplerSpeex[] = "Speex";
Expand Down
24 changes: 24 additions & 0 deletions Source/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,28 @@ struct PadmapperOptions : OptionCategoryBase {
bool CanDeferToMovementHandler(const Action &action) const;
};

struct ModOptions : OptionCategoryBase {
ModOptions();
std::vector<std::string_view> GetActiveModList();
std::vector<std::string_view> GetModList();
std::vector<OptionEntryBase *> GetEntries() override;

private:
struct ModEntry {
ModEntry(std::string_view name)
: name(name)
, enabled(this->name, OptionEntryFlags::None, this->name.c_str(), "", false)
{
}

std::string name;
OptionEntryBoolean enabled;
};

std::vector<ModEntry> &GetModEntries();
std::optional<std::vector<ModEntry>> modEntries;
};

struct Options {
GameModeOptions GameMode;
StartUpOptions StartUp;
Expand All @@ -817,6 +839,7 @@ struct Options {
LanguageOptions Language;
KeymapperOptions Keymapper;
PadmapperOptions Padmapper;
ModOptions Mods;

[[nodiscard]] std::vector<OptionCategoryBase *> GetCategories()
{
Expand All @@ -834,6 +857,7 @@ struct Options {
&Chat,
&Keymapper,
&Padmapper,
&Mods,
};
}
};
Expand Down
28 changes: 22 additions & 6 deletions Source/utils/ini.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ namespace devilution {

namespace {

template <typename T>
bool OrderByValueIndex(const std::pair<std::string, T> &a, const std::pair<std::string, T> &b)
{
return a.second.index < b.second.index;
};

// Returns a pointer to the first non-leading whitespace.
// Only ' ' and '\t' are considered whitespace.
// Requires: begin <= end.
Expand Down Expand Up @@ -74,6 +80,22 @@ Ini::Values::Values(const Value &data)
{
}

std::vector<std::string> Ini::getKeys(std::string_view section) const
{
const auto sectionIt = data_.sections.find(section);
if (sectionIt == data_.sections.end()) return {};

std::vector<std::pair<std::string, ValuesData>> entries(sectionIt->second.entries.begin(), sectionIt->second.entries.end());
c_sort(entries, OrderByValueIndex<ValuesData>);

std::vector<std::string> keys;
keys.reserve(entries.size());
for (const auto &[key, _] : entries) {
keys.push_back(key);
}
return keys;
}

std::span<const Ini::Value> Ini::Values::get() const
{
if (std::holds_alternative<Ini::Value>(rep_)) {
Expand Down Expand Up @@ -367,12 +389,6 @@ void Ini::set(std::string_view section, std::string_view key, float value)

namespace {

template <typename T>
bool OrderByValueIndex(const std::pair<std::string, T> &a, const std::pair<std::string, T> &b)
{
return a.second.index < b.second.index;
};

// Appends a possibly multi-line comment, converting \n to \r\n.
void AppendComment(std::string_view comment, std::string &out)
{
Expand Down
3 changes: 3 additions & 0 deletions Source/utils/ini.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ class Ini {
static tl::expected<Ini, std::string> parse(std::string_view buffer);
[[nodiscard]] std::string serialize() const;

/** @return all the keys associated with this section in the ini */
[[nodiscard]] std::vector<std::string> getKeys(std::string_view section) const;

/** @return all the values associated with this section and key in the ini */
[[nodiscard]] std::span<const Value> get(std::string_view section, std::string_view key) const;

Expand Down
6 changes: 3 additions & 3 deletions assets/lua/devilutionx/events.lua
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ local function CreateEvent()
end

local events = {
---Called early on game boot.
GameBoot = CreateEvent(),
__doc_GameBoot = "Called early on game boot.",
---Called after all mods have been loaded.
LoadModsComplete = CreateEvent(),
__doc_LoadModsComplete = "Called after all mods have been loaded.",

---Called every time a new game is started.
GameStart = CreateEvent(),
Expand Down

0 comments on commit 4931cc6

Please sign in to comment.