Environmental Variable & Logging Refactor
This commit is contained in:
parent
b2db6bcfd2
commit
3b5149abff
@ -4,8 +4,8 @@ project(launcher)
|
||||
add_executable(launcher
|
||||
src/bootstrap.cpp
|
||||
src/patchelf.cpp
|
||||
src/util.c
|
||||
src/crash-report.c
|
||||
src/util.cpp
|
||||
src/crash-report.cpp
|
||||
src/sdk.cpp
|
||||
src/mods.cpp
|
||||
src/options/parser.cpp
|
||||
|
@ -41,14 +41,17 @@ static void print_debug_information() {
|
||||
DEBUG("Reborn Version: v%s", MCPI_VERSION);
|
||||
|
||||
// Architecture
|
||||
const char *arch = "Unknown";
|
||||
const char *arch =
|
||||
#ifdef __x86_64__
|
||||
arch = "AMD64";
|
||||
"AMD64"
|
||||
#elif defined(__aarch64__)
|
||||
arch = "ARM64";
|
||||
"ARM64"
|
||||
#elif defined(__arm__)
|
||||
arch = "ARM32";
|
||||
"ARM32"
|
||||
#else
|
||||
"Unknown"
|
||||
#endif
|
||||
;
|
||||
DEBUG("Reborn Target Architecture: %s", arch);
|
||||
}
|
||||
|
||||
@ -66,9 +69,7 @@ void bootstrap() {
|
||||
#endif
|
||||
|
||||
// Get Binary Directory
|
||||
char *binary_directory_raw = get_binary_directory();
|
||||
const std::string binary_directory = binary_directory_raw;
|
||||
free(binary_directory_raw);
|
||||
const std::string binary_directory = get_binary_directory();
|
||||
DEBUG("Binary Directory: %s", binary_directory.c_str());
|
||||
|
||||
// Copy SDK
|
||||
@ -78,24 +79,21 @@ void bootstrap() {
|
||||
|
||||
// Set MCPI_REBORN_ASSETS_PATH
|
||||
{
|
||||
char *assets_path = realpath("/proc/self/exe", nullptr);
|
||||
ALLOC_CHECK(assets_path);
|
||||
chop_last_component(&assets_path);
|
||||
string_append(&assets_path, "/data");
|
||||
set_and_print_env("MCPI_REBORN_ASSETS_PATH", assets_path);
|
||||
free(assets_path);
|
||||
std::string assets_path = safe_realpath("/proc/self/exe");
|
||||
chop_last_component(assets_path);
|
||||
assets_path += "/data";
|
||||
set_and_print_env(_MCPI_REBORN_ASSETS_PATH_ENV, assets_path.c_str());
|
||||
}
|
||||
|
||||
// Resolve Binary Path & Set MCPI_DIRECTORY
|
||||
char *resolved_path = nullptr;
|
||||
std::string game_binary;
|
||||
{
|
||||
// Log
|
||||
DEBUG("Resolving File Paths...");
|
||||
|
||||
// Resolve Full Binary Path
|
||||
const std::string full_path = binary_directory + ("/" MCPI_BINARY);
|
||||
resolved_path = realpath(full_path.c_str(), nullptr);
|
||||
ALLOC_CHECK(resolved_path);
|
||||
game_binary = safe_realpath(full_path);
|
||||
}
|
||||
|
||||
// Fix MCPI Dependencies
|
||||
@ -113,7 +111,7 @@ void bootstrap() {
|
||||
#endif
|
||||
|
||||
// Patch
|
||||
patch_mcpi_elf_dependencies(resolved_path, new_mcpi_exe_path);
|
||||
patch_mcpi_elf_dependencies(game_binary.c_str(), new_mcpi_exe_path);
|
||||
|
||||
// Verify
|
||||
if (!starts_with(new_mcpi_exe_path, MCPI_PATCHED_DIR)) {
|
||||
@ -123,17 +121,12 @@ void bootstrap() {
|
||||
|
||||
// Set MCPI_VANILLA_ASSETS_PATH
|
||||
{
|
||||
char *assets_path = strdup(resolved_path);
|
||||
ALLOC_CHECK(assets_path);
|
||||
chop_last_component(&assets_path);
|
||||
string_append(&assets_path, "/data");
|
||||
set_and_print_env("MCPI_VANILLA_ASSETS_PATH", assets_path);
|
||||
free(assets_path);
|
||||
std::string assets_path = game_binary;
|
||||
chop_last_component(assets_path);
|
||||
assets_path += "/data";
|
||||
set_and_print_env(_MCPI_VANILLA_ASSETS_PATH_ENV, assets_path.c_str());
|
||||
}
|
||||
|
||||
// Free Resolved Path
|
||||
free(resolved_path);
|
||||
|
||||
// Configure Library Search Path
|
||||
std::string mcpi_ld_path = "";
|
||||
{
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
// Get Cache Path
|
||||
static std::string get_cache_path() {
|
||||
const char *home = getenv("HOME");
|
||||
const char *home = getenv(_MCPI_HOME_ENV);
|
||||
if (home == nullptr) {
|
||||
IMPOSSIBLE();
|
||||
}
|
||||
@ -120,18 +120,18 @@ void save_cache() {
|
||||
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");
|
||||
write_env_to_stream(stream, MCPI_USERNAME_ENV);
|
||||
write_env_to_stream(stream, MCPI_RENDER_DISTANCE_ENV);
|
||||
|
||||
// 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);
|
||||
std::string stripped_flag = strip_feature_flag_default(flag, nullptr);
|
||||
flags[stripped_flag] = false;
|
||||
});
|
||||
{
|
||||
const char *enabled_flags = getenv("MCPI_FEATURE_FLAGS");
|
||||
if (enabled_flags == NULL) {
|
||||
const char *enabled_flags = getenv(MCPI_FEATURE_FLAGS_ENV);
|
||||
if (enabled_flags == nullptr) {
|
||||
IMPOSSIBLE();
|
||||
}
|
||||
std::istringstream enabled_flags_stream(enabled_flags);
|
||||
|
@ -41,12 +41,8 @@ std::string strip_feature_flag_default(const std::string &flag, bool *default_re
|
||||
extern unsigned char available_feature_flags[];
|
||||
extern size_t available_feature_flags_len;
|
||||
void load_available_feature_flags(const std::function<void(std::string)> &callback) {
|
||||
// Get Path
|
||||
char *binary_directory = get_binary_directory();
|
||||
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);
|
||||
// Load Data
|
||||
const std::string data(available_feature_flags, available_feature_flags + available_feature_flags_len);
|
||||
std::stringstream stream(data);
|
||||
// Store Lines
|
||||
std::vector<std::string> lines;
|
||||
@ -68,8 +64,8 @@ void load_available_feature_flags(const std::function<void(std::string)> &callba
|
||||
// Sort
|
||||
std::sort(lines.begin(), lines.end(), [](const std::string &a, const std::string &b) {
|
||||
// Strip Defaults
|
||||
std::string stripped_a = strip_feature_flag_default(a, nullptr);
|
||||
std::string stripped_b = strip_feature_flag_default(b, nullptr);
|
||||
const std::string stripped_a = strip_feature_flag_default(a, nullptr);
|
||||
const std::string stripped_b = strip_feature_flag_default(b, nullptr);
|
||||
// Sort
|
||||
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);
|
||||
if (output != nullptr) {
|
||||
// Trim
|
||||
int length = strlen(output);
|
||||
const size_t length = strlen(output);
|
||||
if (output[length - 1] == '\n') {
|
||||
output[length - 1] = '\0';
|
||||
}
|
||||
@ -164,7 +160,7 @@ void configure_client(const options_t &options) {
|
||||
// --default
|
||||
if (options.use_default) {
|
||||
// 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 = "";
|
||||
load_available_feature_flags([&feature_flags, &cache](const std::string &flag) {
|
||||
bool value;
|
||||
@ -185,10 +181,10 @@ void configure_client(const options_t &options) {
|
||||
}
|
||||
return feature_flags;
|
||||
});
|
||||
set_env_if_unset("MCPI_RENDER_DISTANCE", [&cache]() {
|
||||
set_env_if_unset(MCPI_RENDER_DISTANCE_ENV, [&cache]() {
|
||||
return cache.render_distance;
|
||||
});
|
||||
set_env_if_unset("MCPI_USERNAME", [&cache]() {
|
||||
set_env_if_unset(MCPI_USERNAME_ENV, [&cache]() {
|
||||
return cache.username;
|
||||
});
|
||||
}
|
||||
@ -226,7 +222,7 @@ void configure_client(const options_t &options) {
|
||||
command.push_back(stripped_flag);
|
||||
});
|
||||
// Run
|
||||
run_zenity_and_set_env("MCPI_FEATURE_FLAGS", command);
|
||||
run_zenity_and_set_env(MCPI_FEATURE_FLAGS_ENV, command);
|
||||
}
|
||||
// Setup MCPI_RENDER_DISTANCE
|
||||
{
|
||||
@ -249,7 +245,7 @@ void configure_client(const options_t &options) {
|
||||
command.push_back(render_distance);
|
||||
}
|
||||
// Run
|
||||
run_zenity_and_set_env("MCPI_RENDER_DISTANCE", command);
|
||||
run_zenity_and_set_env(MCPI_RENDER_DISTANCE_ENV, command);
|
||||
}
|
||||
// Setup MCPI_USERNAME
|
||||
{
|
||||
@ -260,7 +256,7 @@ void configure_client(const options_t &options) {
|
||||
command.push_back("--entry-text");
|
||||
command.push_back(cache.username);
|
||||
// Run
|
||||
run_zenity_and_set_env("MCPI_USERNAME", command);
|
||||
run_zenity_and_set_env(MCPI_USERNAME_ENV, command);
|
||||
}
|
||||
|
||||
// Save Cache
|
||||
|
@ -1,14 +1,16 @@
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <signal.h>
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <csignal>
|
||||
#include <poll.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
@ -47,39 +49,67 @@ static void show_report(const char *log_filename) {
|
||||
}
|
||||
|
||||
// Exit Handler
|
||||
static pid_t child_pid = -1;
|
||||
static void exit_handler(__attribute__((unused)) int signal) {
|
||||
// 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
|
||||
#define PIPE_READ 0
|
||||
#define PIPE_WRITE 1
|
||||
#define MCPI_LOGS_DIR "/tmp/.minecraft-pi-logs"
|
||||
static char log_filename[] = MCPI_LOGS_DIR "/XXXXXX";
|
||||
void setup_log_file() {
|
||||
// Ensure Temporary Directory
|
||||
{
|
||||
// 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));
|
||||
#define BUFFER_SIZE 1024
|
||||
static void safe_write(int fd, const void *buf, size_t size) {
|
||||
const ssize_t bytes_written = write(fd, buf, size);
|
||||
if (bytes_written < 0) {
|
||||
ERR("Unable To Write Data: %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() {
|
||||
// Setup Logging
|
||||
setup_log_file();
|
||||
|
||||
// Store Output
|
||||
int output_pipe[2];
|
||||
safe_pipe2(output_pipe, 0);
|
||||
@ -114,19 +144,16 @@ void setup_crash_report() {
|
||||
|
||||
// Continue Execution
|
||||
} else {
|
||||
// Parent Process
|
||||
track_child(ret);
|
||||
|
||||
// Install Signal Handlers
|
||||
struct sigaction act_sigint = {0};
|
||||
child_pid = ret;
|
||||
struct sigaction act_sigint = {};
|
||||
act_sigint.sa_flags = SA_RESTART;
|
||||
act_sigint.sa_handler = &exit_handler;
|
||||
sigaction(SIGINT, &act_sigint, NULL);
|
||||
struct sigaction act_sigterm = {0};
|
||||
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, NULL);
|
||||
atexit(murder_children);
|
||||
sigaction(SIGTERM, &act_sigterm, nullptr);
|
||||
|
||||
// Close Unneeded File Descriptors
|
||||
close(output_pipe[PIPE_WRITE]);
|
||||
@ -136,24 +163,20 @@ void setup_crash_report() {
|
||||
// Set Debug Tag
|
||||
reborn_debug_tag = "(Crash Reporter) ";
|
||||
|
||||
// Setup Logging
|
||||
#define BUFFER_SIZE 1024
|
||||
char buf[BUFFER_SIZE];
|
||||
|
||||
// Setup Polling
|
||||
int number_fds = 3;
|
||||
struct pollfd poll_fds[number_fds];
|
||||
const int number_fds = 3;
|
||||
pollfd poll_fds[number_fds];
|
||||
poll_fds[0].fd = output_pipe[PIPE_READ];
|
||||
poll_fds[1].fd = error_pipe[PIPE_READ];
|
||||
poll_fds[2].fd = STDIN_FILENO;
|
||||
for (int i = 0; i < number_fds; i++) {
|
||||
poll_fds[i].events = POLLIN;
|
||||
for (pollfd &poll_fd : poll_fds) {
|
||||
poll_fd.events = POLLIN;
|
||||
}
|
||||
|
||||
// Poll Data
|
||||
int status;
|
||||
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 (errno == EINTR) {
|
||||
continue;
|
||||
@ -163,86 +186,71 @@ void setup_crash_report() {
|
||||
}
|
||||
|
||||
// Handle Data
|
||||
for (int i = 0; i < number_fds; i++) {
|
||||
if (poll_fds[i].revents != 0) {
|
||||
if (poll_fds[i].revents & POLLIN) {
|
||||
if (poll_fds[i].fd == STDIN_FILENO) {
|
||||
for (pollfd &poll_fd : poll_fds) {
|
||||
if (poll_fd.revents != 0) {
|
||||
if (poll_fd.revents & POLLIN) {
|
||||
char buf[BUFFER_SIZE];
|
||||
if (poll_fd.fd == STDIN_FILENO) {
|
||||
// Data Available From stdin
|
||||
int bytes_available;
|
||||
if (ioctl(fileno(stdin), FIONREAD, &bytes_available) == -1) {
|
||||
bytes_available = 0;
|
||||
}
|
||||
// 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) {
|
||||
ERR("Unable To Read Input: %s", strerror(errno));
|
||||
}
|
||||
// Write To Child
|
||||
if (write(input_pipe[PIPE_WRITE], buf, bytes_read) == -1) {
|
||||
ERR("Unable To Write Input To Child: %s", strerror(errno));
|
||||
}
|
||||
safe_write(input_pipe[PIPE_WRITE], buf, bytes_read);
|
||||
} else {
|
||||
// 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) {
|
||||
ERR("Unable To Read Log Data: %s", strerror(errno));
|
||||
}
|
||||
|
||||
// Print To Terminal
|
||||
buf[bytes_read] = '\0';
|
||||
fprintf(poll_fds[i].fd == output_pipe[PIPE_READ] ? stdout : stderr, "%s", buf);
|
||||
|
||||
safe_write(poll_fd.fd == output_pipe[PIPE_READ] ? STDOUT_FILENO : STDERR_FILENO, buf, bytes_read);
|
||||
// Write To log
|
||||
if (write(reborn_get_log_fd(), buf, bytes_read) == -1) {
|
||||
ERR("Unable To Write Log Data: %s", strerror(errno));
|
||||
}
|
||||
safe_write(reborn_get_log_fd(), buf, bytes_read);
|
||||
}
|
||||
} else {
|
||||
// File Descriptor No Longer Accessible
|
||||
poll_fds[i].events = 0;
|
||||
poll_fd.events = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Untrack Process
|
||||
untrack_child(ret);
|
||||
|
||||
// Close Pipes
|
||||
close(output_pipe[PIPE_READ]);
|
||||
close(error_pipe[PIPE_READ]);
|
||||
close(input_pipe[PIPE_WRITE]);
|
||||
|
||||
// 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
|
||||
if (is_crash) {
|
||||
// Create Exit Code Log Line
|
||||
char *exit_status = NULL;
|
||||
char *exit_status = nullptr;
|
||||
get_exit_status_string(status, &exit_status);
|
||||
char *exit_code_line = NULL;
|
||||
safe_asprintf(&exit_code_line, "[CRASH]: Terminated%s\n", exit_status);
|
||||
const std::string exit_code_line = "[CRASH]: Terminated" + std::string(exit_status) + '\n';
|
||||
free(exit_status);
|
||||
|
||||
// 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
|
||||
if (write(reborn_get_log_fd(), exit_code_line, strlen(exit_code_line)) == -1) {
|
||||
ERR("Unable To Write Exit Code To Log: %s", strerror(errno));
|
||||
}
|
||||
|
||||
// Free Exit Code Log Line
|
||||
free(exit_code_line);
|
||||
safe_write(reborn_get_debug_fd(), exit_code_line.c_str(), strlen(exit_code_line.c_str()));
|
||||
}
|
||||
|
||||
// Close Log File
|
||||
reborn_close_log();
|
||||
unsetenv(MCPI_LOG_ENV);
|
||||
close(log_fd);
|
||||
unsetenv(_MCPI_LOG_FD_ENV);
|
||||
|
||||
// Show Crash Log
|
||||
if (is_crash && !reborn_is_headless()) {
|
||||
show_report(log_filename);
|
||||
show_report(log_filename.c_str());
|
||||
}
|
||||
|
||||
// Exit
|
@ -4,7 +4,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void setup_log_file();
|
||||
void setup_crash_report();
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -10,18 +10,20 @@
|
||||
|
||||
// Bind Options To Environmental Variable
|
||||
static void bind_to_env(const char *env, const bool value) {
|
||||
const bool force = env[0] == '_';
|
||||
if (force || value) {
|
||||
set_and_print_env(env, value ? "1" : nullptr);
|
||||
if (value) {
|
||||
set_and_print_env(env, "1");
|
||||
}
|
||||
}
|
||||
static void setup_environment(const options_t &options) {
|
||||
// Clear Internal Variables
|
||||
clear_internal_env_vars();
|
||||
|
||||
// Passthrough Options To Game
|
||||
bind_to_env(MCPI_SERVER_MODE_ENV, options.server_mode);
|
||||
bind_to_env("_MCPI_BENCHMARK", options.benchmark);
|
||||
bind_to_env("_MCPI_ONLY_GENERATE", options.only_generate);
|
||||
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_SERVER_MODE_ENV, options.server_mode);
|
||||
bind_to_env(_MCPI_BENCHMARK_ENV, options.benchmark);
|
||||
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_NON_HEADLESS_ENV, options.force_non_headless);
|
||||
|
||||
// GTK Dark Mode
|
||||
set_and_print_env("GTK_THEME", "Adwaita:dark");
|
||||
@ -29,12 +31,11 @@ static void setup_environment(const options_t &options) {
|
||||
// Configure PATH
|
||||
{
|
||||
// Get Binary Directory
|
||||
char *binary_directory = get_binary_directory();
|
||||
std::string new_path = std::string(binary_directory) + "/bin";
|
||||
free(binary_directory);
|
||||
const std::string binary_directory = get_binary_directory();
|
||||
std::string new_path = binary_directory + "/bin";
|
||||
// Add Existing PATH
|
||||
{
|
||||
char *value = getenv("PATH");
|
||||
const char *value = getenv("PATH");
|
||||
if (value != nullptr && strlen(value) > 0) {
|
||||
new_path += std::string(":") + value;
|
||||
}
|
||||
@ -42,27 +43,37 @@ static void setup_environment(const options_t &options) {
|
||||
// Set And Free
|
||||
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
|
||||
static void handle_non_launch_commands(const options_t &options) {
|
||||
if (options.copy_sdk) {
|
||||
char *binary_directory = get_binary_directory();
|
||||
const std::string binary_directory = get_binary_directory();
|
||||
copy_sdk(binary_directory, false);
|
||||
free(binary_directory);
|
||||
fflush(stdout);
|
||||
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
|
||||
static void start_game(const options_t &options) {
|
||||
// Disable stdout Buffering
|
||||
@ -70,46 +81,9 @@ static void start_game(const options_t &options) {
|
||||
|
||||
// Setup Crash Reporting
|
||||
if (!options.disable_crash_report) {
|
||||
setup_log_file();
|
||||
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
|
||||
if (!reborn_is_server()) {
|
||||
configure_client(options);
|
||||
@ -128,7 +102,7 @@ int main(int argc, char *argv[]) {
|
||||
reborn_debug_tag = "(Launcher) ";
|
||||
|
||||
// Debug Logging
|
||||
unsetenv(MCPI_LOG_ENV);
|
||||
unsetenv(_MCPI_LOG_FD_ENV);
|
||||
bind_to_env(MCPI_DEBUG_ENV, options.debug);
|
||||
|
||||
// Setup Environment
|
||||
|
@ -10,6 +10,7 @@
|
||||
// Get All Mods In Folder
|
||||
static void load(std::string &ld_preload, const std::string &folder) {
|
||||
// Open Folder
|
||||
ensure_directory(folder.c_str());
|
||||
DIR *dp = opendir(folder.c_str());
|
||||
if (dp != nullptr) {
|
||||
// Loop Through Folder
|
||||
@ -43,8 +44,6 @@ static void load(std::string &ld_preload, const std::string &folder) {
|
||||
}
|
||||
// Close Folder
|
||||
closedir(dp);
|
||||
} else if (errno == ENOENT) {
|
||||
// Folder Doesn't Exist
|
||||
} else {
|
||||
// Unable To Open Folder
|
||||
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
|
||||
{
|
||||
// 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(preload, mods_folder);
|
||||
}
|
||||
|
@ -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(disable_crash_report, "disable-crash-report", -1, "Disable Crash Report Dialog")
|
||||
OPTION(use_default, "default", -3, "Skip Client-Mode Configuration Dialogs")
|
||||
|
@ -8,12 +8,19 @@ const char *argp_program_bug_address = "<" MCPI_DISCORD_INVITE ">";
|
||||
static char doc[] = "Minecraft: Pi Edition Modding Project";
|
||||
|
||||
// Options
|
||||
#define OPTION(ignored, name, key, doc) {name, key, nullptr, 0, doc, 0},
|
||||
static int env_key = -100;
|
||||
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"
|
||||
#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}
|
||||
};
|
||||
#undef OPTION
|
||||
|
||||
// Parse Options
|
||||
#define OPTION(name, ignored, key, ...) \
|
||||
|
@ -13,17 +13,7 @@
|
||||
// Duplicate MCPI Executable Into /tmp
|
||||
static void duplicate_mcpi_executable(char *new_path) {
|
||||
// Ensure Temporary Directory
|
||||
{
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
ensure_directory(MCPI_PATCHED_DIR);
|
||||
|
||||
// Generate New File
|
||||
int new_file_fd = mkstemp(new_path);
|
||||
|
@ -19,7 +19,7 @@ void copy_sdk(const std::string &binary_directory, const bool log_with_debug) {
|
||||
// Ensure SDK Directory
|
||||
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};
|
||||
run_simple_command(command, "Unable To Create SDK Directory");
|
||||
}
|
||||
|
@ -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
41
launcher/src/util.cpp
Normal 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;
|
||||
}
|
@ -1,14 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <string>
|
||||
|
||||
void run_simple_command(const char *const command[], const char *error);
|
||||
|
||||
void chop_last_component(char **str);
|
||||
char *get_binary_directory();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
void chop_last_component(std::string &str);
|
||||
std::string safe_realpath(const std::string &path);
|
||||
std::string get_binary_directory();
|
||||
|
@ -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)
|
||||
|
||||
# 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(
|
||||
reborn-util
|
||||
PUBLIC
|
||||
@ -25,7 +32,12 @@ endif()
|
||||
|
||||
# Patch
|
||||
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_compile_definitions(reborn-patch PUBLIC -DREBORN_HAS_PATCH_CODE)
|
||||
# Install
|
||||
|
16
libreborn/include/libreborn/env.h
Normal file
16
libreborn/include/libreborn/env.h
Normal 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
|
24
libreborn/include/libreborn/env_list.h
Normal file
24
libreborn/include/libreborn/env_list.h
Normal 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, "")
|
@ -32,11 +32,6 @@ char *run_command(const char *const command[], int *exit_status, size_t *output_
|
||||
// Get Exit Status String
|
||||
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
|
||||
}
|
||||
#endif
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <libreborn/config.h>
|
||||
#include "env.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
#include "string.h"
|
||||
|
@ -8,12 +8,9 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
// Log File
|
||||
#define MCPI_LOG_ENV "_MCPI_LOG"
|
||||
int reborn_get_log_fd();
|
||||
void reborn_close_log();
|
||||
void reborn_set_log(const char *file);
|
||||
void reborn_set_log(int fd);
|
||||
// Debug Logging
|
||||
#define MCPI_DEBUG_ENV "MCPI_DEBUG"
|
||||
extern const char *reborn_debug_tag;
|
||||
int reborn_get_debug_fd();
|
||||
|
||||
|
@ -51,9 +51,6 @@ int lock_file(const char *file);
|
||||
void unlock_file(const char *file, int fd);
|
||||
|
||||
// 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();
|
||||
int reborn_is_headless();
|
||||
int reborn_is_server();
|
||||
@ -64,6 +61,9 @@ void reborn_check_display();
|
||||
// Get Home Subdirectory
|
||||
const char *get_home_subdirectory_for_game_data();
|
||||
|
||||
// Make Sure Directory Exists
|
||||
void ensure_directory(const char *path);
|
||||
|
||||
// Customize VTable
|
||||
#define CUSTOM_VTABLE(name, parent) \
|
||||
void _setup_##name##_vtable(parent##_vtable *vtable); \
|
||||
|
20
libreborn/src/util/env.c
Normal file
20
libreborn/src/util/env.c
Normal 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
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
#include <pthread.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#include <libreborn/exec.h>
|
||||
|
||||
@ -57,12 +58,12 @@ char *run_command(const char *const command[], int *exit_status, size_t *output_
|
||||
// Setup stderr
|
||||
dup2(reborn_get_debug_fd(), STDERR_FILENO);
|
||||
|
||||
// Kill On Parent Death
|
||||
prctl(PR_SET_PDEATHSIG, SIGKILL);
|
||||
|
||||
// Run
|
||||
safe_execvpe(command, (const char *const *) environ);
|
||||
} else {
|
||||
// Parent Process
|
||||
track_child(ret);
|
||||
|
||||
// Read stdout
|
||||
close(output_pipe[1]);
|
||||
#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
|
||||
size_t needed_size = position + bytes_read;
|
||||
if (needed_size > size) {
|
||||
// More Memeory Needed
|
||||
// More Memory Needed
|
||||
size_t new_size = size;
|
||||
while (new_size < needed_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
|
||||
size_t needed_size = position + 1;
|
||||
if (needed_size > size) {
|
||||
// More Memeory Needed
|
||||
// More Memory Needed
|
||||
size_t new_size = size + 1;
|
||||
char *new_output = realloc(output, new_size);
|
||||
if (new_output == NULL) {
|
||||
@ -117,7 +118,6 @@ char *run_command(const char *const command[], int *exit_status, size_t *output_
|
||||
// Get Return Code
|
||||
int status;
|
||||
waitpid(ret, &status, 0);
|
||||
untrack_child(ret);
|
||||
if (exit_status != NULL) {
|
||||
*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);
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include <libreborn/log.h>
|
||||
#include <libreborn/exec.h>
|
||||
#include <libreborn/env.h>
|
||||
|
||||
// Debug Tag
|
||||
const char *reborn_debug_tag = "";
|
||||
@ -16,11 +17,8 @@ int reborn_get_log_fd() {
|
||||
return log_fd;
|
||||
}
|
||||
// Open Log File
|
||||
const char *file = getenv(MCPI_LOG_ENV);
|
||||
if (file == NULL) {
|
||||
file = "/dev/null";
|
||||
}
|
||||
log_fd = open(file, O_WRONLY | O_APPEND | O_CLOEXEC);
|
||||
const char *fd_str = getenv(_MCPI_LOG_FD_ENV);
|
||||
log_fd = fd_str ? atoi(fd_str) : open("/dev/null", O_WRONLY | O_APPEND);
|
||||
// Check FD
|
||||
if (log_fd < 0) {
|
||||
ERR("Unable To Open Log: %s", strerror(errno));
|
||||
@ -28,17 +26,12 @@ int reborn_get_log_fd() {
|
||||
// Return
|
||||
return reborn_get_log_fd();
|
||||
}
|
||||
__attribute__((destructor)) void reborn_close_log() {
|
||||
if (log_fd >= 0) {
|
||||
close(log_fd);
|
||||
log_fd = -1;
|
||||
}
|
||||
}
|
||||
void reborn_set_log(const char *file) {
|
||||
// Close Current Log
|
||||
reborn_close_log();
|
||||
void reborn_set_log(const int fd) {
|
||||
// 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
|
||||
|
@ -1,8 +1,10 @@
|
||||
#include <fcntl.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <libreborn/util.h>
|
||||
#include <libreborn/config.h>
|
||||
#include <libreborn/env.h>
|
||||
|
||||
// Safe Version Of pipe()
|
||||
void safe_pipe2(int pipefd[2], int flags) {
|
||||
@ -54,9 +56,9 @@ int reborn_is_headless() {
|
||||
static int is_set = 0;
|
||||
if (!is_set) {
|
||||
ret = reborn_is_server();
|
||||
if (getenv(MCPI_FORCE_HEADLESS_ENV)) {
|
||||
if (getenv(_MCPI_FORCE_HEADLESS_ENV)) {
|
||||
ret = 1;
|
||||
} else if (getenv(MCPI_FORCE_NON_HEADLESS_ENV)) {
|
||||
} else if (getenv(_MCPI_FORCE_NON_HEADLESS_ENV)) {
|
||||
ret = 0;
|
||||
}
|
||||
is_set = 1;
|
||||
@ -67,7 +69,7 @@ int reborn_is_server() {
|
||||
static int ret;
|
||||
static int is_set = 0;
|
||||
if (!is_set) {
|
||||
ret = getenv(MCPI_SERVER_MODE_ENV) != NULL;
|
||||
ret = getenv(_MCPI_SERVER_MODE_ENV) != NULL;
|
||||
is_set = 1;
|
||||
}
|
||||
return ret;
|
||||
@ -90,3 +92,20 @@ const char *get_home_subdirectory_for_game_data() {
|
||||
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);
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
#include <SDL/SDL.h>
|
||||
#include <SDL/SDL_syswm.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
@ -32,11 +31,6 @@ void SDL_Quit() {
|
||||
// Cleanup Media Layer
|
||||
media_cleanup();
|
||||
|
||||
// Wait For Children To Stop
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
murder_children();
|
||||
while (wait(NULL) > 0) {}
|
||||
|
||||
// Exit
|
||||
INFO("Stopped");
|
||||
}
|
||||
|
@ -26,4 +26,5 @@ void init_chat();
|
||||
void init_bucket();
|
||||
void init_cake();
|
||||
void init_home();
|
||||
void init_override();
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ static void Minecraft_update_injection(Minecraft *minecraft) {
|
||||
// Init Benchmark
|
||||
void init_benchmark() {
|
||||
// --benchmark: Activate Benchmark
|
||||
bool active = getenv("_MCPI_BENCHMARK") != nullptr;
|
||||
bool active = getenv(_MCPI_BENCHMARK_ENV) != nullptr;
|
||||
if (active) {
|
||||
misc_run_on_update(Minecraft_update_injection);
|
||||
// Track Ticks
|
||||
|
@ -102,7 +102,10 @@ static void exit_handler(__attribute__((unused)) int data) {
|
||||
}
|
||||
void init_compat() {
|
||||
// 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 = {};
|
||||
act_sigterm.sa_flags = SA_RESTART;
|
||||
act_sigterm.sa_handler = &exit_handler;
|
||||
|
@ -12,7 +12,7 @@ bool _feature_has(const char *name, int server_default) {
|
||||
return server_default > 0;
|
||||
}
|
||||
// Get Value
|
||||
char *env = getenv("MCPI_FEATURE_FLAGS");
|
||||
char *env = getenv(MCPI_FEATURE_FLAGS_ENV);
|
||||
char *features = strdup(env != nullptr ? env : "");
|
||||
char *tok = strtok(features, "|");
|
||||
bool ret = false;
|
||||
|
@ -1,5 +1,3 @@
|
||||
#include <cerrno>
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
#include <symbols/minecraft.h>
|
||||
|
||||
@ -11,16 +9,23 @@ const char *home_get() {
|
||||
static std::string dir = "";
|
||||
// Load
|
||||
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 dir.c_str();
|
||||
}
|
||||
|
||||
// Use MCPI_HOME
|
||||
static const char *getenv_HOME(__attribute__((unused)) const char *env) {
|
||||
return getenv(_MCPI_HOME_ENV);
|
||||
}
|
||||
|
||||
// Init
|
||||
void init_home() {
|
||||
// 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,
|
||||
// making changing directory redundant.
|
||||
|
@ -32,6 +32,7 @@ __attribute__((constructor)) static void init() {
|
||||
init_bucket();
|
||||
init_cake();
|
||||
init_home();
|
||||
init_override();
|
||||
if (!reborn_is_server()) {
|
||||
init_benchmark();
|
||||
}
|
||||
|
@ -381,7 +381,7 @@ HOOK(bind, int, (int sockfd, const struct sockaddr *addr, socklen_t addrlen)) {
|
||||
if (addr->sa_family == AF_INET) {
|
||||
in_addr = *(const struct sockaddr_in *) new_addr;
|
||||
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;
|
||||
if (new_port_str != nullptr && (new_port = strtol(new_port_str, nullptr, 0)) != 0L) {
|
||||
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);
|
||||
|
||||
// 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;
|
||||
if (custom_line_width != nullptr) {
|
||||
// Custom
|
||||
@ -867,7 +867,7 @@ void init_misc() {
|
||||
}
|
||||
|
||||
// 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) {
|
||||
unsigned char nop_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop"
|
||||
patch((void *) 0x173e8, nop_patch);
|
||||
|
@ -19,7 +19,7 @@ static bool LevelData_getSpawnMobs_injection(__attribute__((unused)) LevelData *
|
||||
|
||||
// Get Custom 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) {
|
||||
distance_str = "Short";
|
||||
}
|
||||
@ -38,7 +38,7 @@ static int get_render_distance() {
|
||||
|
||||
// Get Custom Username
|
||||
static const char *get_username() {
|
||||
const char *username = getenv("MCPI_USERNAME");
|
||||
const char *username = getenv(MCPI_USERNAME_ENV);
|
||||
if (username == nullptr) {
|
||||
username = "StevePi";
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include <mods/override/override.h>
|
||||
#include <mods/home/home.h>
|
||||
#include <mods/init/init.h>
|
||||
|
||||
// Hook access
|
||||
HOOK(access, int, (const char *pathname, int mode)) {
|
||||
@ -25,6 +26,12 @@ HOOK(access, int, (const char *pathname, int mode)) {
|
||||
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)
|
||||
char *override_get_path(const char *filename) {
|
||||
// Custom Skin
|
||||
@ -33,10 +40,8 @@ char *override_get_path(const char *filename) {
|
||||
filename = "data/images/mob/char.png";
|
||||
}
|
||||
|
||||
// Get MCPI Home Path
|
||||
const std::string home_path = home_get();
|
||||
// Get Asset Override Path
|
||||
const std::string overrides = home_path + "/overrides";
|
||||
const std::string overrides = get_override_directory();
|
||||
|
||||
// Data Prefiix
|
||||
const std::string data_prefix = "data/";
|
||||
@ -45,8 +50,8 @@ char *override_get_path(const char *filename) {
|
||||
// Folders To Check
|
||||
std::string asset_folders[] = {
|
||||
overrides,
|
||||
getenv("MCPI_REBORN_ASSETS_PATH"),
|
||||
getenv("MCPI_VANILLA_ASSETS_PATH"),
|
||||
getenv(_MCPI_REBORN_ASSETS_PATH_ENV),
|
||||
getenv(_MCPI_VANILLA_ASSETS_PATH_ENV),
|
||||
""
|
||||
};
|
||||
|
||||
@ -105,3 +110,8 @@ HOOK(fopen64, FILE *, (const char *filename, const char *mode)) {
|
||||
// Return File
|
||||
return file;
|
||||
}
|
||||
|
||||
// Init
|
||||
void init_override() {
|
||||
ensure_directory(get_override_directory().c_str());
|
||||
}
|
@ -16,20 +16,9 @@
|
||||
// Ensure Screenshots Folder Exists
|
||||
static void ensure_screenshots_folder(const char *screenshots) {
|
||||
// Check Screenshots Folder
|
||||
struct stat obj = {};
|
||||
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));
|
||||
}
|
||||
}
|
||||
ensure_directory(screenshots);
|
||||
}
|
||||
|
||||
// 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
|
||||
static int save_png(const char *filename, unsigned char *pixels, int line_size, int width, int height) {
|
||||
// Setup
|
||||
@ -48,12 +37,11 @@ void screenshot_take(const char *home) {
|
||||
const std::string screenshots = std::string(home) + "/screenshots";
|
||||
|
||||
// Get Timestamp
|
||||
time_t rawtime;
|
||||
tm *timeinfo = {};
|
||||
time(&rawtime);
|
||||
timeinfo = localtime(&rawtime);
|
||||
char time[TIME_SIZE];
|
||||
strftime(time, TIME_SIZE, "%Y-%m-%d_%H.%M.%S", timeinfo);
|
||||
time_t raw_time;
|
||||
time(&raw_time);
|
||||
tm *time_info = localtime(&raw_time);
|
||||
char time[512];
|
||||
strftime(time, 512, "%Y-%m-%d_%H.%M.%S", time_info);
|
||||
|
||||
// Ensure Screenshots Folder Exists
|
||||
ensure_screenshots_folder(screenshots.c_str());
|
||||
|
@ -26,7 +26,7 @@
|
||||
// --only-generate: Ony Generate World And Then Exit
|
||||
static bool only_generate = false;
|
||||
__attribute__((constructor)) static void _init_only_generate() {
|
||||
only_generate = getenv("_MCPI_ONLY_GENERATE") != nullptr;
|
||||
only_generate = getenv(_MCPI_ONLY_GENERATE_ENV) != nullptr;
|
||||
}
|
||||
|
||||
// Server Properties
|
||||
@ -584,7 +584,7 @@ static void server_init() {
|
||||
// Init Server
|
||||
void init_server() {
|
||||
server_init();
|
||||
set_and_print_env("MCPI_FEATURE_FLAGS", get_features());
|
||||
set_and_print_env("MCPI_RENDER_DISTANCE", "Tiny");
|
||||
set_and_print_env("MCPI_USERNAME", get_motd().c_str());
|
||||
set_and_print_env(MCPI_FEATURE_FLAGS_ENV, get_features());
|
||||
set_and_print_env(MCPI_RENDER_DISTANCE_ENV, "Tiny");
|
||||
set_and_print_env(MCPI_USERNAME_ENV, get_motd().c_str());
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ static void load_pending_skins(__attribute__((unused)) Minecraft *minecraft) {
|
||||
|
||||
// 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) {
|
||||
return custom_server;
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user