BareGit
cmake_minimum_required(VERSION 3.24)

project(nethack_mcp VERSION 0.1.0 LANGUAGES C CXX)

include(CTest)

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

option(NETHACK_BUILD_ENGINE "Build the pinned NetHack library" ON)

include(FetchContent)

# Declare these before loading libmw. Its current CMake files declare the
# same projects without pins, so the application's declarations must win.
FetchContent_Declare(
    httplib
    GIT_REPOSITORY https://github.com/yhirose/cpp-httplib.git
    GIT_TAG 278c2979e8c68468960c3073e28e1c51b098d6a4
    GIT_SHALLOW FALSE)
FetchContent_Declare(
    spdlog
    GIT_REPOSITORY https://github.com/gabime/spdlog.git
    GIT_TAG 57cb5fb7a8ff30079751728234623230535a5c92
    GIT_SHALLOW FALSE)
FetchContent_Declare(
    json
    GIT_REPOSITORY https://github.com/nlohmann/json.git
    GIT_TAG aa391dc0a56f8409e2e7aca6e7c9a9d766d44ce8
    GIT_SHALLOW FALSE)
FetchContent_Declare(
    libmw
    GIT_REPOSITORY https://github.com/MetroWind/libmw.git
    GIT_TAG 00f857d93fda0f0eb84bb8ab540b58063f1c91b2
    GIT_SHALLOW FALSE)
FetchContent_Declare(
    tomlplusplus
    GIT_REPOSITORY https://github.com/marzer/tomlplusplus.git
    GIT_TAG v3.4.0
    GIT_SHALLOW FALSE)

set(LIBMW_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(LIBMW_BUILD_URL ON CACHE BOOL "" FORCE)
set(LIBMW_BUILD_HTTP_SERVER ON CACHE BOOL "" FORCE)
set(LIBMW_BUILD_SQLITE ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(libmw)
FetchContent_MakeAvailable(tomlplusplus)
find_package(Threads REQUIRED)
find_package(OpenSSL REQUIRED COMPONENTS Crypto)

if(NETHACK_BUILD_ENGINE)
    FetchContent_Declare(
        lua
        URL https://www.lua.org/ftp/lua-5.4.9.tar.gz
        URL_HASH SHA256=2335b6c582a52654f94612bf10d2f4672805d05329aa6568b1d8cd9e5c6fb8e6
        DOWNLOAD_EXTRACT_TIMESTAMP TRUE)
    # The upstream archive has no CMakeLists.txt; MakeAvailable fetches it,
    # and the static library target is defined here.
    FetchContent_MakeAvailable(lua)

    set(NETHACK_LUA_INCLUDE_DIR "${lua_SOURCE_DIR}/src")
    add_library(nethack_lua STATIC
        "${NETHACK_LUA_INCLUDE_DIR}/lapi.c"
        "${NETHACK_LUA_INCLUDE_DIR}/lcode.c"
        "${NETHACK_LUA_INCLUDE_DIR}/lctype.c"
        "${NETHACK_LUA_INCLUDE_DIR}/ldebug.c"
        "${NETHACK_LUA_INCLUDE_DIR}/ldo.c"
        "${NETHACK_LUA_INCLUDE_DIR}/ldump.c"
        "${NETHACK_LUA_INCLUDE_DIR}/lfunc.c"
        "${NETHACK_LUA_INCLUDE_DIR}/lgc.c"
        "${NETHACK_LUA_INCLUDE_DIR}/llex.c"
        "${NETHACK_LUA_INCLUDE_DIR}/lmem.c"
        "${NETHACK_LUA_INCLUDE_DIR}/lobject.c"
        "${NETHACK_LUA_INCLUDE_DIR}/lopcodes.c"
        "${NETHACK_LUA_INCLUDE_DIR}/lparser.c"
        "${NETHACK_LUA_INCLUDE_DIR}/lstate.c"
        "${NETHACK_LUA_INCLUDE_DIR}/lstring.c"
        "${NETHACK_LUA_INCLUDE_DIR}/ltable.c"
        "${NETHACK_LUA_INCLUDE_DIR}/ltm.c"
        "${NETHACK_LUA_INCLUDE_DIR}/lundump.c"
        "${NETHACK_LUA_INCLUDE_DIR}/lvm.c"
        "${NETHACK_LUA_INCLUDE_DIR}/lzio.c"
        "${NETHACK_LUA_INCLUDE_DIR}/lauxlib.c"
        "${NETHACK_LUA_INCLUDE_DIR}/lbaselib.c"
        "${NETHACK_LUA_INCLUDE_DIR}/lcorolib.c"
        "${NETHACK_LUA_INCLUDE_DIR}/ldblib.c"
        "${NETHACK_LUA_INCLUDE_DIR}/liolib.c"
        "${NETHACK_LUA_INCLUDE_DIR}/lmathlib.c"
        "${NETHACK_LUA_INCLUDE_DIR}/loadlib.c"
        "${NETHACK_LUA_INCLUDE_DIR}/loslib.c"
        "${NETHACK_LUA_INCLUDE_DIR}/lstrlib.c"
        "${NETHACK_LUA_INCLUDE_DIR}/ltablib.c"
        "${NETHACK_LUA_INCLUDE_DIR}/lutf8lib.c"
        "${NETHACK_LUA_INCLUDE_DIR}/linit.c")
    target_compile_features(nethack_lua PRIVATE c_std_99)
    target_compile_definitions(nethack_lua PRIVATE LUA_USE_LINUX)
    target_include_directories(nethack_lua PUBLIC
        "${NETHACK_LUA_INCLUDE_DIR}")
    target_link_libraries(nethack_lua PUBLIC m ${CMAKE_DL_LIBS})
    set_target_properties(nethack_lua PROPERTIES
        POSITION_INDEPENDENT_CODE ON)

    FetchContent_Declare(
        nethack
        GIT_REPOSITORY https://github.com/NetHack/NetHack.git
        GIT_TAG c94fd5225beef48143244bfb7bc42682aad58741
        GIT_SHALLOW FALSE)
    FetchContent_MakeAvailable(nethack)

    set(NETHACK_WORK_DIR
        "${CMAKE_BINARY_DIR}/nethack-work"
        CACHE PATH "Isolated NetHack build directory")
    set(NETHACK_LIBRARY "${NETHACK_WORK_DIR}/src/libnh.a")
    set(NETHACK_COMPAT_LIBRARY
        "${NETHACK_WORK_DIR}/src/libnethack.a")
    set(NETHACK_RUNTIME_DIR
        "${NETHACK_WORK_DIR}/playground")
    set(NETHACK_BUILD_MARKER
        "${NETHACK_WORK_DIR}/.nethack-library-built")
    file(MAKE_DIRECTORY "${NETHACK_WORK_DIR}/include")

    add_custom_command(
        OUTPUT "${NETHACK_BUILD_MARKER}"
        BYPRODUCTS
            "${NETHACK_COMPAT_LIBRARY}"
            "${NETHACK_RUNTIME_DIR}/nhdat"
            "${NETHACK_RUNTIME_DIR}/symbols"
            "${NETHACK_RUNTIME_DIR}/license"
            "${NETHACK_RUNTIME_DIR}/sysconf"
        COMMAND "${CMAKE_COMMAND}" -E rm -rf "${NETHACK_WORK_DIR}"
        COMMAND "${CMAKE_COMMAND}" -E copy_directory
            "${nethack_SOURCE_DIR}" "${NETHACK_WORK_DIR}"
        COMMAND "${CMAKE_COMMAND}"
            "-DNETHACK_SOURCE_DIR=${NETHACK_WORK_DIR}"
            "-DNETHACK_LUA_INCLUDE_DIR=${NETHACK_LUA_INCLUDE_DIR}"
            "-DNETHACK_LUA_LIBRARY=$<TARGET_FILE:nethack_lua>"
            "-DNETHACK_COMPILER=${CMAKE_C_COMPILER}"
            "-DNETHACK_ARCHIVER=${CMAKE_AR}"
            -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/build_nethack.cmake"
        DEPENDS
            "${CMAKE_CURRENT_SOURCE_DIR}/cmake/build_nethack.cmake"
            nethack_lua
        VERBATIM)

    add_custom_target(nethack_engine_build
        DEPENDS "${NETHACK_BUILD_MARKER}")

    add_library(nethack_lib STATIC IMPORTED GLOBAL)
    set_target_properties(nethack_lib PROPERTIES
        IMPORTED_LOCATION "${NETHACK_COMPAT_LIBRARY}"
        INTERFACE_INCLUDE_DIRECTORIES
            "${NETHACK_WORK_DIR}/include"
        INTERFACE_LINK_LIBRARIES nethack_lua)
    add_dependencies(nethack_lib nethack_engine_build)

    add_library(NetHack::libnethack ALIAS nethack_lib)

    if(BUILD_TESTING)
        add_executable(nethack_engine_probe src/engine_probe.cpp)
        target_compile_options(nethack_engine_probe PRIVATE
            -Wall -Wextra -Wpedantic)
        target_compile_definitions(nethack_engine_probe PRIVATE
            NETHACK_PROBE_RUNTIME_DIR="${NETHACK_RUNTIME_DIR}")
        target_include_directories(nethack_engine_probe PRIVATE
            "${NETHACK_WORK_DIR}/include")
        target_link_libraries(nethack_engine_probe PRIVATE nethack_lib)

        add_test(NAME nethack_engine_probe COMMAND nethack_engine_probe)
        set_tests_properties(nethack_engine_probe PROPERTIES
            WORKING_DIRECTORY "${NETHACK_RUNTIME_DIR}"
            TIMEOUT 10)
    endif()
else()
    set(NETHACK_RUNTIME_DIR "${CMAKE_CURRENT_BINARY_DIR}")
endif()

set(NETHACK_MCP_SOURCES
    src/config_file.cpp
    src/engine_process.cpp
    src/game_manager.cpp
    src/game_record_store.cpp
    src/game_session.cpp
    src/identity.cpp
    src/main.cpp
    src/mcp_server.cpp
    src/observation_store.cpp
    src/protocol.cpp
    src/game_http_server.cpp)

set(STATIC_FILES
    "${CMAKE_CURRENT_SOURCE_DIR}/web/AGENT.md"
    "${CMAKE_CURRENT_SOURCE_DIR}/web/copy_prompt.js"
    "${CMAKE_CURRENT_SOURCE_DIR}/web/home.html"
    "${CMAKE_CURRENT_SOURCE_DIR}/web/home.css"
    "${CMAKE_CURRENT_SOURCE_DIR}/web/local_time.js"
    "${CMAKE_CURRENT_SOURCE_DIR}/web/index.html"
    "${CMAKE_CURRENT_SOURCE_DIR}/web/viewer.css"
    "${CMAKE_CURRENT_SOURCE_DIR}/web/viewer.js"
    "${CMAKE_CURRENT_SOURCE_DIR}/web/kreative_square.ttf")
set(EMBEDDED_ASSETS_SOURCE
    "${CMAKE_CURRENT_BINARY_DIR}/generated/embedded_assets.cpp")
add_custom_command(
    OUTPUT "${EMBEDDED_ASSETS_SOURCE}"
    COMMAND "${CMAKE_COMMAND}"
        "-DOUTPUT=${EMBEDDED_ASSETS_SOURCE}"
        "-DWEB_ROOT=${CMAKE_CURRENT_SOURCE_DIR}/web"
        "-DSTATIC_FILES=${STATIC_FILES}"
        -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/embed_assets.cmake"
    DEPENDS ${STATIC_FILES} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/embed_assets.cmake"
    VERBATIM)
list(APPEND NETHACK_MCP_SOURCES
    "${EMBEDDED_ASSETS_SOURCE}")

if(NETHACK_BUILD_ENGINE)
    list(APPEND NETHACK_MCP_SOURCES
        src/engine_worker.cpp
        src/window_adapter.cpp)
else()
    list(APPEND NETHACK_MCP_SOURCES src/engine_worker_stub.cpp)
endif()

add_executable(nethack_mcp ${NETHACK_MCP_SOURCES})
target_compile_features(nethack_mcp PRIVATE cxx_std_23)
target_compile_options(nethack_mcp PRIVATE -Wall -Wextra -Wpedantic)
target_include_directories(nethack_mcp PRIVATE include)
target_compile_definitions(nethack_mcp PRIVATE
    NETHACK_RUNTIME_DIR="${NETHACK_RUNTIME_DIR}")
target_link_libraries(nethack_mcp PRIVATE
    mw::mw
    mw::http-server
    mw::sqlite
    OpenSSL::Crypto
    nlohmann_json::nlohmann_json
    tomlplusplus::tomlplusplus
    Threads::Threads)
if(NETHACK_BUILD_ENGINE)
    target_include_directories(nethack_mcp PRIVATE
        "${NETHACK_WORK_DIR}/include")
    target_compile_definitions(nethack_mcp PRIVATE NETHACK_HAS_ENGINE=1)
    target_link_libraries(nethack_mcp PRIVATE nethack_lib)
endif()