Cache Launcher Configuration

This commit is contained in:
TheBrokenRail 2022-09-22 17:43:21 -04:00
parent 02c73176a5
commit f328800ce8
17 changed files with 238 additions and 17 deletions

View File

@ -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``

View File

@ -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

View File

@ -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);

View File

@ -0,0 +1,137 @@
#include <cstdlib>
#include <string>
#include <fstream>
#include <unordered_map>
#include <sstream>
#include <sys/stat.h>
#include <libreborn/libreborn.h>
#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<std::string, bool> 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");
}
}

View File

@ -0,0 +1,19 @@
#pragma once
#include <string>
#include <unordered_map>
// Cache Version
#define CACHE_VERSION 0
// Load Cache
typedef struct {
std::string username;
std::string render_distance;
std::unordered_map<std::string, bool> feature_flags;
} launcher_cache;
extern launcher_cache empty_cache;
launcher_cache load_cache();
// Save Cache
void save_cache();

View File

@ -10,9 +10,11 @@
#include <libreborn/libreborn.h>
#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<void(std::string)> callback) {
void load_available_feature_flags(std::function<void(std::string)> 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<std::string()>
}
}
// 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<std::string> 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);
}

View File

@ -0,0 +1,12 @@
#pragma once
#include <string>
#include <functional>
// 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<void(std::string)> callback);

View File

@ -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];

View File

@ -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

View File

@ -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)

View File

@ -3,9 +3,13 @@
#include <stdio.h>
#include <stdlib.h>
// 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")

View File

@ -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]);

4
libreborn/src/util/log.c Normal file
View File

@ -0,0 +1,4 @@
#include <libreborn/log.h>
// Debug Tag
const char *reborn_debug_tag = "";

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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