diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2b834de50..edef44cf1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -56,6 +56,7 @@ jobs: ${{ matrix.prefix }}-SDL2 ${{ matrix.prefix }}-openal ${{ matrix.prefix }}-freetype + ${{ matrix.prefix }}-lua msystem: ${{ matrix.msystem }} path-type: minimal release: false @@ -218,7 +219,7 @@ jobs: sudo rm -f /etc/apt/sources.list.d/azure-cli.list sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list sudo apt-get -qq update - sudo apt-get -y install cmake ninja-build libcurl4-openssl-dev mesa-common-dev libxxf86dga-dev libxrandr-dev libxxf86vm-dev libasound-dev libsdl2-dev libopenal-dev libfreetype6-dev + sudo apt-get -y install cmake ninja-build libcurl4-openssl-dev mesa-common-dev libxxf86dga-dev libxrandr-dev libxxf86vm-dev libasound-dev libsdl2-dev libopenal-dev libfreetype6-dev lua5.4 liblua5.4-dev - uses: actions/checkout@v4 @@ -315,7 +316,7 @@ jobs: apt-get -qq update apt-get install -y make gcc g++ cmake ninja-build - apt-get -y install libcurl4-openssl-dev mesa-common-dev libxxf86dga-dev libxrandr-dev libxxf86vm-dev libasound-dev libsdl2-dev libopenal-dev libfreetype6-dev + apt-get -y install libcurl4-openssl-dev mesa-common-dev libxxf86dga-dev libxrandr-dev libxxf86vm-dev libasound-dev libsdl2-dev libopenal-dev libfreetype6-dev lua5.4 liblua5.4-dev OUTDIR="${GITHUB_WORKSPACE}/bin" mkdir -p "$OUTDIR" @@ -378,7 +379,7 @@ jobs: steps: - name: Install tools - run: brew install coreutils sdl2 openal-soft cmake ninja freetype + run: brew install coreutils sdl2 openal-soft cmake ninja freetype lua - uses: actions/checkout@v4 diff --git a/BUILD.md b/BUILD.md index 0be2a4fed..0f186bdad 100644 --- a/BUILD.md +++ b/BUILD.md @@ -229,3 +229,25 @@ Or let the helper script do it: ``` The flag targets the renderer code under `src/renderers/rendercommon/tr_font.c`, links with your platform’s FreeType library, and defines `BUILD_FREETYPE` for the build. Make sure the FreeType headers/libraries (`libfreetype6-dev`, `freetype-devel`, etc.) are installed before you configure the project. + +--- + +### Lua scripting support + +Lua support is enabled by default. + +To disable it explicitly: + +```bash +cmake -S . -B build -DUSE_LUA=OFF +``` + +`scripts/compile_engine.sh` also supports a `lua` flag, which passes `-DUSE_LUA=ON`. + +The engine looks for common Lua installs, including `lua5.5` / `lua-5.5` (plus `5.4`..`5.1` variants). Lua support requires Lua 5.1 or newer. + +When enabled, these commands are available: + +- `script_reload [file1.lua ...]` +- `script_list` +- `script_dump [maxEntries]` diff --git a/CMakeLists.txt b/CMakeLists.txt index a66b47aa3..a686f8d62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -181,6 +181,173 @@ endif() if(NOT DEFINED USE_WEBM) option(USE_WEBM "Enable WebM audio support" ON) endif() +if(NOT DEFINED USE_LUA) + option(USE_LUA "Enable Lua scripting support" ON) +endif() + +set(LUA_FOUND FALSE) +set(LUA_LINK_LIBRARIES "") +set(LUA_VERSION_NUM 0) +set(LUA_VERSION_STRING "") +set(LUA_EXTRA_LIBRARIES "") + +if(USE_LUA) + set(_LUA_HINT_PATHS + $ENV{LUA_DIR} + $ENV{LUA_PATH} + ${CMAKE_PREFIX_PATH} + ${CMAKE_INSTALL_PREFIX} + /usr/local + /usr + /opt/local + /opt + /opt/homebrew + /sw + ) + if(WIN32) + list(APPEND _LUA_HINT_PATHS + "C:/msys64/mingw64" + "C:/msys64/ucrt64" + "C:/msys64/mingw32" + "C:/msys64/usr" + "C:/Lua" + "C:/Program Files/Lua" + "C:/Program Files (x86)/Lua" + ) + endif() + + find_path(LUA_INCLUDE_DIR + NAMES lua.h + PATH_SUFFIXES + include + include/lua + include/lua5.5 + include/lua-5.5 + include/lua5.4 + include/lua-5.4 + include/lua5.3 + include/lua-5.3 + include/lua5.2 + include/lua-5.2 + include/lua5.1 + include/lua-5.1 + PATHS ${_LUA_HINT_PATHS} + ) + + set(_LUA_HEADER_PATH "") + if(LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/lua.h") + set(_LUA_HEADER_PATH "${LUA_INCLUDE_DIR}/lua.h") + endif() + + if(_LUA_HEADER_PATH) + file(STRINGS "${_LUA_HEADER_PATH}" LUA_VERSION_MAJOR_LINE REGEX "^#define[ \t]+LUA_VERSION_MAJOR_N[ \t]+[0-9]+" LIMIT_COUNT 1) + file(STRINGS "${_LUA_HEADER_PATH}" LUA_VERSION_MINOR_LINE REGEX "^#define[ \t]+LUA_VERSION_MINOR_N[ \t]+[0-9]+" LIMIT_COUNT 1) + if(LUA_VERSION_MAJOR_LINE AND LUA_VERSION_MINOR_LINE) + string(REGEX REPLACE ".*LUA_VERSION_MAJOR_N[ \t]+([0-9]+).*" "\\1" LUA_VERSION_MAJOR "${LUA_VERSION_MAJOR_LINE}") + string(REGEX REPLACE ".*LUA_VERSION_MINOR_N[ \t]+([0-9]+).*" "\\1" LUA_VERSION_MINOR "${LUA_VERSION_MINOR_LINE}") + math(EXPR LUA_VERSION_NUM "${LUA_VERSION_MAJOR} * 100 + ${LUA_VERSION_MINOR}") + set(LUA_VERSION_STRING "${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}") + else() + file(STRINGS "${_LUA_HEADER_PATH}" LUA_VERSION_NUM_LINE REGEX "^#define[ \t]+LUA_VERSION_NUM[ \t]+[0-9]+" LIMIT_COUNT 1) + if(LUA_VERSION_NUM_LINE) + string(REGEX REPLACE ".*LUA_VERSION_NUM[ \t]+([0-9]+).*" "\\1" LUA_VERSION_NUM "${LUA_VERSION_NUM_LINE}") + math(EXPR LUA_VERSION_MAJOR "${LUA_VERSION_NUM} / 100") + math(EXPR LUA_VERSION_MINOR "${LUA_VERSION_NUM} % 100") + set(LUA_VERSION_STRING "${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}") + endif() + endif() + endif() + + set(_LUA_LIB_NAMES lua5.5 lua-5.5 lua55 lua5.4 lua-5.4 lua54 lua5.3 lua-5.3 lua53 lua5.2 lua-5.2 lua52 lua5.1 lua-5.1 lua51 lua) + if(LUA_VERSION_STRING) + set(_LUA_LIB_NAMES lua${LUA_VERSION_STRING} lua-${LUA_VERSION_STRING} lua${LUA_VERSION_MAJOR}${LUA_VERSION_MINOR} ${_LUA_LIB_NAMES}) + list(REMOVE_DUPLICATES _LUA_LIB_NAMES) + endif() + + set(_LUA_LIB_HINTS "") + if(LUA_INCLUDE_DIR) + get_filename_component(_LUA_INCLUDE_PARENT "${LUA_INCLUDE_DIR}" DIRECTORY) + list(APPEND _LUA_LIB_HINTS + "${LUA_INCLUDE_DIR}/../lib" + "${LUA_INCLUDE_DIR}/../lib64" + "${_LUA_INCLUDE_PARENT}/lib" + "${_LUA_INCLUDE_PARENT}/lib64" + ) + list(REMOVE_DUPLICATES _LUA_LIB_HINTS) + endif() + + if(_LUA_LIB_HINTS) + find_library(LUA_LIBRARY + NAMES ${_LUA_LIB_NAMES} + HINTS ${_LUA_LIB_HINTS} + PATH_SUFFIXES "" lib lib64 + NO_DEFAULT_PATH + ) + endif() + + if(NOT LUA_LIBRARY) + find_library(LUA_LIBRARY + NAMES ${_LUA_LIB_NAMES} + PATH_SUFFIXES lib64 lib + PATHS ${_LUA_HINT_PATHS} + ) + endif() + + if((NOT LUA_INCLUDE_DIR OR NOT LUA_LIBRARY) AND NOT WIN32) + find_package(PkgConfig QUIET) + if(PKG_CONFIG_FOUND) + set(_LUA_PKG_NAMES ${_LUA_LIB_NAMES}) + list(REMOVE_DUPLICATES _LUA_PKG_NAMES) + if(_LUA_PKG_NAMES) + set(_LUA_PKG_DETECTED FALSE) + foreach(_LUA_PKG_NAME ${_LUA_PKG_NAMES}) + pkg_search_module(LUA_PKG QUIET ${_LUA_PKG_NAME}) + if(LUA_PKG_FOUND) + set(_LUA_PKG_DETECTED TRUE) + break() + endif() + endforeach() + if(_LUA_PKG_DETECTED) + if(NOT LUA_INCLUDE_DIR AND LUA_PKG_INCLUDE_DIRS) + list(GET LUA_PKG_INCLUDE_DIRS 0 LUA_INCLUDE_DIR) + endif() + if(NOT LUA_LIBRARY) + if(LUA_PKG_LIBRARIES) + list(GET LUA_PKG_LIBRARIES 0 LUA_LIBRARY) + list(REMOVE_AT LUA_PKG_LIBRARIES 0) + set(LUA_EXTRA_LIBRARIES ${LUA_PKG_LIBRARIES}) + elseif(LUA_PKG_LINK_LIBRARIES) + list(GET LUA_PKG_LINK_LIBRARIES 0 LUA_LIBRARY) + list(REMOVE_AT LUA_PKG_LINK_LIBRARIES 0) + set(LUA_EXTRA_LIBRARIES ${LUA_PKG_LINK_LIBRARIES}) + endif() + endif() + endif() + endif() + endif() + endif() + + if(NOT LUA_INCLUDE_DIR OR NOT LUA_LIBRARY) + message(WARNING "Lua headers/library not found; Lua scripting support will be disabled.") + else() + set(LUA_FOUND TRUE) + + if(LUA_VERSION_NUM GREATER 0 AND LUA_VERSION_NUM LESS 501) + message(FATAL_ERROR "Lua 5.1+ is required.") + endif() + + if(LUA_VERSION_STRING) + message(STATUS "Lua support enabled: ${LUA_VERSION_STRING} (${LUA_LIBRARY})") + else() + message(STATUS "Lua support enabled: ${LUA_LIBRARY}") + endif() + + list(APPEND LUA_LINK_LIBRARIES ${LUA_LIBRARY}) + if(LUA_EXTRA_LIBRARIES) + list(APPEND LUA_LINK_LIBRARIES ${LUA_EXTRA_LIBRARIES}) + endif() + endif() +endif() set(OPUS_FOUND FALSE) set(FLAC_FOUND FALSE) @@ -634,6 +801,10 @@ target_include_directories(qcommon PRIVATE ${CMAKE_SOURCE_DIR}/src/audio ) set_property(TARGET qcommon PROPERTY POSITION_INDEPENDENT_CODE ON) +if(USE_LUA AND LUA_FOUND) + target_compile_definitions(qcommon PRIVATE USE_LUA) + target_include_directories(qcommon PRIVATE ${LUA_INCLUDE_DIR}) +endif() # dedicated server ADD_LIBRARY(qcommon_ded OBJECT ${QCOMMON_SRCS} ${SERVER_SRCS}) @@ -649,6 +820,10 @@ target_include_directories(qcommon_ded PRIVATE ${CMAKE_SOURCE_DIR}/src/audio ) set_property(TARGET qcommon_ded PROPERTY POSITION_INDEPENDENT_CODE ON) +if(USE_LUA AND LUA_FOUND) + target_compile_definitions(qcommon_ded PRIVATE USE_LUA) + target_include_directories(qcommon_ded PRIVATE ${LUA_INCLUDE_DIR}) +endif() # Apply SKIP_IDPAK_CHECK to qcommon targets that compile files.c if(SKIP_IDPAK_CHECK) @@ -1145,6 +1320,11 @@ if(UNIX AND NOT MSVC AND NOT APPLE) target_link_options(${DNAME}${BINEXT} PRIVATE "-Wl,--export-dynamic") endif() +if(USE_LUA AND LUA_FOUND) + TARGET_LINK_LIBRARIES(${CNAME}${BINEXT} PRIVATE ${LUA_LINK_LIBRARIES}) + TARGET_LINK_LIBRARIES(${DNAME}${BINEXT} PRIVATE ${LUA_LINK_LIBRARIES}) +endif() + IF(WIN32) TARGET_LINK_LIBRARIES(${CNAME}${BINEXT} PRIVATE winmm comctl32 ws2_32) TARGET_LINK_LIBRARIES(${DNAME}${BINEXT} PRIVATE winmm comctl32 ws2_32) diff --git a/scripts/compile_engine.sh b/scripts/compile_engine.sh index 095fa1b10..b55cb3f17 100755 --- a/scripts/compile_engine.sh +++ b/scripts/compile_engine.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -# Usage: ./compile_engine.sh [game_name] [Debug|Release] [clean] [quiet] [coverage] [vulkan] [opengl] [freetype] +# Usage: ./compile_engine.sh [game_name] [Debug|Release] [clean] [quiet] [coverage] [vulkan] [opengl] [freetype] [lua] # Notes: # - build type defaults to Release # - vulkan and opengl are mutually exclusive @@ -12,6 +12,7 @@ VULKAN=0 OPENGL=0 SKIP_IDPAK=0 FREETYPE=0 +LUA=0 GAME_NAME="idtech3" BUILD_TYPE="Release" @@ -55,6 +56,7 @@ for arg in "$@"; do skip-idpak-check|skip_idpak_check|skip-pak|skip-paks) SKIP_IDPAK=1 ;; opengl) OPENGL=1 ;; freetype) FREETYPE=1 ;; + lua) LUA=1 ;; *) GAME_NAME="$arg" ;; esac done @@ -114,6 +116,11 @@ if [ "$FREETYPE" -eq 1 ]; then echo "CMake: BUILD_FREETYPE=ON" fi +if [ "$LUA" -eq 1 ]; then + CMAKE_FLAGS+=("-DUSE_LUA=ON") + echo "CMake: USE_LUA=ON" +fi + if [ "$VULKAN" -eq 1 ]; then CMAKE_FLAGS+=("-DRENDERER_DEFAULT=vulkan") else diff --git a/src/qcommon/lua_compat.h b/src/qcommon/lua_compat.h new file mode 100644 index 000000000..1e2d4a21b --- /dev/null +++ b/src/qcommon/lua_compat.h @@ -0,0 +1,78 @@ +#ifndef LUA_COMPAT_H +#define LUA_COMPAT_H + +#include +#include +#include + +#if !defined(LUA_VERSION_NUM) +#error "LUA_VERSION_NUM is required" +#endif + +#if LUA_VERSION_NUM < 501 +#error "Lua 5.1+ is required" +#endif + +#ifndef LUA_OK +#define LUA_OK 0 +#endif + +#if LUA_VERSION_NUM >= 502 +#define ID3_LUA_GETGLOBAL(L, N) lua_getglobal((L), (N)) +#else +#define ID3_LUA_GETGLOBAL(L, N) lua_getfield((L), LUA_GLOBALSINDEX, (N)) +#endif + +#if LUA_VERSION_NUM >= 502 +#define ID3_LUA_SETGLOBAL(L, N) lua_setglobal((L), (N)) +#else +#define ID3_LUA_SETGLOBAL(L, N) lua_setfield((L), LUA_GLOBALSINDEX, (N)) +#endif + +#if LUA_VERSION_NUM >= 502 +#define ID3_LUA_PUSH_GLOBAL_TABLE(L) lua_pushglobaltable((L)) +#else +#define ID3_LUA_PUSH_GLOBAL_TABLE(L) lua_pushvalue((L), LUA_GLOBALSINDEX) +#endif + +#if LUA_VERSION_NUM >= 502 +#define ID3_LUA_ABSINDEX(L, IDX) lua_absindex((L), (IDX)) +#else +#define ID3_LUA_ABSINDEX(L, IDX) ((IDX) > 0 || (IDX) <= LUA_REGISTRYINDEX ? (IDX) : lua_gettop((L)) + (IDX) + 1) +#endif + +#if LUA_VERSION_NUM >= 502 +#define ID3_LUA_RAWLEN(L, IDX) lua_rawlen((L), (IDX)) +#else +#define ID3_LUA_RAWLEN(L, IDX) lua_objlen((L), (IDX)) +#endif + +#if LUA_VERSION_NUM >= 502 +#define ID3_LUA_TONUMBERX(L, IDX, ISNUM) lua_tonumberx((L), (IDX), (ISNUM)) +#define ID3_LUA_TOINTEGERX(L, IDX, ISNUM) lua_tointegerx((L), (IDX), (ISNUM)) +#else +#define ID3_LUA_TONUMBERX(L, IDX, ISNUM) (((ISNUM) ? (*(ISNUM) = lua_isnumber((L), (IDX))) : 0), lua_tonumber((L), (IDX))) +#define ID3_LUA_TOINTEGERX(L, IDX, ISNUM) (((ISNUM) ? (*(ISNUM) = lua_isnumber((L), (IDX))) : 0), lua_tointeger((L), (IDX))) +#endif + +#if LUA_VERSION_NUM >= 503 +#define ID3_LUA_ISINTEGER(L, IDX) lua_isinteger((L), (IDX)) +#else +#define ID3_LUA_ISINTEGER(L, IDX) (0) +#endif + +#if LUA_VERSION_NUM >= 502 +#define ID3_LUA_NEWLIB(L, LREG) luaL_newlib((L), (LREG)) +#else +#define ID3_LUA_NEWLIB(L, LREG) (lua_newtable((L)), luaL_register((L), NULL, (LREG))) +#endif + +#if LUA_VERSION_NUM >= 504 +#define ID3_LUA_RESUME(L, FROM, NARGS, NRES_OUT) lua_resume((L), (FROM), (NARGS), (NRES_OUT)) +#elif LUA_VERSION_NUM >= 502 +#define ID3_LUA_RESUME(L, FROM, NARGS, NRES_OUT) ((void)(NRES_OUT), lua_resume((L), (FROM), (NARGS))) +#else +#define ID3_LUA_RESUME(L, FROM, NARGS, NRES_OUT) ((void)(NRES_OUT), lua_resume((L), (NARGS))) +#endif + +#endif diff --git a/src/qcommon/lua_debug.c b/src/qcommon/lua_debug.c new file mode 100644 index 000000000..59afa8233 --- /dev/null +++ b/src/qcommon/lua_debug.c @@ -0,0 +1,216 @@ +#include "q_shared.h" +#include "qcommon.h" +#include "lua_debug.h" + +#ifdef USE_LUA +#include "lua_compat.h" + +#define MAX_LUA_TRACKED_SCRIPTS 64 + +static lua_State *s_luaState; +static int s_luaTrackedCount; +static char s_luaTrackedScripts[MAX_LUA_TRACKED_SCRIPTS][MAX_OSPATH]; + +static void LuaDebug_ClearTrackedScripts( void ) { + s_luaTrackedCount = 0; +} + +static void LuaDebug_CloseState( void ) { + if ( s_luaState ) { + lua_close( s_luaState ); + s_luaState = NULL; + } +} + +static qboolean LuaDebug_OpenState( void ) { + if ( s_luaState ) { + return qtrue; + } + + s_luaState = luaL_newstate(); + if ( !s_luaState ) { + Com_Printf( S_COLOR_RED "Lua: failed to initialize VM\n" ); + return qfalse; + } + + luaL_openlibs( s_luaState ); + return qtrue; +} + +static void LuaDebug_PrintLuaError( const char *prefix ) { + const char *msg = lua_tostring( s_luaState, -1 ); + Com_Printf( S_COLOR_RED "Lua: %s: %s\n", prefix, msg ? msg : "(unknown error)" ); + lua_pop( s_luaState, 1 ); +} + +static qboolean LuaDebug_LoadScript( const char *scriptPath ) { + if ( !LuaDebug_OpenState() ) { + return qfalse; + } + + if ( luaL_loadfile( s_luaState, scriptPath ) != LUA_OK ) { + LuaDebug_PrintLuaError( scriptPath ); + return qfalse; + } + + if ( lua_pcall( s_luaState, 0, LUA_MULTRET, 0 ) != LUA_OK ) { + LuaDebug_PrintLuaError( scriptPath ); + return qfalse; + } + + return qtrue; +} + +static void LuaDebug_TrackScript( const char *scriptPath ) { + int i; + + for ( i = 0; i < s_luaTrackedCount; i++ ) { + if ( !Q_stricmp( s_luaTrackedScripts[i], scriptPath ) ) { + return; + } + } + + if ( s_luaTrackedCount >= MAX_LUA_TRACKED_SCRIPTS ) { + Com_Printf( S_COLOR_YELLOW "Lua: tracked script limit reached (%d)\n", MAX_LUA_TRACKED_SCRIPTS ); + return; + } + + Q_strncpyz( s_luaTrackedScripts[s_luaTrackedCount], scriptPath, sizeof( s_luaTrackedScripts[0] ) ); + s_luaTrackedCount++; +} + +void Cmd_ScriptReload_f( void ) { + int argc = Cmd_Argc(); + int i; + int successCount = 0; + int failureCount = 0; + + if ( argc <= 1 ) { + if ( s_luaTrackedCount <= 0 ) { + LuaDebug_CloseState(); + if ( LuaDebug_OpenState() ) { + Com_Printf( "Lua: runtime initialized (%s, %d)\n", LUA_VERSION, LUA_VERSION_NUM ); + } + return; + } + + LuaDebug_CloseState(); + if ( !LuaDebug_OpenState() ) { + return; + } + + for ( i = 0; i < s_luaTrackedCount; i++ ) { + if ( LuaDebug_LoadScript( s_luaTrackedScripts[i] ) ) { + successCount++; + } else { + failureCount++; + } + } + + Com_Printf( "Lua: reloaded %d tracked script(s), %d failure(s)\n", successCount, failureCount ); + return; + } + + LuaDebug_CloseState(); + LuaDebug_ClearTrackedScripts(); + + if ( !LuaDebug_OpenState() ) { + return; + } + + for ( i = 1; i < argc; i++ ) { + const char *scriptPath = Cmd_Argv( i ); + + if ( !scriptPath || !scriptPath[0] ) { + continue; + } + + if ( LuaDebug_LoadScript( scriptPath ) ) { + LuaDebug_TrackScript( scriptPath ); + successCount++; + } else { + failureCount++; + } + } + + Com_Printf( "Lua: loaded %d script(s), %d failure(s)\n", successCount, failureCount ); +} + +void Cmd_ScriptList_f( void ) { + int i; + + Com_Printf( "Lua: compile-time API %s (LUA_VERSION_NUM=%d)\n", LUA_VERSION, LUA_VERSION_NUM ); + + if ( !s_luaState ) { + Com_Printf( "Lua: runtime not initialized. Run script_reload first.\n" ); + return; + } + + Com_Printf( "Lua: runtime initialized\n" ); + Com_Printf( "Lua: tracked scripts (%d)\n", s_luaTrackedCount ); + + for ( i = 0; i < s_luaTrackedCount; i++ ) { + Com_Printf( " %2d: %s\n", i + 1, s_luaTrackedScripts[i] ); + } +} + +void Cmd_ScriptDump_f( void ) { + int maxEntries = 128; + int printed = 0; + qboolean truncated = qfalse; + + if ( Cmd_Argc() > 1 ) { + const int requested = atoi( Cmd_Argv( 1 ) ); + if ( requested > 0 ) { + maxEntries = requested; + } + } + + if ( !s_luaState ) { + Com_Printf( "Lua: runtime not initialized. Run script_reload first.\n" ); + return; + } + + ID3_LUA_PUSH_GLOBAL_TABLE( s_luaState ); + lua_pushnil( s_luaState ); + + Com_Printf( "Lua: globals (limit %d)\n", maxEntries ); + while ( lua_next( s_luaState, -2 ) != 0 ) { + const char *keyName = lua_tostring( s_luaState, -2 ); + const char *valueType = lua_typename( s_luaState, lua_type( s_luaState, -1 ) ); + + if ( keyName ) { + Com_Printf( " %s : %s\n", keyName, valueType ); + } else { + const char *keyType = lua_typename( s_luaState, lua_type( s_luaState, -2 ) ); + Com_Printf( " [%s] : %s\n", keyType, valueType ); + } + + lua_pop( s_luaState, 1 ); + printed++; + if ( printed >= maxEntries ) { + Com_Printf( " ... output truncated ...\n" ); + truncated = qtrue; + break; + } + } + + lua_pop( s_luaState, truncated ? 2 : 1 ); + Com_Printf( "Lua: dumped %d global entr%s\n", printed, printed == 1 ? "y" : "ies" ); +} + +#else + +void Cmd_ScriptReload_f( void ) { + Com_Printf( "Lua support is disabled in this build. Configure with -DUSE_LUA=ON.\n" ); +} + +void Cmd_ScriptList_f( void ) { + Com_Printf( "Lua support is disabled in this build. Configure with -DUSE_LUA=ON.\n" ); +} + +void Cmd_ScriptDump_f( void ) { + Com_Printf( "Lua support is disabled in this build. Configure with -DUSE_LUA=ON.\n" ); +} + +#endif diff --git a/src/qcommon/lua_debug.h b/src/qcommon/lua_debug.h new file mode 100644 index 000000000..6b7689aca --- /dev/null +++ b/src/qcommon/lua_debug.h @@ -0,0 +1,8 @@ +#ifndef LUA_DEBUG_H +#define LUA_DEBUG_H + +void Cmd_ScriptReload_f( void ); +void Cmd_ScriptList_f( void ); +void Cmd_ScriptDump_f( void ); + +#endif