Environmental Variable & Logging Refactor

This commit is contained in:
TheBrokenRail 2024-06-17 18:09:30 -04:00
parent b2db6bcfd2
commit 3b5149abff
39 changed files with 382 additions and 373 deletions

View File

@ -4,8 +4,8 @@ project(launcher)
add_executable(launcher add_executable(launcher
src/bootstrap.cpp src/bootstrap.cpp
src/patchelf.cpp src/patchelf.cpp
src/util.c src/util.cpp
src/crash-report.c src/crash-report.cpp
src/sdk.cpp src/sdk.cpp
src/mods.cpp src/mods.cpp
src/options/parser.cpp src/options/parser.cpp

View File

@ -41,14 +41,17 @@ static void print_debug_information() {
DEBUG("Reborn Version: v%s", MCPI_VERSION); DEBUG("Reborn Version: v%s", MCPI_VERSION);
// Architecture // Architecture
const char *arch = "Unknown"; const char *arch =
#ifdef __x86_64__ #ifdef __x86_64__
arch = "AMD64"; "AMD64"
#elif defined(__aarch64__) #elif defined(__aarch64__)
arch = "ARM64"; "ARM64"
#elif defined(__arm__) #elif defined(__arm__)
arch = "ARM32"; "ARM32"
#else
"Unknown"
#endif #endif
;
DEBUG("Reborn Target Architecture: %s", arch); DEBUG("Reborn Target Architecture: %s", arch);
} }
@ -66,9 +69,7 @@ void bootstrap() {
#endif #endif
// Get Binary Directory // Get Binary Directory
char *binary_directory_raw = get_binary_directory(); const std::string binary_directory = get_binary_directory();
const std::string binary_directory = binary_directory_raw;
free(binary_directory_raw);
DEBUG("Binary Directory: %s", binary_directory.c_str()); DEBUG("Binary Directory: %s", binary_directory.c_str());
// Copy SDK // Copy SDK
@ -78,24 +79,21 @@ void bootstrap() {
// Set MCPI_REBORN_ASSETS_PATH // Set MCPI_REBORN_ASSETS_PATH
{ {
char *assets_path = realpath("/proc/self/exe", nullptr); std::string assets_path = safe_realpath("/proc/self/exe");
ALLOC_CHECK(assets_path); chop_last_component(assets_path);
chop_last_component(&assets_path); assets_path += "/data";
string_append(&assets_path, "/data"); set_and_print_env(_MCPI_REBORN_ASSETS_PATH_ENV, assets_path.c_str());
set_and_print_env("MCPI_REBORN_ASSETS_PATH", assets_path);
free(assets_path);
} }
// Resolve Binary Path & Set MCPI_DIRECTORY // Resolve Binary Path & Set MCPI_DIRECTORY
char *resolved_path = nullptr; std::string game_binary;
{ {
// Log // Log
DEBUG("Resolving File Paths..."); DEBUG("Resolving File Paths...");
// Resolve Full Binary Path // Resolve Full Binary Path
const std::string full_path = binary_directory + ("/" MCPI_BINARY); const std::string full_path = binary_directory + ("/" MCPI_BINARY);
resolved_path = realpath(full_path.c_str(), nullptr); game_binary = safe_realpath(full_path);
ALLOC_CHECK(resolved_path);
} }
// Fix MCPI Dependencies // Fix MCPI Dependencies
@ -113,7 +111,7 @@ void bootstrap() {
#endif #endif
// Patch // Patch
patch_mcpi_elf_dependencies(resolved_path, new_mcpi_exe_path); patch_mcpi_elf_dependencies(game_binary.c_str(), new_mcpi_exe_path);
// Verify // Verify
if (!starts_with(new_mcpi_exe_path, MCPI_PATCHED_DIR)) { if (!starts_with(new_mcpi_exe_path, MCPI_PATCHED_DIR)) {
@ -123,17 +121,12 @@ void bootstrap() {
// Set MCPI_VANILLA_ASSETS_PATH // Set MCPI_VANILLA_ASSETS_PATH
{ {
char *assets_path = strdup(resolved_path); std::string assets_path = game_binary;
ALLOC_CHECK(assets_path); chop_last_component(assets_path);
chop_last_component(&assets_path); assets_path += "/data";
string_append(&assets_path, "/data"); set_and_print_env(_MCPI_VANILLA_ASSETS_PATH_ENV, assets_path.c_str());
set_and_print_env("MCPI_VANILLA_ASSETS_PATH", assets_path);
free(assets_path);
} }
// Free Resolved Path
free(resolved_path);
// Configure Library Search Path // Configure Library Search Path
std::string mcpi_ld_path = ""; std::string mcpi_ld_path = "";
{ {

View File

@ -13,7 +13,7 @@
// Get Cache Path // Get Cache Path
static std::string get_cache_path() { static std::string get_cache_path() {
const char *home = getenv("HOME"); const char *home = getenv(_MCPI_HOME_ENV);
if (home == nullptr) { if (home == nullptr) {
IMPOSSIBLE(); IMPOSSIBLE();
} }
@ -120,18 +120,18 @@ void save_cache() {
stream.write((const char *) &cache_version, 1); stream.write((const char *) &cache_version, 1);
// Save Username And Render Distance // Save Username And Render Distance
write_env_to_stream(stream, "MCPI_USERNAME"); write_env_to_stream(stream, MCPI_USERNAME_ENV);
write_env_to_stream(stream, "MCPI_RENDER_DISTANCE"); write_env_to_stream(stream, MCPI_RENDER_DISTANCE_ENV);
// Save Feature Flags // Save Feature Flags
std::unordered_map<std::string, bool> flags; std::unordered_map<std::string, bool> flags;
load_available_feature_flags([&flags](std::string flag) { load_available_feature_flags([&flags](std::string flag) {
std::string stripped_flag = strip_feature_flag_default(flag, NULL); std::string stripped_flag = strip_feature_flag_default(flag, nullptr);
flags[stripped_flag] = false; flags[stripped_flag] = false;
}); });
{ {
const char *enabled_flags = getenv("MCPI_FEATURE_FLAGS"); const char *enabled_flags = getenv(MCPI_FEATURE_FLAGS_ENV);
if (enabled_flags == NULL) { if (enabled_flags == nullptr) {
IMPOSSIBLE(); IMPOSSIBLE();
} }
std::istringstream enabled_flags_stream(enabled_flags); std::istringstream enabled_flags_stream(enabled_flags);

View File

@ -41,12 +41,8 @@ std::string strip_feature_flag_default(const std::string &flag, bool *default_re
extern unsigned char available_feature_flags[]; extern unsigned char available_feature_flags[];
extern size_t available_feature_flags_len; extern size_t available_feature_flags_len;
void load_available_feature_flags(const std::function<void(std::string)> &callback) { void load_available_feature_flags(const std::function<void(std::string)> &callback) {
// Get Path // Load Data
char *binary_directory = get_binary_directory(); const std::string data(available_feature_flags, available_feature_flags + available_feature_flags_len);
std::string path = std::string(binary_directory) + "/available-feature-flags";
free(binary_directory);
// Load File
std::string data(available_feature_flags, available_feature_flags + available_feature_flags_len);
std::stringstream stream(data); std::stringstream stream(data);
// Store Lines // Store Lines
std::vector<std::string> lines; std::vector<std::string> lines;
@ -68,8 +64,8 @@ void load_available_feature_flags(const std::function<void(std::string)> &callba
// Sort // Sort
std::sort(lines.begin(), lines.end(), [](const std::string &a, const std::string &b) { std::sort(lines.begin(), lines.end(), [](const std::string &a, const std::string &b) {
// Strip Defaults // Strip Defaults
std::string stripped_a = strip_feature_flag_default(a, nullptr); const std::string stripped_a = strip_feature_flag_default(a, nullptr);
std::string stripped_b = strip_feature_flag_default(b, nullptr); const std::string stripped_b = strip_feature_flag_default(b, nullptr);
// Sort // Sort
return stripped_a < stripped_b; return stripped_a < stripped_b;
}); });
@ -90,7 +86,7 @@ static void run_command_and_set_env(const char *env_name, const char *command[])
char *output = run_command(command, &return_code, nullptr); char *output = run_command(command, &return_code, nullptr);
if (output != nullptr) { if (output != nullptr) {
// Trim // Trim
int length = strlen(output); const size_t length = strlen(output);
if (output[length - 1] == '\n') { if (output[length - 1] == '\n') {
output[length - 1] = '\0'; output[length - 1] = '\0';
} }
@ -164,7 +160,7 @@ void configure_client(const options_t &options) {
// --default // --default
if (options.use_default) { if (options.use_default) {
// Use Default Feature Flags // Use Default Feature Flags
set_env_if_unset("MCPI_FEATURE_FLAGS", [&cache]() { set_env_if_unset(MCPI_FEATURE_FLAGS_ENV, [&cache]() {
std::string feature_flags = ""; std::string feature_flags = "";
load_available_feature_flags([&feature_flags, &cache](const std::string &flag) { load_available_feature_flags([&feature_flags, &cache](const std::string &flag) {
bool value; bool value;
@ -185,10 +181,10 @@ void configure_client(const options_t &options) {
} }
return feature_flags; return feature_flags;
}); });
set_env_if_unset("MCPI_RENDER_DISTANCE", [&cache]() { set_env_if_unset(MCPI_RENDER_DISTANCE_ENV, [&cache]() {
return cache.render_distance; return cache.render_distance;
}); });
set_env_if_unset("MCPI_USERNAME", [&cache]() { set_env_if_unset(MCPI_USERNAME_ENV, [&cache]() {
return cache.username; return cache.username;
}); });
} }
@ -226,7 +222,7 @@ void configure_client(const options_t &options) {
command.push_back(stripped_flag); command.push_back(stripped_flag);
}); });
// Run // Run
run_zenity_and_set_env("MCPI_FEATURE_FLAGS", command); run_zenity_and_set_env(MCPI_FEATURE_FLAGS_ENV, command);
} }
// Setup MCPI_RENDER_DISTANCE // Setup MCPI_RENDER_DISTANCE
{ {
@ -249,7 +245,7 @@ void configure_client(const options_t &options) {
command.push_back(render_distance); command.push_back(render_distance);
} }
// Run // Run
run_zenity_and_set_env("MCPI_RENDER_DISTANCE", command); run_zenity_and_set_env(MCPI_RENDER_DISTANCE_ENV, command);
} }
// Setup MCPI_USERNAME // Setup MCPI_USERNAME
{ {
@ -260,7 +256,7 @@ void configure_client(const options_t &options) {
command.push_back("--entry-text"); command.push_back("--entry-text");
command.push_back(cache.username); command.push_back(cache.username);
// Run // Run
run_zenity_and_set_env("MCPI_USERNAME", command); run_zenity_and_set_env(MCPI_USERNAME_ENV, command);
} }
// Save Cache // Save Cache

View File

@ -1,14 +1,16 @@
#include <unistd.h> #include <unistd.h>
#include <string.h> #include <cstring>
#include <errno.h> #include <cerrno>
#include <stdlib.h> #include <cstdlib>
#include <stdio.h> #include <cstdio>
#include <stdint.h> #include <cstdint>
#include <signal.h> #include <csignal>
#include <poll.h> #include <poll.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/prctl.h> #include <sys/prctl.h>
#include <ctime>
#include <string>
#include <libreborn/libreborn.h> #include <libreborn/libreborn.h>
@ -47,39 +49,67 @@ static void show_report(const char *log_filename) {
} }
// Exit Handler // Exit Handler
static pid_t child_pid = -1;
static void exit_handler(__attribute__((unused)) int signal) { static void exit_handler(__attribute__((unused)) int signal) {
// Murder // Murder
murder_children(); kill(child_pid, SIGTERM);
}
// Log File
static std::string log_filename;
static int log_fd;
static void setup_log_file() {
// Get Log Directory
const std::string home = std::string(getenv(_MCPI_HOME_ENV)) + get_home_subdirectory_for_game_data();
ensure_directory(home.c_str());
const std::string logs = home + "/logs";
ensure_directory(logs.c_str());
// Get Timestamp
time_t raw_time;
time(&raw_time);
tm *time_info = localtime(&raw_time);
char time[512];
strftime(time, 512, "%Y-%m-%d", time_info);
// Get Log Filename
std::string file;
int num = 1;
do {
file = std::string(time) + '-' + std::to_string(num) + ".log";
log_filename = logs + '/' + file;
num++;
} while (access(log_filename.c_str(), F_OK) != -1);
// Create latest.log Symlink
const std::string latest_log = logs + "/latest.log";
unlink(latest_log.c_str());
if (symlink(file.c_str(), latest_log.c_str()) != 0) {
WARN("Unable To Create Latest Log Symlink: %s", strerror(errno));
}
// Create File
log_fd = open(log_filename.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (log_fd == -1) {
ERR("Unable To Create Log File: %s", strerror(errno));
}
reborn_set_log(log_fd);
} }
// Setup // Setup
#define PIPE_READ 0 #define PIPE_READ 0
#define PIPE_WRITE 1 #define PIPE_WRITE 1
#define MCPI_LOGS_DIR "/tmp/.minecraft-pi-logs" #define BUFFER_SIZE 1024
static char log_filename[] = MCPI_LOGS_DIR "/XXXXXX"; static void safe_write(int fd, const void *buf, size_t size) {
void setup_log_file() { const ssize_t bytes_written = write(fd, buf, size);
// Ensure Temporary Directory if (bytes_written < 0) {
{ ERR("Unable To Write Data: %s", strerror(errno));
// Check If It Exists
struct stat tmp_stat;
int exists = stat(MCPI_LOGS_DIR, &tmp_stat) != 0 ? 0 : S_ISDIR(tmp_stat.st_mode);
if (!exists) {
// Doesn't Exist
if (mkdir(MCPI_LOGS_DIR, S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
ERR("Unable To Create Temporary Folder: %s", strerror(errno));
}
}
} }
// Create Temporary File
int log_file_fd = mkstemp(log_filename);
if (log_file_fd == -1) {
ERR("Unable To Create Log File: %s", strerror(errno));
}
close(log_file_fd);
reborn_set_log(log_filename);
} }
void setup_crash_report() { void setup_crash_report() {
// Setup Logging
setup_log_file();
// Store Output // Store Output
int output_pipe[2]; int output_pipe[2];
safe_pipe2(output_pipe, 0); safe_pipe2(output_pipe, 0);
@ -114,19 +144,16 @@ void setup_crash_report() {
// Continue Execution // Continue Execution
} else { } else {
// Parent Process
track_child(ret);
// Install Signal Handlers // Install Signal Handlers
struct sigaction act_sigint = {0}; child_pid = ret;
struct sigaction act_sigint = {};
act_sigint.sa_flags = SA_RESTART; act_sigint.sa_flags = SA_RESTART;
act_sigint.sa_handler = &exit_handler; act_sigint.sa_handler = &exit_handler;
sigaction(SIGINT, &act_sigint, NULL); sigaction(SIGINT, &act_sigint, nullptr);
struct sigaction act_sigterm = {0}; struct sigaction act_sigterm = {};
act_sigterm.sa_flags = SA_RESTART; act_sigterm.sa_flags = SA_RESTART;
act_sigterm.sa_handler = &exit_handler; act_sigterm.sa_handler = &exit_handler;
sigaction(SIGTERM, &act_sigterm, NULL); sigaction(SIGTERM, &act_sigterm, nullptr);
atexit(murder_children);
// Close Unneeded File Descriptors // Close Unneeded File Descriptors
close(output_pipe[PIPE_WRITE]); close(output_pipe[PIPE_WRITE]);
@ -136,24 +163,20 @@ void setup_crash_report() {
// Set Debug Tag // Set Debug Tag
reborn_debug_tag = "(Crash Reporter) "; reborn_debug_tag = "(Crash Reporter) ";
// Setup Logging
#define BUFFER_SIZE 1024
char buf[BUFFER_SIZE];
// Setup Polling // Setup Polling
int number_fds = 3; const int number_fds = 3;
struct pollfd poll_fds[number_fds]; pollfd poll_fds[number_fds];
poll_fds[0].fd = output_pipe[PIPE_READ]; poll_fds[0].fd = output_pipe[PIPE_READ];
poll_fds[1].fd = error_pipe[PIPE_READ]; poll_fds[1].fd = error_pipe[PIPE_READ];
poll_fds[2].fd = STDIN_FILENO; poll_fds[2].fd = STDIN_FILENO;
for (int i = 0; i < number_fds; i++) { for (pollfd &poll_fd : poll_fds) {
poll_fds[i].events = POLLIN; poll_fd.events = POLLIN;
} }
// Poll Data // Poll Data
int status; int status;
while (waitpid(ret, &status, WNOHANG) != ret) { while (waitpid(ret, &status, WNOHANG) != ret) {
int poll_ret = poll(poll_fds, number_fds, -1); const int poll_ret = poll(poll_fds, number_fds, -1);
if (poll_ret == -1) { if (poll_ret == -1) {
if (errno == EINTR) { if (errno == EINTR) {
continue; continue;
@ -163,86 +186,71 @@ void setup_crash_report() {
} }
// Handle Data // Handle Data
for (int i = 0; i < number_fds; i++) { for (pollfd &poll_fd : poll_fds) {
if (poll_fds[i].revents != 0) { if (poll_fd.revents != 0) {
if (poll_fds[i].revents & POLLIN) { if (poll_fd.revents & POLLIN) {
if (poll_fds[i].fd == STDIN_FILENO) { char buf[BUFFER_SIZE];
if (poll_fd.fd == STDIN_FILENO) {
// Data Available From stdin // Data Available From stdin
int bytes_available; int bytes_available;
if (ioctl(fileno(stdin), FIONREAD, &bytes_available) == -1) { if (ioctl(fileno(stdin), FIONREAD, &bytes_available) == -1) {
bytes_available = 0; bytes_available = 0;
} }
// Read // Read
ssize_t bytes_read = read(poll_fds[i].fd, buf, BUFFER_SIZE); const ssize_t bytes_read = read(poll_fd.fd, buf, BUFFER_SIZE);
if (bytes_read == -1) { if (bytes_read == -1) {
ERR("Unable To Read Input: %s", strerror(errno)); ERR("Unable To Read Input: %s", strerror(errno));
} }
// Write To Child // Write To Child
if (write(input_pipe[PIPE_WRITE], buf, bytes_read) == -1) { safe_write(input_pipe[PIPE_WRITE], buf, bytes_read);
ERR("Unable To Write Input To Child: %s", strerror(errno));
}
} else { } else {
// Data Available From Child's stdout/stderr // Data Available From Child's stdout/stderr
ssize_t bytes_read = read(poll_fds[i].fd, buf, BUFFER_SIZE - 1 /* Account For NULL-Terminator */); const ssize_t bytes_read = read(poll_fd.fd, buf, BUFFER_SIZE);
if (bytes_read == -1) { if (bytes_read == -1) {
ERR("Unable To Read Log Data: %s", strerror(errno)); ERR("Unable To Read Log Data: %s", strerror(errno));
} }
// Print To Terminal // Print To Terminal
buf[bytes_read] = '\0'; safe_write(poll_fd.fd == output_pipe[PIPE_READ] ? STDOUT_FILENO : STDERR_FILENO, buf, bytes_read);
fprintf(poll_fds[i].fd == output_pipe[PIPE_READ] ? stdout : stderr, "%s", buf);
// Write To log // Write To log
if (write(reborn_get_log_fd(), buf, bytes_read) == -1) { safe_write(reborn_get_log_fd(), buf, bytes_read);
ERR("Unable To Write Log Data: %s", strerror(errno));
}
} }
} else { } else {
// File Descriptor No Longer Accessible // File Descriptor No Longer Accessible
poll_fds[i].events = 0; poll_fd.events = 0;
} }
} }
} }
} }
// Untrack Process
untrack_child(ret);
// Close Pipes // Close Pipes
close(output_pipe[PIPE_READ]); close(output_pipe[PIPE_READ]);
close(error_pipe[PIPE_READ]); close(error_pipe[PIPE_READ]);
close(input_pipe[PIPE_WRITE]); close(input_pipe[PIPE_WRITE]);
// Check If Is Crash // Check If Is Crash
int is_crash = !is_exit_status_success(status); const bool is_crash = !is_exit_status_success(status);
// Log Exit Code To log If Crash // Log Exit Code To log If Crash
if (is_crash) { if (is_crash) {
// Create Exit Code Log Line // Create Exit Code Log Line
char *exit_status = NULL; char *exit_status = nullptr;
get_exit_status_string(status, &exit_status); get_exit_status_string(status, &exit_status);
char *exit_code_line = NULL; const std::string exit_code_line = "[CRASH]: Terminated" + std::string(exit_status) + '\n';
safe_asprintf(&exit_code_line, "[CRASH]: Terminated%s\n", exit_status);
free(exit_status); free(exit_status);
// Print Exit Code Log Line // Print Exit Code Log Line
fprintf(stderr, "%s", exit_code_line); safe_write(STDERR_FILENO, exit_code_line.c_str(), strlen(exit_code_line.c_str()));
// Write Exit Code Log Line // Write Exit Code Log Line
if (write(reborn_get_log_fd(), exit_code_line, strlen(exit_code_line)) == -1) { safe_write(reborn_get_debug_fd(), exit_code_line.c_str(), strlen(exit_code_line.c_str()));
ERR("Unable To Write Exit Code To Log: %s", strerror(errno));
}
// Free Exit Code Log Line
free(exit_code_line);
} }
// Close Log File // Close Log File
reborn_close_log(); close(log_fd);
unsetenv(MCPI_LOG_ENV); unsetenv(_MCPI_LOG_FD_ENV);
// Show Crash Log // Show Crash Log
if (is_crash && !reborn_is_headless()) { if (is_crash && !reborn_is_headless()) {
show_report(log_filename); show_report(log_filename.c_str());
} }
// Exit // Exit

View File

@ -4,7 +4,6 @@
extern "C" { extern "C" {
#endif #endif
void setup_log_file();
void setup_crash_report(); void setup_crash_report();
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -10,18 +10,20 @@
// Bind Options To Environmental Variable // Bind Options To Environmental Variable
static void bind_to_env(const char *env, const bool value) { static void bind_to_env(const char *env, const bool value) {
const bool force = env[0] == '_'; if (value) {
if (force || value) { set_and_print_env(env, "1");
set_and_print_env(env, value ? "1" : nullptr);
} }
} }
static void setup_environment(const options_t &options) { static void setup_environment(const options_t &options) {
// Clear Internal Variables
clear_internal_env_vars();
// Passthrough Options To Game // Passthrough Options To Game
bind_to_env(MCPI_SERVER_MODE_ENV, options.server_mode); bind_to_env(_MCPI_SERVER_MODE_ENV, options.server_mode);
bind_to_env("_MCPI_BENCHMARK", options.benchmark); bind_to_env(_MCPI_BENCHMARK_ENV, options.benchmark);
bind_to_env("_MCPI_ONLY_GENERATE", options.only_generate); bind_to_env(_MCPI_ONLY_GENERATE_ENV, options.only_generate);
bind_to_env(MCPI_FORCE_HEADLESS_ENV, options.force_headless); bind_to_env(_MCPI_FORCE_HEADLESS_ENV, options.force_headless);
bind_to_env(MCPI_FORCE_NON_HEADLESS_ENV, options.force_non_headless); bind_to_env(_MCPI_FORCE_NON_HEADLESS_ENV, options.force_non_headless);
// GTK Dark Mode // GTK Dark Mode
set_and_print_env("GTK_THEME", "Adwaita:dark"); set_and_print_env("GTK_THEME", "Adwaita:dark");
@ -29,12 +31,11 @@ static void setup_environment(const options_t &options) {
// Configure PATH // Configure PATH
{ {
// Get Binary Directory // Get Binary Directory
char *binary_directory = get_binary_directory(); const std::string binary_directory = get_binary_directory();
std::string new_path = std::string(binary_directory) + "/bin"; std::string new_path = binary_directory + "/bin";
free(binary_directory);
// Add Existing PATH // Add Existing PATH
{ {
char *value = getenv("PATH"); const char *value = getenv("PATH");
if (value != nullptr && strlen(value) > 0) { if (value != nullptr && strlen(value) > 0) {
new_path += std::string(":") + value; new_path += std::string(":") + value;
} }
@ -42,27 +43,37 @@ static void setup_environment(const options_t &options) {
// Set And Free // Set And Free
set_and_print_env("PATH", new_path.c_str()); set_and_print_env("PATH", new_path.c_str());
} }
// Setup MCPI_HOME
if (!reborn_is_server()) {
// Ensure $HOME
const char *home = getenv("HOME");
if (home == nullptr) {
ERR("$HOME Is Not Set");
}
set_and_print_env(_MCPI_HOME_ENV, home);
} else {
// Set Home To Current Directory, So World Data Is Stored There
char *launch_directory = getcwd(nullptr, 0);
ALLOC_CHECK(launch_directory);
set_and_print_env(_MCPI_HOME_ENV, launch_directory);
free(launch_directory);
}
// Create If Needed
const std::string minecraft_folder = std::string(getenv(_MCPI_HOME_ENV)) + get_home_subdirectory_for_game_data();
ensure_directory(minecraft_folder.c_str());
} }
// Non-Launch Commands // Non-Launch Commands
static void handle_non_launch_commands(const options_t &options) { static void handle_non_launch_commands(const options_t &options) {
if (options.copy_sdk) { if (options.copy_sdk) {
char *binary_directory = get_binary_directory(); const std::string binary_directory = get_binary_directory();
copy_sdk(binary_directory, false); copy_sdk(binary_directory, false);
free(binary_directory);
fflush(stdout); fflush(stdout);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
} }
// Exit Handler
static void exit_handler(__attribute__((unused)) int signal_id) {
// Pass Signal To Child
murder_children();
while (wait(nullptr) > 0) {}
_exit(EXIT_SUCCESS);
}
// Start The Game // Start The Game
static void start_game(const options_t &options) { static void start_game(const options_t &options) {
// Disable stdout Buffering // Disable stdout Buffering
@ -70,46 +81,9 @@ static void start_game(const options_t &options) {
// Setup Crash Reporting // Setup Crash Reporting
if (!options.disable_crash_report) { if (!options.disable_crash_report) {
setup_log_file();
setup_crash_report(); setup_crash_report();
} }
// Install Signal Handlers
struct sigaction act_sigint = {};
act_sigint.sa_flags = SA_RESTART;
act_sigint.sa_handler = &exit_handler;
sigaction(SIGINT, &act_sigint, nullptr);
struct sigaction act_sigterm = {};
act_sigterm.sa_flags = SA_RESTART;
act_sigterm.sa_handler = &exit_handler;
sigaction(SIGTERM, &act_sigterm, nullptr);
// Setup Home
if (!reborn_is_server()) {
// Ensure $HOME
const char *home = getenv("HOME");
if (home == nullptr) {
ERR("$HOME Is Not Set");
}
// Create If Needed
{
std::string minecraft_folder = std::string(home) + get_home_subdirectory_for_game_data();
struct stat tmp_stat = {};
bool exists = stat(minecraft_folder.c_str(), &tmp_stat) != 0 ? false : S_ISDIR(tmp_stat.st_mode);
if (!exists) {
// Doesn't Exist
if (mkdir(minecraft_folder.c_str(), S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
ERR("Unable To Create Data Directory: %s", strerror(errno));
}
}
}
} else {
// Set Home To Current Directory, So World Data Is Stored There
char *launch_directory = getcwd(nullptr, 0);
set_and_print_env("HOME", launch_directory);
free(launch_directory);
}
// Configure Client Options // Configure Client Options
if (!reborn_is_server()) { if (!reborn_is_server()) {
configure_client(options); configure_client(options);
@ -128,7 +102,7 @@ int main(int argc, char *argv[]) {
reborn_debug_tag = "(Launcher) "; reborn_debug_tag = "(Launcher) ";
// Debug Logging // Debug Logging
unsetenv(MCPI_LOG_ENV); unsetenv(_MCPI_LOG_FD_ENV);
bind_to_env(MCPI_DEBUG_ENV, options.debug); bind_to_env(MCPI_DEBUG_ENV, options.debug);
// Setup Environment // Setup Environment

View File

@ -10,6 +10,7 @@
// Get All Mods In Folder // Get All Mods In Folder
static void load(std::string &ld_preload, const std::string &folder) { static void load(std::string &ld_preload, const std::string &folder) {
// Open Folder // Open Folder
ensure_directory(folder.c_str());
DIR *dp = opendir(folder.c_str()); DIR *dp = opendir(folder.c_str());
if (dp != nullptr) { if (dp != nullptr) {
// Loop Through Folder // Loop Through Folder
@ -43,8 +44,6 @@ static void load(std::string &ld_preload, const std::string &folder) {
} }
// Close Folder // Close Folder
closedir(dp); closedir(dp);
} else if (errno == ENOENT) {
// Folder Doesn't Exist
} else { } else {
// Unable To Open Folder // Unable To Open Folder
ERR("Error Opening Directory: %s: %s", folder.c_str(), strerror(errno)); ERR("Error Opening Directory: %s: %s", folder.c_str(), strerror(errno));
@ -60,7 +59,7 @@ std::string bootstrap_mods(const std::string &binary_directory) {
// ~/.minecraft-pi/mods // ~/.minecraft-pi/mods
{ {
// Get Mods Folder // Get Mods Folder
std::string mods_folder = std::string(getenv("HOME")) + get_home_subdirectory_for_game_data() + SUBDIRECTORY_FOR_MODS; std::string mods_folder = std::string(getenv(_MCPI_HOME_ENV)) + get_home_subdirectory_for_game_data() + SUBDIRECTORY_FOR_MODS;
// Load Mods From ./mods // Load Mods From ./mods
load(preload, mods_folder); load(preload, mods_folder);
} }

View File

@ -1,4 +1,4 @@
OPTION(debug, "debug", 'd', "Enable Debug Logging (" MCPI_DEBUG_ENV ")") OPTION(debug, "debug", 'd', "Enable Debug Logging")
OPTION(copy_sdk, "copy-sdk", -2, "Extract Modding SDK And Exit") OPTION(copy_sdk, "copy-sdk", -2, "Extract Modding SDK And Exit")
OPTION(disable_crash_report, "disable-crash-report", -1, "Disable Crash Report Dialog") OPTION(disable_crash_report, "disable-crash-report", -1, "Disable Crash Report Dialog")
OPTION(use_default, "default", -3, "Skip Client-Mode Configuration Dialogs") OPTION(use_default, "default", -3, "Skip Client-Mode Configuration Dialogs")

View File

@ -8,12 +8,19 @@ const char *argp_program_bug_address = "<" MCPI_DISCORD_INVITE ">";
static char doc[] = "Minecraft: Pi Edition Modding Project"; static char doc[] = "Minecraft: Pi Edition Modding Project";
// Options // Options
#define OPTION(ignored, name, key, doc) {name, key, nullptr, 0, doc, 0}, static int env_key = -100;
static argp_option options_data[] = { static argp_option options_data[] = {
{nullptr, 0, nullptr, 0, "Game Options:", 0},
#define OPTION(ignored, name, key, doc) {name, key, nullptr, 0, doc, 0},
#include "option-list.h" #include "option-list.h"
#undef OPTION
{nullptr, 0, nullptr, 0, "Environmental Variables:", 0},
#define ENV(name, doc) {#name, env_key--, nullptr, OPTION_DOC | OPTION_NO_USAGE | (is_env_var_internal(name##_ENV) ? OPTION_HIDDEN : 0), doc, 0},
#include <libreborn/env_list.h>
#undef ENV
{nullptr, 0, nullptr, 0, "Help Options:", -1},
{nullptr, 0, nullptr, 0, nullptr, 0} {nullptr, 0, nullptr, 0, nullptr, 0}
}; };
#undef OPTION
// Parse Options // Parse Options
#define OPTION(name, ignored, key, ...) \ #define OPTION(name, ignored, key, ...) \

View File

@ -13,17 +13,7 @@
// Duplicate MCPI Executable Into /tmp // Duplicate MCPI Executable Into /tmp
static void duplicate_mcpi_executable(char *new_path) { static void duplicate_mcpi_executable(char *new_path) {
// Ensure Temporary Directory // Ensure Temporary Directory
{ ensure_directory(MCPI_PATCHED_DIR);
// Check If It Exists
struct stat tmp_stat = {};
int exists = stat(MCPI_PATCHED_DIR, &tmp_stat) != 0 ? 0 : S_ISDIR(tmp_stat.st_mode);
if (!exists) {
// Doesn't Exist
if (mkdir(MCPI_PATCHED_DIR, S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
ERR("Unable To Create Temporary Folder: %s", strerror(errno));
}
}
}
// Generate New File // Generate New File
int new_file_fd = mkstemp(new_path); int new_file_fd = mkstemp(new_path);

View File

@ -19,7 +19,7 @@ void copy_sdk(const std::string &binary_directory, const bool log_with_debug) {
// Ensure SDK Directory // Ensure SDK Directory
std::string sdk_path; std::string sdk_path;
{ {
sdk_path = std::string(getenv("HOME")) + HOME_SUBDIRECTORY_FOR_SDK; sdk_path = std::string(getenv(_MCPI_HOME_ENV)) + HOME_SUBDIRECTORY_FOR_SDK;
const char *const command[] = {"mkdir", "-p", sdk_path.c_str(), nullptr}; const char *const command[] = {"mkdir", "-p", sdk_path.c_str(), nullptr};
run_simple_command(command, "Unable To Create SDK Directory"); run_simple_command(command, "Unable To Create SDK Directory");
} }

View File

@ -1,39 +0,0 @@
#include <libreborn/libreborn.h>
#include "util.h"
// Simpler Version Of run_command()
void run_simple_command(const char *const command[], const char *error) {
int status = 0;
char *output = run_command(command, &status, NULL);
if (output != NULL) {
free(output);
}
if (!is_exit_status_success(status)) {
ERR("%s", error);
}
}
// Chop Off Last Component
void chop_last_component(char **str) {
size_t length = strlen(*str);
for (size_t i = 0; i < length; i++) {
size_t j = length - i - 1;
if ((*str)[j] == '/') {
(*str)[j] = '\0';
break;
}
}
}
// Get Binary Directory (Remember To Free)
char *get_binary_directory() {
// Get Path To Current Executable
char *exe = realpath("/proc/self/exe", NULL);
ALLOC_CHECK(exe);
// Chop Off Last Component
chop_last_component(&exe);
// Return
return exe;
}

41
launcher/src/util.cpp Normal file
View File

@ -0,0 +1,41 @@
#include <libreborn/libreborn.h>
#include "util.h"
// Simpler Version Of run_command()
void run_simple_command(const char *const command[], const char *error) {
int status = 0;
char *output = run_command(command, &status, nullptr);
if (output != nullptr) {
free(output);
}
if (!is_exit_status_success(status)) {
ERR("%s", error);
}
}
// Chop Off Last Component
void chop_last_component(std::string &str) {
const std::string::size_type pos = str.find_last_of('/');
if (pos == std::string::npos) {
return;
}
str = str.substr(0, pos);
}
// Get Binary Directory (Remember To Free)
std::string safe_realpath(const std::string &path) {
char *raw = realpath(path.c_str(), nullptr);
ALLOC_CHECK(raw);
std::string str = raw;
free(raw);
return str;
}
std::string get_binary_directory() {
// Get Path To Current Executable
std::string exe = safe_realpath("/proc/self/exe");
// Chop Off Last Component
chop_last_component(exe);
// Return
return exe;
}

View File

@ -1,14 +1,9 @@
#pragma once #pragma once
#ifdef __cplusplus #include <string>
extern "C" {
#endif
void run_simple_command(const char *const command[], const char *error); void run_simple_command(const char *const command[], const char *error);
void chop_last_component(char **str); void chop_last_component(std::string &str);
char *get_binary_directory(); std::string safe_realpath(const std::string &path);
std::string get_binary_directory();
#ifdef __cplusplus
}
#endif

View File

@ -5,7 +5,14 @@ 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) configure_file(include/libreborn/config.h.in "${CMAKE_CURRENT_BINARY_DIR}/include/libreborn/config.h" ESCAPE_QUOTES @ONLY)
# Util # Util
add_library(reborn-util SHARED src/util/exec.c src/util/string.c src/util/util.c src/util/log.c src/util/cp437.cpp) add_library(reborn-util SHARED
src/util/exec.c
src/util/string.c
src/util/util.c
src/util/log.c
src/util/cp437.cpp
src/util/env.c
)
target_include_directories( target_include_directories(
reborn-util reborn-util
PUBLIC PUBLIC
@ -25,7 +32,12 @@ endif()
# Patch # Patch
if(BUILD_ARM_COMPONENTS) if(BUILD_ARM_COMPONENTS)
add_library(reborn-patch SHARED src/patch/patch.cpp src/patch/segments.cpp src/patch/code-block.cpp src/patch/instruction.cpp) add_library(reborn-patch SHARED
src/patch/patch.cpp
src/patch/segments.cpp
src/patch/code-block.cpp
src/patch/instruction.cpp
)
target_link_libraries(reborn-patch dl pthread reborn-util) target_link_libraries(reborn-patch dl pthread reborn-util)
target_compile_definitions(reborn-patch PUBLIC -DREBORN_HAS_PATCH_CODE) target_compile_definitions(reborn-patch PUBLIC -DREBORN_HAS_PATCH_CODE)
# Install # Install

View File

@ -0,0 +1,16 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#define ENV(name, ...) extern const char *name##_ENV;
#include "env_list.h"
#undef ENV
int is_env_var_internal(const char *env);
void clear_internal_env_vars();
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,24 @@
// Configure Client
ENV(MCPI_FEATURE_FLAGS, "Client-Mode Feature Flags")
ENV(MCPI_USERNAME, "Player Username")
ENV(MCPI_RENDER_DISTANCE, "Render Distance")
// Game Assets
ENV(_MCPI_REBORN_ASSETS_PATH, "")
ENV(_MCPI_VANILLA_ASSETS_PATH, "")
// Command Line Arguments
ENV(_MCPI_BENCHMARK, "")
ENV(_MCPI_ONLY_GENERATE, "")
// Logging
ENV(_MCPI_LOG_FD, "")
ENV(MCPI_DEBUG, "Enable Debug Logging")
// Server/Headless
ENV(_MCPI_SERVER_MODE, "")
ENV(_MCPI_FORCE_HEADLESS, "")
ENV(_MCPI_FORCE_NON_HEADLESS, "")
// Extra Configuration
ENV(MCPI_SKIN_SERVER, "Custom Skin Server")
ENV(MCPI_API_PORT, "Custom API Port")
ENV(MCPI_BLOCK_OUTLINE_WIDTH, "Custom Width For Block Outline (In Pixels)")
ENV(MCPI_GUI_SCALE, "Custom GUI Scale")
// $HOME
ENV(_MCPI_HOME, "")

View File

@ -32,11 +32,6 @@ char *run_command(const char *const command[], int *exit_status, size_t *output_
// Get Exit Status String // Get Exit Status String
void get_exit_status_string(int status, char **out); void get_exit_status_string(int status, char **out);
// Track Children
void track_child(pid_t pid);
void untrack_child(pid_t pid);
void murder_children();
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <libreborn/config.h> #include <libreborn/config.h>
#include "env.h"
#include "log.h" #include "log.h"
#include "util.h" #include "util.h"
#include "string.h" #include "string.h"

View File

@ -8,12 +8,9 @@ extern "C" {
#endif #endif
// Log File // Log File
#define MCPI_LOG_ENV "_MCPI_LOG"
int reborn_get_log_fd(); int reborn_get_log_fd();
void reborn_close_log(); void reborn_set_log(int fd);
void reborn_set_log(const char *file);
// Debug Logging // Debug Logging
#define MCPI_DEBUG_ENV "MCPI_DEBUG"
extern const char *reborn_debug_tag; extern const char *reborn_debug_tag;
int reborn_get_debug_fd(); int reborn_get_debug_fd();

View File

@ -51,9 +51,6 @@ int lock_file(const char *file);
void unlock_file(const char *file, int fd); void unlock_file(const char *file, int fd);
// Access Configuration At Runtime // Access Configuration At Runtime
#define MCPI_SERVER_MODE_ENV "_MCPI_SERVER_MODE"
#define MCPI_FORCE_HEADLESS_ENV "_MCPI_FORCE_HEADLESS"
#define MCPI_FORCE_NON_HEADLESS_ENV "_MCPI_FORCE_NON_HEADLESS"
const char *reborn_get_version(); const char *reborn_get_version();
int reborn_is_headless(); int reborn_is_headless();
int reborn_is_server(); int reborn_is_server();
@ -64,6 +61,9 @@ void reborn_check_display();
// Get Home Subdirectory // Get Home Subdirectory
const char *get_home_subdirectory_for_game_data(); const char *get_home_subdirectory_for_game_data();
// Make Sure Directory Exists
void ensure_directory(const char *path);
// Customize VTable // Customize VTable
#define CUSTOM_VTABLE(name, parent) \ #define CUSTOM_VTABLE(name, parent) \
void _setup_##name##_vtable(parent##_vtable *vtable); \ void _setup_##name##_vtable(parent##_vtable *vtable); \

20
libreborn/src/util/env.c Normal file
View File

@ -0,0 +1,20 @@
#include <libreborn/env.h>
#include <libreborn/exec.h>
// Define Constants
#define ENV(name, ...) const char *name##_ENV = #name;
#include <libreborn/env_list.h>
#undef ENV
// Clear Internal Variables
int is_env_var_internal(const char *env) {
return env[0] == '_';
}
void clear_internal_env_vars() {
#define ENV(name, ...) \
if (is_env_var_internal(name##_ENV)) { \
set_and_print_env(name##_ENV, NULL); \
}
#include <libreborn/env_list.h>
#undef ENV
}

View File

@ -1,4 +1,5 @@
#include <pthread.h> #include <pthread.h>
#include <sys/prctl.h>
#include <libreborn/exec.h> #include <libreborn/exec.h>
@ -57,12 +58,12 @@ char *run_command(const char *const command[], int *exit_status, size_t *output_
// Setup stderr // Setup stderr
dup2(reborn_get_debug_fd(), STDERR_FILENO); dup2(reborn_get_debug_fd(), STDERR_FILENO);
// Kill On Parent Death
prctl(PR_SET_PDEATHSIG, SIGKILL);
// Run // Run
safe_execvpe(command, (const char *const *) environ); safe_execvpe(command, (const char *const *) environ);
} else { } else {
// Parent Process
track_child(ret);
// Read stdout // Read stdout
close(output_pipe[1]); close(output_pipe[1]);
#define BUFFER_SIZE 1024 #define BUFFER_SIZE 1024
@ -75,7 +76,7 @@ char *run_command(const char *const command[], int *exit_status, size_t *output_
// Grow Output If Needed // Grow Output If Needed
size_t needed_size = position + bytes_read; size_t needed_size = position + bytes_read;
if (needed_size > size) { if (needed_size > size) {
// More Memeory Needed // More Memory Needed
size_t new_size = size; size_t new_size = size;
while (new_size < needed_size) { while (new_size < needed_size) {
new_size += BUFFER_SIZE; new_size += BUFFER_SIZE;
@ -98,7 +99,7 @@ char *run_command(const char *const command[], int *exit_status, size_t *output_
// Add NULL-Terminator To Output // Add NULL-Terminator To Output
size_t needed_size = position + 1; size_t needed_size = position + 1;
if (needed_size > size) { if (needed_size > size) {
// More Memeory Needed // More Memory Needed
size_t new_size = size + 1; size_t new_size = size + 1;
char *new_output = realloc(output, new_size); char *new_output = realloc(output, new_size);
if (new_output == NULL) { if (new_output == NULL) {
@ -117,7 +118,6 @@ char *run_command(const char *const command[], int *exit_status, size_t *output_
// Get Return Code // Get Return Code
int status; int status;
waitpid(ret, &status, 0); waitpid(ret, &status, 0);
untrack_child(ret);
if (exit_status != NULL) { if (exit_status != NULL) {
*exit_status = status; *exit_status = status;
} }
@ -140,36 +140,3 @@ void get_exit_status_string(int status, char **out) {
} }
} }
} }
// Track Children
#define MAX_CHILDREN 128
static pid_t children[MAX_CHILDREN] = { 0 };
static pthread_mutex_t children_lock = PTHREAD_MUTEX_INITIALIZER;
void track_child(pid_t pid) {
pthread_mutex_lock(&children_lock);
for (int i = 0; i < MAX_CHILDREN; i++) {
if (children[i] == 0) {
children[i] = pid;
break;
}
}
pthread_mutex_unlock(&children_lock);
}
void untrack_child(pid_t pid) {
pthread_mutex_lock(&children_lock);
for (int i = 0; i < MAX_CHILDREN; i++) {
if (children[i] == pid) {
children[i] = 0;
}
}
pthread_mutex_unlock(&children_lock);
}
void murder_children() {
pthread_mutex_lock(&children_lock);
for (int i = 0; i < MAX_CHILDREN; i++) {
if (children[i] != 0) {
kill(children[i], SIGTERM);
}
}
pthread_mutex_unlock(&children_lock);
}

View File

@ -5,6 +5,7 @@
#include <libreborn/log.h> #include <libreborn/log.h>
#include <libreborn/exec.h> #include <libreborn/exec.h>
#include <libreborn/env.h>
// Debug Tag // Debug Tag
const char *reborn_debug_tag = ""; const char *reborn_debug_tag = "";
@ -16,11 +17,8 @@ int reborn_get_log_fd() {
return log_fd; return log_fd;
} }
// Open Log File // Open Log File
const char *file = getenv(MCPI_LOG_ENV); const char *fd_str = getenv(_MCPI_LOG_FD_ENV);
if (file == NULL) { log_fd = fd_str ? atoi(fd_str) : open("/dev/null", O_WRONLY | O_APPEND);
file = "/dev/null";
}
log_fd = open(file, O_WRONLY | O_APPEND | O_CLOEXEC);
// Check FD // Check FD
if (log_fd < 0) { if (log_fd < 0) {
ERR("Unable To Open Log: %s", strerror(errno)); ERR("Unable To Open Log: %s", strerror(errno));
@ -28,17 +26,12 @@ int reborn_get_log_fd() {
// Return // Return
return reborn_get_log_fd(); return reborn_get_log_fd();
} }
__attribute__((destructor)) void reborn_close_log() { void reborn_set_log(const int fd) {
if (log_fd >= 0) {
close(log_fd);
log_fd = -1;
}
}
void reborn_set_log(const char *file) {
// Close Current Log
reborn_close_log();
// Set Variable // Set Variable
set_and_print_env(MCPI_LOG_ENV, file); log_fd = -1;
char buf[128];
sprintf(buf, "%i", fd);
set_and_print_env(_MCPI_LOG_FD_ENV, buf);
} }
// Debug Logging // Debug Logging

View File

@ -1,8 +1,10 @@
#include <fcntl.h> #include <fcntl.h>
#include <sys/file.h> #include <sys/file.h>
#include <sys/stat.h>
#include <libreborn/util.h> #include <libreborn/util.h>
#include <libreborn/config.h> #include <libreborn/config.h>
#include <libreborn/env.h>
// Safe Version Of pipe() // Safe Version Of pipe()
void safe_pipe2(int pipefd[2], int flags) { void safe_pipe2(int pipefd[2], int flags) {
@ -54,9 +56,9 @@ int reborn_is_headless() {
static int is_set = 0; static int is_set = 0;
if (!is_set) { if (!is_set) {
ret = reborn_is_server(); ret = reborn_is_server();
if (getenv(MCPI_FORCE_HEADLESS_ENV)) { if (getenv(_MCPI_FORCE_HEADLESS_ENV)) {
ret = 1; ret = 1;
} else if (getenv(MCPI_FORCE_NON_HEADLESS_ENV)) { } else if (getenv(_MCPI_FORCE_NON_HEADLESS_ENV)) {
ret = 0; ret = 0;
} }
is_set = 1; is_set = 1;
@ -67,7 +69,7 @@ int reborn_is_server() {
static int ret; static int ret;
static int is_set = 0; static int is_set = 0;
if (!is_set) { if (!is_set) {
ret = getenv(MCPI_SERVER_MODE_ENV) != NULL; ret = getenv(_MCPI_SERVER_MODE_ENV) != NULL;
is_set = 1; is_set = 1;
} }
return ret; return ret;
@ -89,4 +91,21 @@ const char *get_home_subdirectory_for_game_data() {
// Store Game Data In $HOME Root (In Server Mode, $HOME Is Changed To The Launch Directory) // Store Game Data In $HOME Root (In Server Mode, $HOME Is Changed To The Launch Directory)
return ""; return "";
} }
}
// Make Sure Directory Exists
void ensure_directory(const char *path) {
int ret = mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (ret != 0 && errno != EEXIST) {
ERR("Unable To Create Directory: %s", strerror(errno));
}
int is_dir = 0;
struct stat obj = {};
ret = stat(path, &obj);
if (ret == 0) {
is_dir = S_ISDIR(obj.st_mode);
}
if (!is_dir) {
ERR("Not A Directory: %s", path);
}
} }

View File

@ -1,6 +1,5 @@
#include <SDL/SDL.h> #include <SDL/SDL.h>
#include <SDL/SDL_syswm.h> #include <SDL/SDL_syswm.h>
#include <X11/Xlib.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <libreborn/libreborn.h> #include <libreborn/libreborn.h>
@ -32,11 +31,6 @@ void SDL_Quit() {
// Cleanup Media Layer // Cleanup Media Layer
media_cleanup(); media_cleanup();
// Wait For Children To Stop
signal(SIGCHLD, SIG_IGN);
murder_children();
while (wait(NULL) > 0) {}
// Exit // Exit
INFO("Stopped"); INFO("Stopped");
} }

View File

@ -26,4 +26,5 @@ void init_chat();
void init_bucket(); void init_bucket();
void init_cake(); void init_cake();
void init_home(); void init_home();
void init_override();
} }

View File

@ -158,7 +158,7 @@ static void Minecraft_update_injection(Minecraft *minecraft) {
// Init Benchmark // Init Benchmark
void init_benchmark() { void init_benchmark() {
// --benchmark: Activate Benchmark // --benchmark: Activate Benchmark
bool active = getenv("_MCPI_BENCHMARK") != nullptr; bool active = getenv(_MCPI_BENCHMARK_ENV) != nullptr;
if (active) { if (active) {
misc_run_on_update(Minecraft_update_injection); misc_run_on_update(Minecraft_update_injection);
// Track Ticks // Track Ticks

View File

@ -102,7 +102,10 @@ static void exit_handler(__attribute__((unused)) int data) {
} }
void init_compat() { void init_compat() {
// Install Signal Handlers // Install Signal Handlers
signal(SIGINT, SIG_IGN); struct sigaction act_sigint = {};
act_sigint.sa_flags = SA_RESTART;
act_sigint.sa_handler = &exit_handler;
sigaction(SIGINT, &act_sigint, nullptr);
struct sigaction act_sigterm = {}; struct sigaction act_sigterm = {};
act_sigterm.sa_flags = SA_RESTART; act_sigterm.sa_flags = SA_RESTART;
act_sigterm.sa_handler = &exit_handler; act_sigterm.sa_handler = &exit_handler;

View File

@ -12,7 +12,7 @@ bool _feature_has(const char *name, int server_default) {
return server_default > 0; return server_default > 0;
} }
// Get Value // Get Value
char *env = getenv("MCPI_FEATURE_FLAGS"); char *env = getenv(MCPI_FEATURE_FLAGS_ENV);
char *features = strdup(env != nullptr ? env : ""); char *features = strdup(env != nullptr ? env : "");
char *tok = strtok(features, "|"); char *tok = strtok(features, "|");
bool ret = false; bool ret = false;

View File

@ -1,5 +1,3 @@
#include <cerrno>
#include <libreborn/libreborn.h> #include <libreborn/libreborn.h>
#include <symbols/minecraft.h> #include <symbols/minecraft.h>
@ -11,16 +9,23 @@ const char *home_get() {
static std::string dir = ""; static std::string dir = "";
// Load // Load
if (dir.empty()) { if (dir.empty()) {
dir = std::string(getenv("HOME")) + std::string(get_home_subdirectory_for_game_data()); dir = std::string(getenv(_MCPI_HOME_ENV)) + std::string(get_home_subdirectory_for_game_data());
} }
// Return // Return
return dir.c_str(); return dir.c_str();
} }
// Use MCPI_HOME
static const char *getenv_HOME(__attribute__((unused)) const char *env) {
return getenv(_MCPI_HOME_ENV);
}
// Init // Init
void init_home() { void init_home() {
// Store Data In ~/.minecraft-pi Instead Of ~/.minecraft // Store Data In ~/.minecraft-pi Instead Of ~/.minecraft
patch_address((void *) &Strings::default_path, (void *) get_home_subdirectory_for_game_data()); patch_address(&Strings::default_path, (void *) get_home_subdirectory_for_game_data());
// Use MCPI_HOME Instead Of $HOME
overwrite_call((void *) 0xe0e4, (void *) getenv_HOME);
// The override code resolves assets manually, // The override code resolves assets manually,
// making changing directory redundant. // making changing directory redundant.

View File

@ -32,6 +32,7 @@ __attribute__((constructor)) static void init() {
init_bucket(); init_bucket();
init_cake(); init_cake();
init_home(); init_home();
init_override();
if (!reborn_is_server()) { if (!reborn_is_server()) {
init_benchmark(); init_benchmark();
} }

View File

@ -381,7 +381,7 @@ HOOK(bind, int, (int sockfd, const struct sockaddr *addr, socklen_t addrlen)) {
if (addr->sa_family == AF_INET) { if (addr->sa_family == AF_INET) {
in_addr = *(const struct sockaddr_in *) new_addr; in_addr = *(const struct sockaddr_in *) new_addr;
if (in_addr.sin_port == ntohs(4711)) { if (in_addr.sin_port == ntohs(4711)) {
const char *new_port_str = getenv("MCPI_API_PORT"); const char *new_port_str = getenv(MCPI_API_PORT_ENV);
long int new_port; long int new_port;
if (new_port_str != nullptr && (new_port = strtol(new_port_str, nullptr, 0)) != 0L) { if (new_port_str != nullptr && (new_port = strtol(new_port_str, nullptr, 0)) != 0L) {
in_addr.sin_port = htons(new_port); in_addr.sin_port = htons(new_port);
@ -555,7 +555,7 @@ static void LevelRenderer_render_AABB_glColor4f_injection(__attribute__((unused)
glColor4f(0, 0, 0, 0.4); glColor4f(0, 0, 0, 0.4);
// Find Line Width // Find Line Width
char *custom_line_width = getenv("MCPI_BLOCK_OUTLINE_WIDTH"); char *custom_line_width = getenv(MCPI_BLOCK_OUTLINE_WIDTH_ENV);
float line_width; float line_width;
if (custom_line_width != nullptr) { if (custom_line_width != nullptr) {
// Custom // Custom
@ -867,7 +867,7 @@ void init_misc() {
} }
// Custom GUI Scale // Custom GUI Scale
const char *gui_scale_str = getenv("MCPI_GUI_SCALE"); const char *gui_scale_str = getenv(MCPI_GUI_SCALE_ENV);
if (gui_scale_str != nullptr) { if (gui_scale_str != nullptr) {
unsigned char nop_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop" unsigned char nop_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop"
patch((void *) 0x173e8, nop_patch); patch((void *) 0x173e8, nop_patch);

View File

@ -19,7 +19,7 @@ static bool LevelData_getSpawnMobs_injection(__attribute__((unused)) LevelData *
// Get Custom Render Distance // Get Custom Render Distance
static int get_render_distance() { static int get_render_distance() {
const char *distance_str = getenv("MCPI_RENDER_DISTANCE"); const char *distance_str = getenv(MCPI_RENDER_DISTANCE_ENV);
if (distance_str == nullptr) { if (distance_str == nullptr) {
distance_str = "Short"; distance_str = "Short";
} }
@ -38,7 +38,7 @@ static int get_render_distance() {
// Get Custom Username // Get Custom Username
static const char *get_username() { static const char *get_username() {
const char *username = getenv("MCPI_USERNAME"); const char *username = getenv(MCPI_USERNAME_ENV);
if (username == nullptr) { if (username == nullptr) {
username = "StevePi"; username = "StevePi";
} }

View File

@ -10,6 +10,7 @@
#include <mods/override/override.h> #include <mods/override/override.h>
#include <mods/home/home.h> #include <mods/home/home.h>
#include <mods/init/init.h>
// Hook access // Hook access
HOOK(access, int, (const char *pathname, int mode)) { HOOK(access, int, (const char *pathname, int mode)) {
@ -25,6 +26,12 @@ HOOK(access, int, (const char *pathname, int mode)) {
return ret; return ret;
} }
// Get Override Folder
static std::string get_override_directory() {
const std::string home_path = home_get();
return home_path + "/overrides";
}
// Get Override Path For File (If It Exists) // Get Override Path For File (If It Exists)
char *override_get_path(const char *filename) { char *override_get_path(const char *filename) {
// Custom Skin // Custom Skin
@ -33,10 +40,8 @@ char *override_get_path(const char *filename) {
filename = "data/images/mob/char.png"; filename = "data/images/mob/char.png";
} }
// Get MCPI Home Path
const std::string home_path = home_get();
// Get Asset Override Path // Get Asset Override Path
const std::string overrides = home_path + "/overrides"; const std::string overrides = get_override_directory();
// Data Prefiix // Data Prefiix
const std::string data_prefix = "data/"; const std::string data_prefix = "data/";
@ -45,8 +50,8 @@ char *override_get_path(const char *filename) {
// Folders To Check // Folders To Check
std::string asset_folders[] = { std::string asset_folders[] = {
overrides, overrides,
getenv("MCPI_REBORN_ASSETS_PATH"), getenv(_MCPI_REBORN_ASSETS_PATH_ENV),
getenv("MCPI_VANILLA_ASSETS_PATH"), getenv(_MCPI_VANILLA_ASSETS_PATH_ENV),
"" ""
}; };
@ -105,3 +110,8 @@ HOOK(fopen64, FILE *, (const char *filename, const char *mode)) {
// Return File // Return File
return file; return file;
} }
// Init
void init_override() {
ensure_directory(get_override_directory().c_str());
}

View File

@ -16,20 +16,9 @@
// Ensure Screenshots Folder Exists // Ensure Screenshots Folder Exists
static void ensure_screenshots_folder(const char *screenshots) { static void ensure_screenshots_folder(const char *screenshots) {
// Check Screenshots Folder // Check Screenshots Folder
struct stat obj = {}; ensure_directory(screenshots);
if (stat(screenshots, &obj) != 0 || !S_ISDIR(obj.st_mode)) {
// Create Screenshots Folder
const int ret = mkdir(screenshots, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (ret != 0) {
// Unable To Create Folder
ERR("Error Creating Directory: %s: %s", screenshots, strerror(errno));
}
}
} }
// 4 (Year) + 1 (Hyphen) + 2 (Month) + 1 (Hyphen) + 2 (Day) + 1 (Underscore) + 2 (Hour) + 1 (Period) + 2 (Minute) + 1 (Period) + 2 (Second) + 1 (Null Terminator)
#define TIME_SIZE 20
// Take Screenshot // Take Screenshot
static int save_png(const char *filename, unsigned char *pixels, int line_size, int width, int height) { static int save_png(const char *filename, unsigned char *pixels, int line_size, int width, int height) {
// Setup // Setup
@ -48,12 +37,11 @@ void screenshot_take(const char *home) {
const std::string screenshots = std::string(home) + "/screenshots"; const std::string screenshots = std::string(home) + "/screenshots";
// Get Timestamp // Get Timestamp
time_t rawtime; time_t raw_time;
tm *timeinfo = {}; time(&raw_time);
time(&rawtime); tm *time_info = localtime(&raw_time);
timeinfo = localtime(&rawtime); char time[512];
char time[TIME_SIZE]; strftime(time, 512, "%Y-%m-%d_%H.%M.%S", time_info);
strftime(time, TIME_SIZE, "%Y-%m-%d_%H.%M.%S", timeinfo);
// Ensure Screenshots Folder Exists // Ensure Screenshots Folder Exists
ensure_screenshots_folder(screenshots.c_str()); ensure_screenshots_folder(screenshots.c_str());

View File

@ -26,7 +26,7 @@
// --only-generate: Ony Generate World And Then Exit // --only-generate: Ony Generate World And Then Exit
static bool only_generate = false; static bool only_generate = false;
__attribute__((constructor)) static void _init_only_generate() { __attribute__((constructor)) static void _init_only_generate() {
only_generate = getenv("_MCPI_ONLY_GENERATE") != nullptr; only_generate = getenv(_MCPI_ONLY_GENERATE_ENV) != nullptr;
} }
// Server Properties // Server Properties
@ -584,7 +584,7 @@ static void server_init() {
// Init Server // Init Server
void init_server() { void init_server() {
server_init(); server_init();
set_and_print_env("MCPI_FEATURE_FLAGS", get_features()); set_and_print_env(MCPI_FEATURE_FLAGS_ENV, get_features());
set_and_print_env("MCPI_RENDER_DISTANCE", "Tiny"); set_and_print_env(MCPI_RENDER_DISTANCE_ENV, "Tiny");
set_and_print_env("MCPI_USERNAME", get_motd().c_str()); set_and_print_env(MCPI_USERNAME_ENV, get_motd().c_str());
} }

View File

@ -65,7 +65,7 @@ static void load_pending_skins(__attribute__((unused)) Minecraft *minecraft) {
// Skin Server // Skin Server
static std::string get_skin_server() { static std::string get_skin_server() {
const char *custom_server = getenv("MCPI_SKIN_SERVER"); const char *custom_server = getenv(MCPI_SKIN_SERVER_ENV);
if (custom_server != nullptr) { if (custom_server != nullptr) {
return custom_server; return custom_server;
} else { } else {