From 2dd6b449b4519b8a171f567bc44751a59129a2dc Mon Sep 17 00:00:00 2001 From: sunmachine Date: Mon, 13 Jul 2026 20:41:57 -0400 Subject: [PATCH 1/3] Create a new launcher target for Linux. --- src/CMakeLists.txt | 8 +++++ src/launcher_main/CMakeLists.txt | 50 ++++++++++++++++++++++++++++++++ src/launcher_main/main.cpp | 9 +++++- 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 src/launcher_main/CMakeLists.txt diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 481b1c3ef3..0d66bdc599 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -51,6 +51,8 @@ option(NEO_USE_SEPARATE_BUILD_INFO "Use separate build info on Linux" OFF) option(NEO_ENABLE_CPACK "Enable CPack" OFF) option(NEO_USE_MEM_DEBUG "Enable USE_MEM_DEBUG defines" OFF) option(NEO_GENERATE_GAMEDATA "Generate SourceMod gamedata" ${NEO_DEDICATED}) +option(NEO_BUILD_LAUNCHER "Build the Steam mod launcher" OFF) # NOTE Linux only +set(NEO_MOD_APPID "" CACHE STRING "Steam appid for the mod launcher's steam_appid.txt") message(STATUS "Treat compile warnings as errors: ${CMAKE_COMPILE_WARNING_AS_ERROR}") message(STATUS "CI build mode: ${NEO_CI_BUILD}") @@ -74,6 +76,8 @@ message(STATUS "Install game libraries: ${NEO_INSTALL_LIBRARIES}") message(STATUS "Install game resources: ${NEO_INSTALL_RESOURCES}") message(STATUS "Use separate build info on Linux: ${NEO_USE_SEPARATE_BUILD_INFO}") message(STATUS "Enable CPack: ${NEO_ENABLE_CPACK}") +message(STATUS "Build the Steam mod launcher: ${NEO_BUILD_LAUNCHER}") +message(STATUS "Mod launcher appid override: ${NEO_MOD_APPID}") message("") if(NEO_COPY_LIBRARIES) @@ -452,6 +456,10 @@ endif() add_subdirectory(game/server) +if(NEO_BUILD_LAUNCHER AND OS_LINUX AND NOT NEO_DEDICATED) + add_subdirectory(launcher_main) +endif() + if(NEO_COPY_LIBRARIES) set(COPY_ALL_LIBS_FLAG ALL) else() diff --git a/src/launcher_main/CMakeLists.txt b/src/launcher_main/CMakeLists.txt new file mode 100644 index 0000000000..b8f2f56a0e --- /dev/null +++ b/src/launcher_main/CMakeLists.txt @@ -0,0 +1,50 @@ +# Steam mod launcher (Linux). Valve's prebuilt Linux launcher never sets +# SDK_EXEC_DIR before exec'ing hl2.sh, so relative SDK search paths in +# gameinfo.txt silently fail to mount on standalone (non-sourcemods) installs. +# So we build it ourselves. +find_package(SDL2 REQUIRED) + +add_executable(launcher_main + main.cpp +) + +# GetExecutableModName derives the default -game dir from the executable name +set_target_properties(launcher_main + PROPERTIES + OUTPUT_NAME neo +) + +target_include_directories(launcher_main + PRIVATE + ${CMAKE_SOURCE_DIR}/public +) + +target_link_libraries(launcher_main + PRIVATE + SDL2::SDL2 + ${CMAKE_DL_LIBS} +) + +# main.cpp defines __wrap_fopen/__wrap_fopen64 which reference __real_fopen/__real_fopen64 +target_link_options(launcher_main + PRIVATE + -Wl,--wrap=fopen + -Wl,--wrap=fopen64 +) + +if(NEO_MOD_APPID) + target_compile_definitions(launcher_main + PRIVATE + MOD_LAUNCHER + MOD_APPID=${NEO_MOD_APPID} + ) +endif() + +if(NEO_COPY_LIBRARIES) + add_custom_command(TARGET launcher_main POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ + ${CMAKE_SOURCE_DIR}/../game/ + VERBATIM + ) +endif() diff --git a/src/launcher_main/main.cpp b/src/launcher_main/main.cpp index 19dbd35e2d..4bd16e154d 100644 --- a/src/launcher_main/main.cpp +++ b/src/launcher_main/main.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #define MAX_PATH PATH_MAX #endif @@ -627,6 +628,10 @@ int main( int argc, char *argv[] ) return 1; } + // Tell the engine where the SDK Base install lives so FileSystem_LoadSearchPaths + // resolves relative SDK mounts against it. Same thing the Windows path does above. + setenv( "SDK_EXEC_DIR", szGameInstallDir, 1 ); + char szExecutable[8192]; snprintf(szExecutable, sizeof(szExecutable), "%s/hl2.sh", szGameInstallDir ); @@ -662,7 +667,9 @@ int main( int argc, char *argv[] ) execvp( szExecutable, new_argv.data() ); - return 0; + // execvp only returns on failure + fprintf( stderr, "[Source Mod Launcher] Failed to exec %s: %s\n", szExecutable, strerror( errno ) ); + return 1; } #else From d0be0b43afd572dc4260de23533a6628f5b649c5 Mon Sep 17 00:00:00 2001 From: sunmachine Date: Mon, 13 Jul 2026 21:08:53 -0400 Subject: [PATCH 2/3] Fix launcher build on Linux and stage the standalone install layout Rename the launcher binary to neo_linux64: Linux cannot have both the executable and the game/neo mod directory under the same name at the install root, and GetExecutableModName strips the _linux64 suffix so the default -game dir is still neo. Add src/common to the include path and include tier1/strtools.h before steam_api.h, since our patched matchmakingtypes.h uses V_strcpy_safe. When NEO_COPY_LIBRARIES is on, also stage libsteam_api.so into game/bin/linux64/ where the launcher dlopens it. --- src/launcher_main/CMakeLists.txt | 12 +++++++++++- src/launcher_main/main.cpp | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/launcher_main/CMakeLists.txt b/src/launcher_main/CMakeLists.txt index b8f2f56a0e..58ec7d4a93 100644 --- a/src/launcher_main/CMakeLists.txt +++ b/src/launcher_main/CMakeLists.txt @@ -9,14 +9,17 @@ add_executable(launcher_main ) # GetExecutableModName derives the default -game dir from the executable name +# by stripping from the last underscore: neo_linux64 -> -game neo. The suffix +# also avoids colliding with the game/neo directory at the install root. set_target_properties(launcher_main PROPERTIES - OUTPUT_NAME neo + OUTPUT_NAME neo_linux64 ) target_include_directories(launcher_main PRIVATE ${CMAKE_SOURCE_DIR}/public + ${CMAKE_SOURCE_DIR}/common ) target_link_libraries(launcher_main @@ -41,10 +44,17 @@ if(NEO_MOD_APPID) endif() if(NEO_COPY_LIBRARIES) + # Stage the standalone-install layout: launcher at the install root next to + # neo/, and the steam_api lib where the launcher dlopens it. add_custom_command(TARGET launcher_main POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ ${CMAKE_SOURCE_DIR}/../game/ + COMMAND ${CMAKE_COMMAND} -E make_directory + ${CMAKE_SOURCE_DIR}/../game/bin/linux64 + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_SOURCE_DIR}/lib/public/linux64/libsteam_api.so + ${CMAKE_SOURCE_DIR}/../game/bin/linux64/ VERBATIM ) endif() diff --git a/src/launcher_main/main.cpp b/src/launcher_main/main.cpp index 4bd16e154d..1c4ebe4e58 100644 --- a/src/launcher_main/main.cpp +++ b/src/launcher_main/main.cpp @@ -48,6 +48,7 @@ extern "C" { __declspec( dllexport ) int AmdPowerXpressRequestHighPerformance = #endif +#include "tier1/strtools.h" // matchmakingtypes.h (via steam_api.h) uses V_strcpy_safe #include #ifdef _WIN32 From 9d9067ebd68d187906ad853c65ea81c378e6a672 Mon Sep 17 00:00:00 2001 From: sunmachine Date: Mon, 13 Jul 2026 21:53:47 -0400 Subject: [PATCH 3/3] Ignore the Linux launcher binary and its runtime steam_appid.txt --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index ee09bd1a5c..1d80cca7ab 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,10 @@ out /game/neo/screenshots +# Mod launcher binary and the appid file it writes at runtime +/game/neo_linux64 +/game/steam_appid.txt + # Binary Assets /game/**/bin/** /game/neo/maps