From eebae4c524e868ecb0e6b7b10fe535cebfcb7e66 Mon Sep 17 00:00:00 2001 From: Tyler Wilding Date: Sun, 2 Jun 2024 12:52:46 -0400 Subject: [PATCH] nrepl: default to a single port, nothing breaks if the port is already bound to --- common/cross_sockets/XSocketServer.cpp | 16 +++- common/cross_sockets/XSocketServer.h | 2 +- common/log/log.cpp | 6 +- common/repl/config.cpp | 9 ++ common/repl/config.h | 11 +++ common/repl/{util.cpp => repl_wrapper.cpp} | 96 +++++++++++++++------- common/repl/{util.h => repl_wrapper.h} | 17 ++-- goalc/compiler/Compiler.cpp | 2 +- 8 files changed, 113 insertions(+), 46 deletions(-) rename common/repl/{util.cpp => repl_wrapper.cpp} (67%) rename common/repl/{util.h => repl_wrapper.h} (76%) diff --git a/common/cross_sockets/XSocketServer.cpp b/common/cross_sockets/XSocketServer.cpp index 8c83ca2b47..bd563b91d1 100644 --- a/common/cross_sockets/XSocketServer.cpp +++ b/common/cross_sockets/XSocketServer.cpp @@ -35,7 +35,7 @@ void XSocketServer::shutdown_server() { close_server_socket(); } -bool XSocketServer::init_server() { +bool XSocketServer::init_server(bool failure_may_occur) { listening_socket = open_socket(AF_INET, SOCK_STREAM, 0); if (listening_socket < 0) { listening_socket = -1; @@ -76,19 +76,27 @@ bool XSocketServer::init_server() { addr.sin_port = htons(tcp_port); if (bind(listening_socket, (sockaddr*)&addr, sizeof(addr)) < 0) { - lg::error("[XSocketServer:{}] failed to bind", tcp_port); + if (failure_may_occur) { + lg::debug("[XSocketServer:{}] failed to bind", tcp_port); + } else { + lg::error("[XSocketServer:{}] failed to bind", tcp_port); + } close_server_socket(); return false; } if (listen(listening_socket, 0) < 0) { - lg::error("[XSocketServer:{}] failed to listen", tcp_port); + if (failure_may_occur) { + lg::debug("[XSocketServer:{}] failed to listen", tcp_port); + } else { + lg::error("[XSocketServer:{}] failed to listen", tcp_port); + } close_server_socket(); return false; } server_initialized = true; - lg::info("[XSocketServer:{}] initialized", tcp_port); + lg::debug("[XSocketServer:{}] initialized", tcp_port); post_init(); return true; } diff --git a/common/cross_sockets/XSocketServer.h b/common/cross_sockets/XSocketServer.h index 495868c3c9..bcb1660b53 100644 --- a/common/cross_sockets/XSocketServer.h +++ b/common/cross_sockets/XSocketServer.h @@ -20,7 +20,7 @@ class XSocketServer { XSocketServer(const XSocketServer&) = delete; XSocketServer& operator=(const XSocketServer&) = delete; - bool init_server(); + bool init_server(bool failure_may_occur = false); void shutdown_server(); void close_server_socket(); diff --git a/common/log/log.cpp b/common/log/log.cpp index bce3d2e597..dfe623b0bc 100644 --- a/common/log/log.cpp +++ b/common/log/log.cpp @@ -163,7 +163,7 @@ void set_file(const std::string& filename, file_util::find_files_in_dir(fs::path(complete_filename).parent_path(), std::regex(fmt::format("{}\\.(\\d\\.)?log", filename))); for (const auto& file : old_log_files) { - lg::info("removing {}", file.string()); + lg::debug("removing {}", file.string()); fs::remove(file); } // remove the oldest log file if there are more than LOG_ROTATE_MAX @@ -172,9 +172,9 @@ void set_file(const std::string& filename, // sort the names and remove them existing_log_files = file_util::sort_filepaths(existing_log_files, true); if (existing_log_files.size() > (LOG_ROTATE_MAX - 1)) { - lg::info("removing {} log files", existing_log_files.size() - (LOG_ROTATE_MAX - 1)); + lg::debug("removing {} log files", existing_log_files.size() - (LOG_ROTATE_MAX - 1)); for (int i = 0; i < (int)existing_log_files.size() - (LOG_ROTATE_MAX - 1); i++) { - lg::info("removing {}", existing_log_files.at(i).string()); + lg::debug("removing {}", existing_log_files.at(i).string()); fs::remove(existing_log_files.at(i)); } } diff --git a/common/repl/config.cpp b/common/repl/config.cpp index aaf311c831..57274ef96e 100644 --- a/common/repl/config.cpp +++ b/common/repl/config.cpp @@ -7,15 +7,21 @@ namespace REPL { void to_json(json& j, const Config& obj) { j = json{ + {"nreplPort", obj.nrepl_port}, {"gameVersionFolder", obj.game_version_folder}, {"numConnectToTargetAttempts", obj.target_connect_attempts}, {"asmFileSearchDirs", obj.asm_file_search_dirs}, {"keybinds", obj.keybinds}, {"perGameHistory", obj.per_game_history}, + {"permissiveRedefinitions", obj.permissive_redefinitions}, }; } void from_json(const json& j, Config& obj) { + // TODO - make a camelCase variant of json_serialize/deserialize macros + if (j.contains("nreplPort")) { + j.at("nreplPort").get_to(obj.nrepl_port); + } if (j.contains("gameVersionFolder")) { j.at("gameVersionFolder").get_to(obj.game_version_folder); } @@ -55,6 +61,9 @@ void from_json(const json& j, Config& obj) { if (j.contains("perGameHistory")) { j.at("perGameHistory").get_to(obj.per_game_history); } + if (j.contains("permissiveRedefinitions")) { + j.at("permissiveRedefinitions").get_to(obj.permissive_redefinitions); + } // if there is game specific configuration, override any values we just set if (j.contains(version_to_game_name(obj.game_version))) { from_json(j.at(version_to_game_name(obj.game_version)), obj); diff --git a/common/repl/config.h b/common/repl/config.h index 96b0247efc..ff1857be72 100644 --- a/common/repl/config.h +++ b/common/repl/config.h @@ -26,11 +26,14 @@ struct KeyBind { void to_json(json& j, const KeyBind& obj); void from_json(const json& j, KeyBind& obj); +// TODO - per-game config struct Config { GameVersion game_version; Config(GameVersion _game_version) : game_version(_game_version){}; // this is the default REPL configuration + int nrepl_port = 8181; + int temp_nrepl_port = -1; std::string game_version_folder; int target_connect_attempts = 30; std::vector asm_file_search_dirs = {}; @@ -45,6 +48,14 @@ struct Config { {KeyBind::Modifier::CTRL, "B", "Displays the most recently caught backtrace", "(:di)"}, {KeyBind::Modifier::CTRL, "N", "Full build of the game", "(mi)"}}; bool per_game_history = true; + bool permissive_redefinitions = false; + + int get_nrepl_port() { + if (temp_nrepl_port != -1) { + return temp_nrepl_port; + } + return nrepl_port; + } }; void to_json(json& j, const Config& obj); void from_json(const json& j, Config& obj); diff --git a/common/repl/util.cpp b/common/repl/repl_wrapper.cpp similarity index 67% rename from common/repl/util.cpp rename to common/repl/repl_wrapper.cpp index 7484e074a5..20ed742b56 100644 --- a/common/repl/util.cpp +++ b/common/repl/repl_wrapper.cpp @@ -1,4 +1,4 @@ -#include "util.h" +#include "repl_wrapper.h" #include "common/util/FileUtil.h" #include "common/util/json_util.h" @@ -8,36 +8,66 @@ #include "fmt/color.h" #include "fmt/core.h" #include "third-party/replxx/include/replxx.hxx" -// TODO - expand a list of hints (ie. a hint for defun to show at a glance how to write a function, -// or perhaps, show the docstring for the current function being used?) namespace REPL { void Wrapper::clear_screen() { repl.clear_screen(); } -void Wrapper::print_welcome_message() { - // TODO - dont print on std-out - // Welcome message / brief intro for documentation - std::string ascii; - ascii += " _____ _____ _____ _____ __ \n"; - ascii += "| |___ ___ ___| __| | _ | | \n"; - ascii += "| | | . | -_| | | | | | | |__ \n"; - ascii += "|_____| _|___|_|_|_____|_____|__|__|_____|\n"; - ascii += " |_| \n"; - fmt::print(fmt::emphasis::bold | fg(fmt::color::orange), ascii); - - fmt::print("Welcome to OpenGOAL {}.{}!\n", versions::GOAL_VERSION_MAJOR, - versions::GOAL_VERSION_MINOR); - fmt::print("Run {} or {} for help with common commands and REPL usage.\n", - fmt::styled("(repl-help)", fmt::emphasis::bold | fg(fmt::color::cyan)), - fmt::styled("(repl-keybinds)", fmt::emphasis::bold | fg(fmt::color::cyan))); - fmt::print("Run "); - fmt::print(fmt::emphasis::bold | fg(fmt::color::cyan), "(lt)"); - fmt::print(" to connect to the local target.\n"); - fmt::print("Run "); - fmt::print(fmt::emphasis::bold | fg(fmt::color::cyan), "(mi)"); - fmt::print(" to rebuild the entire game.\n\n"); +void Wrapper::print_welcome_message(const std::vector loaded_projects) { + std::string message; + message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " ..:::::..\n"); + message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .:-----------:.\n"); + message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .-----."); + message += fmt::format(fmt::emphasis::bold, " Welcome to OpenGOAL {}.{} [{}]", + versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR, + fmt::format(fg(fmt::color::gray), "{}", build_revision())); + if (!username.empty() && username != "#f" && username != "unknown") { + message += fmt::format(fg(fmt::color::light_green), " {}", username); + } + message += "!\n"; + message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .---."); + if (repl_config.game_version == GameVersion::Jak1) { + message += fmt::format(" [{}]: ", fmt::format(fg(fmt::color::orange), "jak1")); + } else if (repl_config.game_version == GameVersion::Jak2) { + message += fmt::format(" [{}]: ", fmt::format(fg(fmt::color::purple), "jak2")); + } else if (repl_config.game_version == GameVersion::Jak3) { + message += fmt::format(" [{}]: ", fmt::format(fg(fmt::color::gold), "jak3")); + } else { + message += fmt::format(" [{}]: ", fmt::format(fg(fmt::color::magenta), "jakx")); + } + const auto loaded_projects_str = fmt::format("{}", fmt::join(loaded_projects, ",")); + message += fmt::format(fg(fmt::color::gray), "{}\n", loaded_projects_str); + message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " . --- ."); + message += + fmt::format(" Project Path: {}\n", + fmt::format(fg(fmt::color::gray), file_util::get_jak_project_dir().string())); + message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " - :===: -"); + message += " nREPL:"; + if (!nrepl_alive) { + message += fmt::format(fg(fmt::color::red), "DISABLED\n"); + } else { + message += fmt::format(fg(fmt::color::light_green), " Listening on {}\n", + repl_config.get_nrepl_port()); + } + message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " --. .--: :--. .--"); + message += " Source File Search Dirs: "; + const auto search_dir_string = + fmt::format("{}", fmt::join(repl_config.asm_file_search_dirs, ",")); + message += fmt::format("[{}]\n", fmt::format(fg(fmt::color::gray), search_dir_string)); + message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .=======. =======."); + message += fmt::format(" {} or {} for basic help and usage\n", + fmt::format(fg(fmt::color::cyan), "(repl-help)"), + fmt::format(fg(fmt::color::cyan), "(repl-keybinds)")); + message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .-=====-. .-=====-"); + message += + fmt::format(" {} to connect to the game\n", fmt::format(fg(fmt::color::cyan), "(lt)")); + message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .-===========-."); + message += fmt::format(" {} to recompile the active project.\n", + fmt::format(fg(fmt::color::cyan), "(mi)")); + message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .-===-.\n"); + message += fmt::format(fmt::emphasis::bold | fg(fmt::color::orange), " .\n"); + fmt::print("{}", message); } void Wrapper::print_to_repl(const std::string& str) { @@ -242,20 +272,24 @@ StartupFile load_user_startup_file(const std::string& username, const GameVersio return startup_file; } -REPL::Config load_repl_config(const std::string& username, const GameVersion game_version) { +REPL::Config load_repl_config(const std::string& username, + const GameVersion game_version, + const int nrepl_port) { auto repl_config_path = file_util::get_jak_project_dir() / "goal_src" / "user" / username / "repl-config.json"; + REPL::Config loaded_config(game_version); if (file_util::file_exists(repl_config_path.string())) { try { - REPL::Config config(game_version); auto repl_config_data = parse_commented_json(file_util::read_text_file(repl_config_path), "repl-config.json"); - from_json(repl_config_data, config); - return config; + from_json(repl_config_data, loaded_config); + loaded_config.temp_nrepl_port = nrepl_port; + return loaded_config; } catch (std::exception& e) { - REPL::Config config(game_version); + // do nothing } } - return REPL::Config(game_version); + loaded_config.temp_nrepl_port = nrepl_port; + return loaded_config; } } // namespace REPL diff --git a/common/repl/util.h b/common/repl/repl_wrapper.h similarity index 76% rename from common/repl/util.h rename to common/repl/repl_wrapper.h index 0f2510466a..a5c3318bba 100644 --- a/common/repl/util.h +++ b/common/repl/repl_wrapper.h @@ -17,18 +17,20 @@ struct StartupFile { }; class Wrapper { - replxx::Replxx repl; - public: std::string username; Config repl_config; StartupFile startup_file; + bool nrepl_alive = false; std::vector examples{}; std::vector> regex_colors{}; Wrapper(GameVersion version) : repl_config(version) {} - Wrapper(const std::string& _username, const Config& config, const StartupFile& startup) - : username(_username), repl_config(config), startup_file(startup) {} + Wrapper(const std::string& _username, + const Config& config, + const StartupFile& startup, + bool nrepl_alive) + : username(_username), repl_config(config), startup_file(startup), nrepl_alive(nrepl_alive) {} replxx::Replxx& get_repl() { return repl; } void init_settings(); void reload_startup_file(); @@ -36,7 +38,7 @@ class Wrapper { // Functionality / Commands void clear_screen(); void print_to_repl(const std::string& str); - void print_welcome_message(); + void print_welcome_message(const std::vector loaded_projects); void set_history_max_size(size_t len); const char* readline(const std::string& prompt); void add_to_history(const std::string& line); @@ -47,11 +49,14 @@ class Wrapper { std::pair get_current_repl_token(std::string const& context); private: + replxx::Replxx repl; replxx::Replxx::key_press_handler_t commit_text_action(std::string text_to_commit); std::vector keybindings = {}; }; std::string find_repl_username(); StartupFile load_user_startup_file(const std::string& username, const GameVersion game_version); -REPL::Config load_repl_config(const std::string& username, const GameVersion game_version); +REPL::Config load_repl_config(const std::string& username, + const GameVersion game_version, + const int nrepl_port); } // namespace REPL diff --git a/goalc/compiler/Compiler.cpp b/goalc/compiler/Compiler.cpp index 2dccd8297d..9362880ea8 100644 --- a/goalc/compiler/Compiler.cpp +++ b/goalc/compiler/Compiler.cpp @@ -65,7 +65,7 @@ Compiler::Compiler(GameVersion version, if (m_repl) { m_repl->load_history(); // init repl - m_repl->print_welcome_message(); + m_repl->print_welcome_message(m_make.get_loaded_projects()); auto& examples = m_repl->examples; auto& regex_colors = m_repl->regex_colors; m_repl->init_settings();