From 702e54789c6d16cd6feb0e6457477f85a32410ce Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Sat, 7 Oct 2023 02:54:16 +0200 Subject: [PATCH] Add Lua support --- 3rdParty/Lua/CMakeLists.txt | 24 ++++ CMake/Assets.cmake | 1 + CMake/Dependencies.cmake | 24 ++++ Packaging/resources/assets/lua/init.lua | 26 +++++ Source/CMakeLists.txt | 3 + Source/diablo.cpp | 4 + Source/utils/lua.cpp | 147 ++++++++++++++++++++++++ Source/utils/lua.hpp | 11 ++ test/timedemo_test.cpp | 2 + uwp-project/devilutionx.vcxproj | 8 +- vcpkg.json | 3 +- 11 files changed, 248 insertions(+), 5 deletions(-) create mode 100644 3rdParty/Lua/CMakeLists.txt create mode 100644 Packaging/resources/assets/lua/init.lua create mode 100644 Source/utils/lua.cpp create mode 100644 Source/utils/lua.hpp diff --git a/3rdParty/Lua/CMakeLists.txt b/3rdParty/Lua/CMakeLists.txt new file mode 100644 index 00000000000..4d998ad303f --- /dev/null +++ b/3rdParty/Lua/CMakeLists.txt @@ -0,0 +1,24 @@ +include(functions/FetchContent_MakeAvailableExcludeFromAll) + +set(LUA_ENABLE_TESTING OFF) +set(LUA_BUILD_COMPILER OFF) +if(DEVILUTIONX_STATIC_LUA) + set(LUA_ENABLE_SHARED OFF) +else() + set(LUA_ENABLE_SHARED ON) +endif() + +include(FetchContent) +FetchContent_Declare(Lua + URL https://github.com/walterschell/Lua/archive/88246d621abf7b6fba9332f49229d507f020e450.tar.gz + URL_HASH MD5=03b76927cb5341ffc53bea12c37ddcca +) +FetchContent_MakeAvailableExcludeFromAll(Lua) + +if(ANDROID AND ("${ANDROID_ABI}" STREQUAL "armeabi-v7a" OR "${ANDROID_ABI}" STREQUAL "x86")) + target_compile_definitions(lua_internal INTERFACE -DLUA_USE_C89) +elseif(NINTENDO_3DS OR VITA OR NINTENDO_SWITCH OR NXDK) + target_compile_definitions(lua_static PUBLIC -DLUA_USE_C89) +elseif(IOS) + target_compile_definitions(lua_static PUBLIC -DLUA_USE_IOS) +endif() diff --git a/CMake/Assets.cmake b/CMake/Assets.cmake index 2b843784363..af0cf737ea3 100644 --- a/CMake/Assets.cmake +++ b/CMake/Assets.cmake @@ -137,6 +137,7 @@ set(devilutionx_assets levels/l2data/bonechat.dun levels/towndata/automap.dun levels/towndata/automap.amp + lua/init.lua nlevels/cutl5w.clx nlevels/cutl6w.clx nlevels/l5data/cornerstone.dun diff --git a/CMake/Dependencies.cmake b/CMake/Dependencies.cmake index da00a27b461..9e6894cccfc 100644 --- a/CMake/Dependencies.cmake +++ b/CMake/Dependencies.cmake @@ -24,6 +24,30 @@ if(SUPPORTS_MPQ) endif() 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") + set(DEVILUTIONX_SYSTEM_LUA OFF) + endif() +endif() +dependency_options("lua" DEVILUTIONX_SYSTEM_LUA ON DEVILUTIONX_STATIC_LUA) +if(NOT DEVILUTIONX_SYSTEM_LUA) + add_subdirectory(3rdParty/Lua) + if(DEVILUTIONX_STATIC_LUA) + set(LUA_LIBRARIES lua_static) + else() + set(LUA_LIBRARIES lua_shared) + endif() +else() + find_package(Lua 5.4 REQUIRED) + include_directories(${LUA_INCLUDE_DIR}) +endif() + if(SCREEN_READER_INTEGRATION) if(WIN32) add_subdirectory(3rdParty/tolk) diff --git a/Packaging/resources/assets/lua/init.lua b/Packaging/resources/assets/lua/init.lua new file mode 100644 index 00000000000..4997ebd2201 --- /dev/null +++ b/Packaging/resources/assets/lua/init.lua @@ -0,0 +1,26 @@ +Events = {} + +function Events:RegisterEvent(eventName) + self[eventName] = { + Functions = {}, + Add = function(func) + table.insert(self[eventName].Functions, func) + end, + Remove = function(func) + for i, f in ipairs(self[eventName].Functions) do + if f == func then + table.remove(self[eventName].Functions, i) + break + end + end + end, + Trigger = function() + for _, func in ipairs(self[eventName].Functions) do + func() + end + end, + } +end + +Events:RegisterEvent("OnGameBoot") +Events:RegisterEvent("OnGameStart") diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index a027ccdd3b6..b4baa18cbf3 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -159,6 +159,7 @@ set(libdevilutionx_SRCS utils/format_int.cpp utils/language.cpp utils/logged_fstream.cpp + utils/lua.cpp utils/paths.cpp utils/parse_int.cpp utils/pcx_to_clx.cpp @@ -287,6 +288,8 @@ if(DISCORD_INTEGRATION) target_link_libraries(libdevilutionx PRIVATE discord discord_game_sdk) endif() +target_link_libraries(libdevilutionx PRIVATE ${LUA_LIBRARIES}) + if(SCREEN_READER_INTEGRATION AND WIN32) target_compile_definitions(libdevilutionx PRIVATE Tolk) endif() diff --git a/Source/diablo.cpp b/Source/diablo.cpp index fc16323bc56..52f772807fc 100644 --- a/Source/diablo.cpp +++ b/Source/diablo.cpp @@ -81,6 +81,7 @@ #include "utils/console.h" #include "utils/display.h" #include "utils/language.h" +#include "utils/lua.hpp" #include "utils/parse_int.hpp" #include "utils/paths.h" #include "utils/screen_reader.hpp" @@ -822,6 +823,7 @@ void RunGameLoop(interface_mode uMsg) nthread_ignore_mutex(false); discord_manager::StartGame(); + LuaEvent("OnGameStart"); #ifdef GPERF_HEAP_FIRST_GAME_ITERATION unsigned run_game_iteration = 0; #endif @@ -1230,6 +1232,7 @@ void DiabloDeinit() { FreeItemGFX(); + LuaShutdown(); ShutDownScreenReader(); if (gbSndInited) @@ -2449,6 +2452,7 @@ int DiabloMain(int argc, char **argv) LoadLanguageArchive(); ApplicationInit(); + LuaInitialize(); SaveOptions(); // Finally load game data diff --git a/Source/utils/lua.cpp b/Source/utils/lua.cpp new file mode 100644 index 00000000000..fb887c2a1eb --- /dev/null +++ b/Source/utils/lua.cpp @@ -0,0 +1,147 @@ +#include "utils/lua.hpp" + +#include + +extern "C" { +#include "lauxlib.h" +#include "lua.h" +#include "lualib.h" +} + +#include "engine/assets.hpp" +#include "plrmsg.h" +#include "utils/console.h" + +namespace devilution { + +namespace { + +lua_State *LuaState; + +int LuaPrint(lua_State *state) +{ + int nargs = lua_gettop(state); + if (nargs >= 1 && lua_isstring(state, 1)) { + std::string msg = lua_tostring(state, 1); + msg += "\n"; + printInConsole(msg); + } + + return 0; +} + +int LuaPlayerMessage(lua_State *state) +{ + int nargs = lua_gettop(state); + if (nargs >= 1 && lua_isstring(state, 1)) { + std::string_view msg = lua_tostring(state, 1); + EventPlrMsg(msg, UiFlags::ColorRed); + } + + return 0; +} + +void RunScript(std::string_view path) +{ + AssetRef ref = FindAsset(path); + if (!ref.ok()) + return; + + const size_t size = ref.size(); + std::unique_ptr luaScript { new char[size + 1] }; + + AssetHandle handle = OpenAsset(std::move(ref)); + if (!handle.ok()) + return; + + if (size > 0 && !handle.read(luaScript.get(), size)) + return; + + luaScript[size] = '\0'; // Terminate string + + 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)); +} + +} // namespace + +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); + +#ifdef _DEBUG + luaL_requiref(LuaState, LUA_DBLIBNAME, luaopen_debug, 1); + lua_pop(LuaState, 1); +#endif + + // Registering globals + lua_register(LuaState, "print", LuaPrint); + lua_pushstring(LuaState, LUA_VERSION); + lua_setglobal(LuaState, "_VERSION"); + + // Registering devilutionx object table + lua_newtable(LuaState); + lua_pushcfunction(LuaState, LuaPlayerMessage); + lua_setfield(LuaState, -2, "message"); + lua_setglobal(LuaState, "devilutionx"); + + RunScript("lua/init.lua"); + RunScript("lua/user.lua"); + + LuaEvent("OnGameBoot"); +} + +void LuaShutdown() +{ + if (LuaState == nullptr) + return; + + lua_close(LuaState); +} + +void LuaEvent(std::string name) +{ + lua_getglobal(LuaState, "Events"); + if (!lua_istable(LuaState, -1)) { + lua_pop(LuaState, 1); + SDL_Log("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()); + 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()); + return; + } + if (lua_pcall(LuaState, 0, 0, 0) != LUA_OK) { + SDL_Log("%s", lua_tostring(LuaState, -1)); + } + lua_pop(LuaState, 2); +} + +} // namespace devilution diff --git a/Source/utils/lua.hpp b/Source/utils/lua.hpp new file mode 100644 index 00000000000..a66a5251f85 --- /dev/null +++ b/Source/utils/lua.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace devilution { + +void LuaInitialize(); +void LuaShutdown(); +void LuaEvent(std::string name); + +} // namespace devilution diff --git a/test/timedemo_test.cpp b/test/timedemo_test.cpp index 8102fa493e6..ca1ddfdd6e1 100644 --- a/test/timedemo_test.cpp +++ b/test/timedemo_test.cpp @@ -8,6 +8,7 @@ #include "pfile.h" #include "playerdat.hpp" #include "utils/display.h" +#include "utils/lua.hpp" #include "utils/paths.h" using namespace devilution; @@ -33,6 +34,7 @@ void RunTimedemo(std::string timedemoFolderName) InitKeymapActions(); LoadOptions(); + LuaInitialize(); const int demoNumber = 0; diff --git a/uwp-project/devilutionx.vcxproj b/uwp-project/devilutionx.vcxproj index b63e5404d87..81689119674 100644 --- a/uwp-project/devilutionx.vcxproj +++ b/uwp-project/devilutionx.vcxproj @@ -73,8 +73,8 @@ - sdl_image.lib;libpng16_staticd.lib;pkware.lib;fmtd.lib;zlibstatic.lib;bzip2.lib;libsmackerdec.lib;libmpq.lib;libdevilutionx.lib;sdl2.lib;sdl_audiolib.lib;asio.lib;sodium.lib;zt.lib;lwip_pic.lib;miniupnpc_pic.lib;natpmp_pic.lib;zt_pic.lib;zto_pic.lib;shlwapi.lib;shell32.lib;%(AdditionalDependencies) - ..\build\SDL\VisualC-WinRT\x64\Debug\SDL-UWP;..\build\3rdParty\SDL_image\Debug;..\build\_deps\zlib-build\Debug;..\build\3rdParty\PKWare\Debug;..\build\3rdParty\bzip2\Debug;..\build\3rdParty\libsmackerdec\Debug;..\build\3rdParty\libmpq\Debug;..\build\_deps\sdl_audiolib-build\Debug;..\build\3rdParty\asio\Release;..\build\_deps\libsodium-build\Debug;..\build\_deps\libzt-build\lib\Debug;..\build\_deps\libfmt-build\Debug;..\build\_deps\libpng-build\Debug;..\build\Source\libdevilutionx.dir\Debug;%(AdditionalLibraryDirectories) + sdl_image.lib;libpng16_staticd.lib;pkware.lib;fmtd.lib;zlibstatic.lib;bzip2.lib;libsmackerdec.lib;libmpq.lib;libdevilutionx.lib;lua_static.lib;sdl2.lib;sdl_audiolib.lib;asio.lib;sodium.lib;zt.lib;lwip_pic.lib;miniupnpc_pic.lib;natpmp_pic.lib;zt_pic.lib;zto_pic.lib;shlwapi.lib;shell32.lib;%(AdditionalDependencies) + ..\build\SDL\VisualC-WinRT\x64\Debug\SDL-UWP;..\build\3rdParty\SDL_image\Debug;..\build\_deps\zlib-build\Debug;..\build\3rdParty\PKWare\Debug;..\build\3rdParty\bzip2\Debug;..\build\3rdParty\libsmackerdec\Debug;..\build\3rdParty\libmpq\Debug;..\build\_deps\lua-build\lua-5.4.6\Debug;..\build\_deps\sdl_audiolib-build\Debug;..\build\3rdParty\asio\Debug;..\build\_deps\libsodium-build\Debug;..\build\_deps\libzt-build\lib\Debug;..\build\_deps\libfmt-build\Debug;..\build\_deps\libpng-build\Debug;..\build\Source\libdevilutionx.dir\Debug;%(AdditionalLibraryDirectories) pch.h @@ -92,8 +92,8 @@ - sdl_image.lib;libpng16_static.lib;pkware.lib;fmt.lib;zlibstatic.lib;bzip2.lib;libsmackerdec.lib;libmpq.lib;libdevilutionx.lib;sdl2.lib;sdl_audiolib.lib;asio.lib;sodium.lib;zt.lib;lwip_pic.lib;miniupnpc_pic.lib;natpmp_pic.lib;zt_pic.lib;zto_pic.lib;shlwapi.lib;shell32.lib;%(AdditionalDependencies) - ..\build\SDL\VisualC-WinRT\x64\Release\SDL-UWP;..\build\3rdParty\SDL_image\Release;..\build\_deps\zlib-build\Release;..\build\3rdParty\PKWare\Release;..\build\3rdParty\bzip2\Release;..\build\3rdParty\libsmackerdec\Release;..\build\3rdParty\libmpq\Release;..\build\_deps\sdl_audiolib-build\Release;..\build\3rdParty\asio\Release;..\build\_deps\libsodium-build\Release;..\build\_deps\libzt-build\lib\Release;..\build\_deps\libfmt-build\Release;..\build\_deps\libpng-build\Release;..\build\Source\libdevilutionx.dir\Release;%(AdditionalLibraryDirectories) + sdl_image.lib;libpng16_static.lib;pkware.lib;fmt.lib;zlibstatic.lib;bzip2.lib;libsmackerdec.lib;libmpq.lib;libdevilutionx.lib;lua_static.lib;sdl2.lib;sdl_audiolib.lib;asio.lib;sodium.lib;zt.lib;lwip_pic.lib;miniupnpc_pic.lib;natpmp_pic.lib;zt_pic.lib;zto_pic.lib;shlwapi.lib;shell32.lib;%(AdditionalDependencies) + ..\build\SDL\VisualC-WinRT\x64\Release\SDL-UWP;..\build\3rdParty\SDL_image\Release;..\build\_deps\zlib-build\Release;..\build\3rdParty\PKWare\Release;..\build\3rdParty\bzip2\Release;..\build\3rdParty\libsmackerdec\Release;..\build\3rdParty\libmpq\Release;..\build\_deps\lua-build\lua-5.4.6\Release;..\build\_deps\sdl_audiolib-build\Release;..\build\3rdParty\asio\Release;..\build\_deps\libsodium-build\Release;..\build\_deps\libzt-build\lib\Release;..\build\_deps\libfmt-build\Release;..\build\_deps\libpng-build\Release;..\build\Source\libdevilutionx.dir\Release;%(AdditionalLibraryDirectories) pch.h diff --git a/vcpkg.json b/vcpkg.json index ab1c58361fe..56d85a23b03 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -4,7 +4,8 @@ "dependencies": [ "fmt", "bzip2", - "simpleini" + "simpleini", + "lua" ], "builtin-baseline": "e8c2a04eb7ca058b6e2f0e6e33c67fdbffeee846", "features": {