Skip to content

Commit

Permalink
Add sol2 for Lua <-> C++ bindings
Browse files Browse the repository at this point in the history
  • Loading branch information
glebm committed Oct 15, 2023
1 parent ad3a206 commit 72b49ab
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 65 deletions.
13 changes: 13 additions & 0 deletions 3rdParty/sol2/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
include(functions/FetchContent_MakeAvailableExcludeFromAll)

set(SOL2_ENABLE_INSTALL OFF)

include(FetchContent)
FetchContent_Declare(sol2
URL https://github.com/ThePhD/sol2/archive/9c882a28fdb6f4ad79a53a4191b43ce48a661175.tar.gz
URL_HASH MD5=2637c3fcdcce3ff34b36437c1d3b99d1
)
FetchContent_MakeAvailableExcludeFromAll(sol2)

target_include_directories(sol2 SYSTEM BEFORE INTERFACE ${CMAKE_CURRENT_LIST_DIR}/sol_config)
target_compile_definitions(sol2 INTERFACE -DSOL_NO_EXCEPTIONS)
8 changes: 8 additions & 0 deletions 3rdParty/sol2/sol_config/sol/config.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#pragma once

#define SOL_SAFE_USERTYPE 1
#define SOL_SAFE_REFERENCES 1
#define SOL_SAFE_FUNCTION_CALLS 1
#define SOL_SAFE_FUNCTION 1
#define SOL_NO_NIL 0
#define SOL_IN_DEBUG_DETECTED 0
36 changes: 36 additions & 0 deletions 3rdParty/sol2/sol_config/sol/debug.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#pragma once

// sol2 uses std::cout for debug logging by default.
// We want to use SDL logging instead for better compatibility.

#include <cstddef>
#include <string>

#include <sol/stack.hpp>

namespace devilutionx {
void Sol2DebugPrintStack(lua_State *L);
void Sol2DebugPrintSection(const std::string &message, lua_State *L);
} // namespace devilutionx

namespace sol::detail::debug {

inline std::string dump_types(lua_State *L) {
std::string visual;
std::size_t size = lua_gettop(L) + 1;
for (std::size_t i = 1; i < size; ++i) {
if (i != 1) {
visual += " | ";
}
visual += type_name(L, stack::get<type>(L, static_cast<int>(i)));
}
return visual;
}

inline void print_stack(lua_State *L) { ::devilutionx::Sol2DebugPrintStack(L); }

inline void print_section(const std::string &message, lua_State *L) {
::devilutionx::Sol2DebugPrintSection(message, L);
}

} // namespace sol::detail::debug
4 changes: 2 additions & 2 deletions CMake/Dependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ endif()
find_package(Lua 5.4 QUIET)
if(LUA_FOUND)
message("-- Found Lua ${LUA_VERSION_STRING}")
# No clear way to statically link from the system lib
set(DEVILUTIONX_STATIC_LUA OFF)
else()
if(NOT DEFINED DEVILUTIONX_SYSTEM_LUA)
message("-- Suitable system Lua package not found, will use Lua from source")
Expand All @@ -48,6 +46,8 @@ else()
include_directories(${LUA_INCLUDE_DIR})
endif()

add_subdirectory(3rdParty/sol2)

if(SCREEN_READER_INTEGRATION)
if(WIN32)
add_subdirectory(3rdParty/tolk)
Expand Down
2 changes: 1 addition & 1 deletion Source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ if(DISCORD_INTEGRATION)
target_link_libraries(libdevilutionx PRIVATE discord discord_game_sdk)
endif()

target_link_libraries(libdevilutionx PRIVATE ${LUA_LIBRARIES})
target_link_libraries(libdevilutionx PRIVATE ${LUA_LIBRARIES} sol2::sol2)

if(SCREEN_READER_INTEGRATION AND WIN32)
target_compile_definitions(libdevilutionx PRIVATE Tolk)
Expand Down
125 changes: 65 additions & 60 deletions Source/utils/lua.cpp
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
#include "utils/lua.hpp"

#include <optional>
#include <string_view>

extern "C" {
#include "lauxlib.h"
#include "lua.h"
#include "lualib.h"
}
#include <sol/sol.hpp>

#include "engine/assets.hpp"
#include "plrmsg.h"
#include "utils/console.h"
#include "utils/log.hpp"

namespace devilution {

namespace {

lua_State *LuaState;
std::optional<sol::state> luaState;

int LuaPrint(lua_State *state)
{
Expand All @@ -41,14 +39,27 @@ int LuaPlayerMessage(lua_State *state)
return 0;
}

bool CheckResult(sol::protected_function_result result)
{
const bool valid = result.valid();
if (!valid) {
if (result.get_type() == sol::type::string) {
LogError("Lua error: {}", result.get<std::string>());
} else {
LogError("Unknown Lua error");
}
}
return valid;
}

void RunScript(std::string_view path)
{
AssetRef ref = FindAsset(path);
if (!ref.ok())
return;

const size_t size = ref.size();
std::unique_ptr<char[]> luaScript { new char[size + 1] };
std::unique_ptr<char[]> luaScript { new char[size] };

AssetHandle handle = OpenAsset(std::move(ref));
if (!handle.ok())
Expand All @@ -57,52 +68,53 @@ void RunScript(std::string_view path)
if (size > 0 && !handle.read(luaScript.get(), size))
return;

luaScript[size] = '\0'; // Terminate string
const std::string_view luaScriptStr(luaScript.get(), size);
CheckResult(luaState->safe_script(luaScriptStr));
}

int status = luaL_loadstring(LuaState, luaScript.get());
if (status == LUA_OK)
status = lua_pcall(LuaState, 0, 0, 0);
if (status != LUA_OK)
SDL_Log("%s", lua_tostring(LuaState, -1));
void LuaPanic(sol::optional<std::string> maybe_msg)
{
LogError("Lua is in a panic state and will now abort() the application:\n",
maybe_msg.value_or("unknown error"));
}

} // namespace

void Sol2DebugPrintStack(lua_State *L)
{
LogDebug("{}", sol::detail::debug::dump_types(L));
}

void Sol2DebugPrintSection(const std::string &message, lua_State *L)
{
LogDebug("-- {} -- [ {} ]", message, sol::detail::debug::dump_types(L));
}

void LuaInitialize()
{
LuaState = luaL_newstate();

// Load libraries
luaL_requiref(LuaState, LUA_GNAME, luaopen_base, 1);
lua_pop(LuaState, 1);
luaL_requiref(LuaState, LUA_LOADLIBNAME, luaopen_package, 1);
lua_pop(LuaState, 1);
luaL_requiref(LuaState, LUA_COLIBNAME, luaopen_coroutine, 1);
lua_pop(LuaState, 1);
luaL_requiref(LuaState, LUA_TABLIBNAME, luaopen_table, 1);
lua_pop(LuaState, 1);
luaL_requiref(LuaState, LUA_STRLIBNAME, luaopen_string, 1);
lua_pop(LuaState, 1);
luaL_requiref(LuaState, LUA_MATHLIBNAME, luaopen_math, 1);
lua_pop(LuaState, 1);
luaL_requiref(LuaState, LUA_UTF8LIBNAME, luaopen_utf8, 1);
lua_pop(LuaState, 1);
luaState.emplace(sol::c_call<decltype(&LuaPanic), &LuaPanic>);
sol::state &lua = *luaState;
lua.open_libraries(
sol::lib::base,
sol::lib::package,
sol::lib::coroutine,
sol::lib::table,
sol::lib::string,
sol::lib::math,
sol::lib::utf8);

#ifdef _DEBUG
luaL_requiref(LuaState, LUA_DBLIBNAME, luaopen_debug, 1);
lua_pop(LuaState, 1);
lua.open_libraries(sol::lib::debug);
#endif

// Registering globals
lua_register(LuaState, "print", LuaPrint);
lua_pushstring(LuaState, LUA_VERSION);
lua_setglobal(LuaState, "_VERSION");
lua["print"] = LuaPrint;
lua["_VERSION"] = LUA_VERSION;

// Registering devilutionx object table
lua_newtable(LuaState);
lua_pushcfunction(LuaState, LuaPlayerMessage);
lua_setfield(LuaState, -2, "message");
lua_setglobal(LuaState, "devilutionx");
sol::table devilutionx(lua, sol::create);
devilutionx["message"] = LuaPlayerMessage;
lua["devilutionx"] = devilutionx;

RunScript("lua/init.lua");
RunScript("lua/user.lua");
Expand All @@ -112,36 +124,29 @@ void LuaInitialize()

void LuaShutdown()
{
if (LuaState == nullptr)
return;

lua_close(LuaState);
luaState = std::nullopt;
}

void LuaEvent(std::string name)
void LuaEvent(std::string_view name)
{
lua_getglobal(LuaState, "Events");
if (!lua_istable(LuaState, -1)) {
lua_pop(LuaState, 1);
SDL_Log("Events table missing!");
sol::state &lua = *luaState;
const sol::object events = lua["Events"];
if (!events.is<sol::table>()) {
LogError("Events table missing!");
return;
}
lua_getfield(LuaState, -1, name.c_str());
if (!lua_istable(LuaState, -1)) {
lua_pop(LuaState, 2);
SDL_Log("Events.%s event not registered", name.c_str());
const sol::object event = events.as<sol::table>()[name];
if (!event.is<sol::table>()) {
LogError("Events.{} event not registered", name);
return;
}
lua_getfield(LuaState, -1, "Trigger");
if (!lua_isfunction(LuaState, -1)) {
lua_pop(LuaState, 3);
SDL_Log("Events.%s.Trigger is not a function", name.c_str());
const sol::object trigger = event.as<sol::table>()["Trigger"];
if (!trigger.is<sol::function>()) {
LogError("Events.{}.Trigger is not a function", name);
return;
}
if (lua_pcall(LuaState, 0, 0, 0) != LUA_OK) {
SDL_Log("%s", lua_tostring(LuaState, -1));
}
lua_pop(LuaState, 2);
const sol::protected_function fn = trigger.as<sol::protected_function>();
CheckResult(fn());
}

} // namespace devilution
4 changes: 2 additions & 2 deletions Source/utils/lua.hpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#pragma once

#include <string>
#include <string_view>

namespace devilution {

void LuaInitialize();
void LuaShutdown();
void LuaEvent(std::string name);
void LuaEvent(std::string_view name);

} // namespace devilution

0 comments on commit 72b49ab

Please sign in to comment.