From f328800ce8e3b5a9b0b61695df0b5d3e7501943d Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Thu, 22 Sep 2022 17:43:21 -0400 Subject: [PATCH] Cache Launcher Configuration --- docs/COMMAND_LINE.md | 3 + launcher/CMakeLists.txt | 2 +- launcher/src/bootstrap.c | 3 + launcher/src/client/cache.cpp | 137 ++++++++++++++++++++++++ launcher/src/client/cache.h | 19 ++++ launcher/src/client/launcher.cpp | 48 ++++++--- launcher/src/client/launcher.h | 12 +++ launcher/src/crash-report.c | 3 + libreborn/CMakeLists.txt | 2 +- libreborn/include/libreborn/exec.h | 3 + libreborn/include/libreborn/log.h | 6 +- libreborn/src/util/exec.c | 3 + libreborn/src/util/log.c | 4 + media-layer/proxy/src/client/client.cpp | 3 + media-layer/proxy/src/common/common.h | 2 +- media-layer/proxy/src/server/server.cpp | 3 + scripts/test.sh | 2 +- 17 files changed, 238 insertions(+), 17 deletions(-) create mode 100644 launcher/src/client/cache.cpp create mode 100644 launcher/src/client/cache.h create mode 100644 launcher/src/client/launcher.h create mode 100644 libreborn/src/util/log.c diff --git a/docs/COMMAND_LINE.md b/docs/COMMAND_LINE.md index 80a3e1a..8e20828 100644 --- a/docs/COMMAND_LINE.md +++ b/docs/COMMAND_LINE.md @@ -25,6 +25,9 @@ If you run MCPI-Reborn with ``--benchmark``, it will enter a simple benchmark mo The world used will always be re-created on start and uses a hard-coded seed. +### ``--no-cache`` (Client Mode Only) +If you run MCPI-Reborn with ``--no-cache``, it will skip loading and saving the cached launcher configuration. + ## Environmental Variables ### ``MCPI_DEBUG`` diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 7d7345f..8060d7a 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -9,7 +9,7 @@ if(MCPI_SERVER_MODE) target_sources(launcher PRIVATE src/server/launcher.c) else() embed_resource(launcher src/client/available-feature-flags) - target_sources(launcher PRIVATE src/client/launcher.cpp) + target_sources(launcher PRIVATE src/client/launcher.cpp src/client/cache.cpp) endif() target_link_libraries(launcher reborn-util) # RPath diff --git a/launcher/src/bootstrap.c b/launcher/src/bootstrap.c index 6b6d64a..319c9e8 100644 --- a/launcher/src/bootstrap.c +++ b/launcher/src/bootstrap.c @@ -101,6 +101,9 @@ void pre_bootstrap(int argc, char *argv[]) { // Disable stdout Buffering setvbuf(stdout, NULL, _IONBF, 0); + // Set Debug Tag + reborn_debug_tag = "(Launcher) "; + // Set Default Native Component Environment #define set_variable_default(name) set_and_print_env("MCPI_NATIVE_" name, getenv(name)); for_each_special_environmental_variable(set_variable_default); diff --git a/launcher/src/client/cache.cpp b/launcher/src/client/cache.cpp new file mode 100644 index 0000000..ec25743 --- /dev/null +++ b/launcher/src/client/cache.cpp @@ -0,0 +1,137 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include "launcher.h" +#include "cache.h" + +// Get Cache Path +static std::string get_cache_path() { + const char *home = getenv("HOME"); + if (home == NULL) { + IMPOSSIBLE(); + } + return std::string(home) + "/.minecraft-pi/.launcher-cache"; +} + +// Load +launcher_cache empty_cache = { + .username = DEFAULT_USERNAME, + .render_distance = DEFAULT_RENDER_DISTANCE, + .feature_flags = {} +}; +launcher_cache load_cache() { + // Log + DEBUG("Loading Launcher Cache..."); + + // Open File + std::ifstream stream(get_cache_path(), std::ios::in | std::ios::binary); + if (!stream) { + // No Warning If File Doesn't Exist + struct stat s; + if (stat(get_cache_path().c_str(), &s) == 0) { + WARN("Unable To Open Launcher Cache For Loading"); + } + return empty_cache; + } + + // Check Version + unsigned char cache_version; + stream.read((char *) &cache_version, 1); + if (stream.eof() || cache_version != (unsigned char) CACHE_VERSION) { + WARN("Invalid Launcher Cache Version"); + stream.close(); + return empty_cache; + } + + // Load Username And Render Distance + launcher_cache cache; + std::getline(stream, cache.username, '\0'); + std::getline(stream, cache.render_distance, '\0'); + + // Load Feature Flags + std::string flag; + while (!stream.eof() && std::getline(stream, flag, '\0')) { + if (flag.length() > 0) { + unsigned char is_enabled = 0; + stream.read((char *) &is_enabled, 1); + cache.feature_flags[flag] = is_enabled != (unsigned char) 0; + } + stream.peek(); + } + + // Finish + stream.close(); + if (!stream) { + WARN("Failure While Loading Launcher Cache: %s", strerror(errno)); + return empty_cache; + } + + // Return + return cache; +} + +// Save +#define write_env_to_stream(stream, env) \ + { \ + const char *env_value = getenv(env); \ + if (env == NULL) { \ + IMPOSSIBLE(); \ + } \ + stream.write(env_value, strlen(env_value) + 1); \ + } +void save_cache() { + // Log + DEBUG("Saving Launcher Cache..."); + + // Open File + std::ofstream stream(get_cache_path(), std::ios::out | std::ios::binary); + if (!stream) { + WARN("Unable To Open Launcher Cache For Saving"); + return; + } + + // Save Cache Version + unsigned char cache_version = (unsigned char) CACHE_VERSION; + stream.write((const char *) &cache_version, 1); + + // Save Username And Render Distance + write_env_to_stream(stream, "MCPI_USERNAME"); + write_env_to_stream(stream, "MCPI_RENDER_DISTANCE"); + + // Save Feature Flags + std::unordered_map flags; + load_available_feature_flags([&flags](std::string flag) { + std::string stripped_flag = strip_feature_flag_default(flag, NULL); + flags[stripped_flag] = false; + }); + { + const char *enabled_flags = getenv("MCPI_FEATURE_FLAGS"); + if (enabled_flags == NULL) { + IMPOSSIBLE(); + } + std::istringstream enabled_flags_stream(enabled_flags); + std::string flag; + while (std::getline(enabled_flags_stream, flag, '|')) { + if (flag.length() > 0) { + flags[flag] = true; + } + } + } + for (auto &it : flags) { + stream.write(it.first.c_str(), it.first.size() + 1); + unsigned char val = it.second ? (unsigned char) 1 : (unsigned char) 0; + stream.write((const char *) &val, 1); + } + + // Finish + stream.close(); + if (!stream.good()) { + WARN("Failure While Saving Launcher Cache"); + } +} diff --git a/launcher/src/client/cache.h b/launcher/src/client/cache.h new file mode 100644 index 0000000..ba80c47 --- /dev/null +++ b/launcher/src/client/cache.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +// Cache Version +#define CACHE_VERSION 0 + +// Load Cache +typedef struct { + std::string username; + std::string render_distance; + std::unordered_map feature_flags; +} launcher_cache; +extern launcher_cache empty_cache; +launcher_cache load_cache(); + +// Save Cache +void save_cache(); diff --git a/launcher/src/client/launcher.cpp b/launcher/src/client/launcher.cpp index 47c68ae..c945845 100644 --- a/launcher/src/client/launcher.cpp +++ b/launcher/src/client/launcher.cpp @@ -10,9 +10,11 @@ #include #include "../bootstrap.h" +#include "launcher.h" +#include "cache.h" // Strip Feature Flag Default -static std::string strip_feature_flag_default(std::string flag, bool *default_ret) { +std::string strip_feature_flag_default(std::string flag, bool *default_ret) { // Valid Values std::string true_str = "TRUE "; std::string false_str = "FALSE "; @@ -38,7 +40,7 @@ static std::string strip_feature_flag_default(std::string flag, bool *default_re // Load Available Feature Flags extern unsigned char available_feature_flags[]; extern size_t available_feature_flags_len; -static void load_available_feature_flags(std::function callback) { +void load_available_feature_flags(std::function callback) { // Get Path char *binary_directory = get_binary_directory(); std::string path = std::string(binary_directory) + "/available-feature-flags"; @@ -134,10 +136,6 @@ static void set_env_if_unset(const char *env_name, std::function } } -// Defaults -#define DEFAULT_USERNAME "StevePi" -#define DEFAULT_RENDER_DISTANCE "Short" - // Launch #define LIST_DIALOG_SIZE "400" int main(int argc, char *argv[]) { @@ -146,6 +144,11 @@ int main(int argc, char *argv[]) { ERR("Don't Run As Root"); } + // Ensure HOME + if (getenv("HOME") == NULL) { + ERR("$HOME Isn't Set"); + } + // Pre-Bootstrap pre_bootstrap(argc, argv); @@ -192,6 +195,15 @@ int main(int argc, char *argv[]) { } } + // --no-cache + bool no_cache = false; + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--no-cache") == 0) { + no_cache = true; + break; + } + } + // Create ~/.minecraft-pi If Needed // Minecraft Folder { @@ -212,6 +224,9 @@ int main(int argc, char *argv[]) { free(minecraft_folder); } + // Load Cache + launcher_cache cache = no_cache ? empty_cache : load_cache(); + // Setup MCPI_FEATURE_FLAGS { std::vector command; @@ -225,12 +240,16 @@ int main(int argc, char *argv[]) { command.push_back("Enabled"); command.push_back("--column"); command.push_back("Feature"); - load_available_feature_flags([&command](std::string flag) { - bool default_value; + load_available_feature_flags([&command, &cache](std::string flag) { + bool value; // Strip Default Value - std::string stripped_flag = strip_feature_flag_default(flag, &default_value); + std::string stripped_flag = strip_feature_flag_default(flag, &value); + // Use Cache + if (cache.feature_flags.count(stripped_flag) > 0) { + value = cache.feature_flags[stripped_flag]; + } // Specify Default Value - if (default_value) { + if (value) { // Enabled By Default command.push_back("TRUE"); } else { @@ -260,7 +279,7 @@ int main(int argc, char *argv[]) { command.push_back("Name"); std::string render_distances[] = {"Far", "Normal", "Short", "Tiny"}; for (std::string &render_distance : render_distances) { - command.push_back(render_distance.compare(DEFAULT_RENDER_DISTANCE) == 0 ? "TRUE" : "FALSE"); + command.push_back(render_distance.compare(cache.render_distance) == 0 ? "TRUE" : "FALSE"); command.push_back(render_distance); } // Run @@ -273,11 +292,16 @@ int main(int argc, char *argv[]) { command.push_back("--text"); command.push_back("Enter Minecraft Username:"); command.push_back("--entry-text"); - command.push_back(DEFAULT_USERNAME); + command.push_back(cache.username); // Run run_zenity_and_set_env("MCPI_USERNAME", command); } + // Save Cache + if (!no_cache) { + save_cache(); + } + // Bootstrap bootstrap(argc, argv); } diff --git a/launcher/src/client/launcher.h b/launcher/src/client/launcher.h new file mode 100644 index 0000000..ad5f32e --- /dev/null +++ b/launcher/src/client/launcher.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +// Defaults +#define DEFAULT_USERNAME "StevePi" +#define DEFAULT_RENDER_DISTANCE "Short" + +// Feature Flags +std::string strip_feature_flag_default(std::string flag, bool *default_ret); +void load_available_feature_flags(std::function callback); diff --git a/launcher/src/crash-report.c b/launcher/src/crash-report.c index 3bee5b6..3d64ef5 100644 --- a/launcher/src/crash-report.c +++ b/launcher/src/crash-report.c @@ -98,6 +98,9 @@ void setup_crash_report() { close(error_pipe[PIPE_WRITE]); close(input_pipe[PIPE_READ]); + // Set Debug Tag + reborn_debug_tag = "(Crash Reporter) "; + // Setup Logging #define BUFFER_SIZE 1024 char buf[BUFFER_SIZE]; diff --git a/libreborn/CMakeLists.txt b/libreborn/CMakeLists.txt index 0622639..9462a00 100644 --- a/libreborn/CMakeLists.txt +++ b/libreborn/CMakeLists.txt @@ -5,7 +5,7 @@ file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include/libreborn") configure_file(include/libreborn/config.h.in "${CMAKE_CURRENT_BINARY_DIR}/include/libreborn/config.h" ESCAPE_QUOTES @ONLY) # Util -add_library(reborn-util SHARED src/util/elf.c src/util/exec.c src/util/string.c src/util/util.c) +add_library(reborn-util SHARED src/util/elf.c src/util/exec.c src/util/string.c src/util/util.c src/util/log.c) target_include_directories( reborn-util PUBLIC diff --git a/libreborn/include/libreborn/exec.h b/libreborn/include/libreborn/exec.h index da50efb..3e54259 100644 --- a/libreborn/include/libreborn/exec.h +++ b/libreborn/include/libreborn/exec.h @@ -32,6 +32,9 @@ void chop_last_component(char **str); // Get Binary Directory (Remember To Free) char *get_binary_directory(); +// Debug Tag +#define CHILD_PROCESS_TAG "(Child Process) " + // Run Command And Get Output char *run_command(const char *const command[], int *exit_status); #define is_exit_status_success(status) (WIFEXITED(status) && WEXITSTATUS(status) == 0) diff --git a/libreborn/include/libreborn/log.h b/libreborn/include/libreborn/log.h index 9a71525..22e9846 100644 --- a/libreborn/include/libreborn/log.h +++ b/libreborn/include/libreborn/log.h @@ -3,9 +3,13 @@ #include #include +// Debug Tag +extern const char *reborn_debug_tag; + // Logging #define INFO(format, ...) { fprintf(stderr, "[INFO]: " format "\n", ##__VA_ARGS__); } #define WARN(format, ...) { fprintf(stderr, "[WARN]: " format "\n", ##__VA_ARGS__); } -#define DEBUG(format, ...) { const char *debug = getenv("MCPI_DEBUG"); if (debug != NULL) { fprintf(stderr, "[DEBUG]: " format "\n", ##__VA_ARGS__); } } +#define RAW_DEBUG(tag, format, ...) { const char *debug = getenv("MCPI_DEBUG"); if (debug != NULL) { fprintf(stderr, "[DEBUG]: %s" format "\n", tag, ##__VA_ARGS__); } } +#define DEBUG(format, ...) RAW_DEBUG(reborn_debug_tag, format, ##__VA_ARGS__) #define ERR(format, ...) { fprintf(stderr, "[ERR]: (%s:%i): " format "\n", __FILE__, __LINE__, ##__VA_ARGS__); exit(EXIT_FAILURE); } #define IMPOSSIBLE() ERR("This Should Never Be Called") diff --git a/libreborn/src/util/exec.c b/libreborn/src/util/exec.c index fbd96b8..358df34 100644 --- a/libreborn/src/util/exec.c +++ b/libreborn/src/util/exec.c @@ -82,6 +82,9 @@ char *run_command(const char *const command[], int *exit_status) { } else if (ret == 0) { // Child Process + // Set Debug Tag + reborn_debug_tag = CHILD_PROCESS_TAG; + // Pipe stdout dup2(output_pipe[1], STDOUT_FILENO); close(output_pipe[0]); diff --git a/libreborn/src/util/log.c b/libreborn/src/util/log.c new file mode 100644 index 0000000..83bfd4f --- /dev/null +++ b/libreborn/src/util/log.c @@ -0,0 +1,4 @@ +#include + +// Debug Tag +const char *reborn_debug_tag = ""; diff --git a/media-layer/proxy/src/client/client.cpp b/media-layer/proxy/src/client/client.cpp index def2b5d..f3b43bf 100644 --- a/media-layer/proxy/src/client/client.cpp +++ b/media-layer/proxy/src/client/client.cpp @@ -45,6 +45,9 @@ static void exit_handler(__attribute__((unused)) int signal_id) { // Main int main(int argc, char *argv[]) { + // Set Debug Tag + reborn_debug_tag = PROXY_LOG_TAG; + // Install Signal Handlers signal(SIGINT, SIG_IGN); struct sigaction act_sigterm; diff --git a/media-layer/proxy/src/common/common.h b/media-layer/proxy/src/common/common.h index 23fa672..78e07f2 100644 --- a/media-layer/proxy/src/common/common.h +++ b/media-layer/proxy/src/common/common.h @@ -22,7 +22,7 @@ extern "C" { #define CONNECTED_MSG "Connected" -#define PROXY_INFO(format, ...) DEBUG(PROXY_LOG_TAG format, ##__VA_ARGS__); +#define PROXY_INFO(format, ...) RAW_DEBUG(PROXY_LOG_TAG, format, ##__VA_ARGS__); #define PROXY_ERR(format, ...) { close_connection(); ERR(PROXY_LOG_TAG format, ##__VA_ARGS__); } // Safely Send/Receive Data From The Connection diff --git a/media-layer/proxy/src/server/server.cpp b/media-layer/proxy/src/server/server.cpp index aad5169..8bb7b21 100644 --- a/media-layer/proxy/src/server/server.cpp +++ b/media-layer/proxy/src/server/server.cpp @@ -64,6 +64,9 @@ static void start_media_layer_proxy_client(int read, int write) { } else if (ret == 0) { // Child Process + // Set Debug Tag + reborn_debug_tag = CHILD_PROCESS_TAG; + // Prepare Arguments char *read_str = NULL; safe_asprintf(&read_str, "%i", read); diff --git a/scripts/test.sh b/scripts/test.sh index d8e8558..053065f 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -31,4 +31,4 @@ export _MCPI_SKIP_ROOT_CHECK=1 # Run Benchmark export HOME="$(pwd)/build/test" -minecraft-pi-reborn-client --default --benchmark +minecraft-pi-reborn-client --default --no-cache --benchmark