Browse Source

Lua mod ini file configuration

pull/7624/head
staphen 1 year ago committed by Anders Jenbo
parent
commit
4931cc6386
  1. 25
      Source/lua/lua.cpp
  2. 1
      Source/lua/lua.hpp
  3. 46
      Source/options.cpp
  4. 24
      Source/options.h
  5. 28
      Source/utils/ini.cpp
  6. 3
      Source/utils/ini.hpp
  7. 6
      assets/lua/devilutionx/events.lua

25
Source/lua/lua.cpp

@ -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"
@ -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> } });
@ -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),
@ -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()

1
Source/lua/lua.hpp

@ -8,6 +8,7 @@
namespace devilution {
void LuaInitialize();
void LuaReloadActiveMods();
void LuaShutdown();
void LuaEvent(std::string_view name);
sol::state &GetLuaState();

46
Source/options.cpp

@ -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";

24
Source/options.h

@ -803,6 +803,28 @@ private:
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;
@ -817,6 +839,7 @@ struct Options {
LanguageOptions Language;
KeymapperOptions Keymapper;
PadmapperOptions Padmapper;
ModOptions Mods;
[[nodiscard]] std::vector<OptionCategoryBase *> GetCategories()
{
@ -834,6 +857,7 @@ struct Options {
&Chat,
&Keymapper,
&Padmapper,
&Mods,
};
}
};

28
Source/utils/ini.cpp

@ -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.
@ -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_)) {
@ -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)
{

3
Source/utils/ini.hpp

@ -54,6 +54,9 @@ public:
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;

6
assets/lua/devilutionx/events.lua

@ -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(),

Loading…
Cancel
Save