Environmental Variable & Logging Refactor
Some checks failed
CI / Test (AMD64, Client) (push) Blocked by required conditions
CI / Test (AMD64, Server) (push) Blocked by required conditions
CI / Test (ARM64, Client) (push) Blocked by required conditions
CI / Test (ARM64, Server) (push) Blocked by required conditions
CI / Test (ARMHF, Client) (push) Blocked by required conditions
CI / Test (ARMHF, Server) (push) Blocked by required conditions
CI / Build Example Mods (push) Blocked by required conditions
CI / Release (push) Blocked by required conditions
CI / Build (ARM64) (push) Successful in 19m47s
CI / Build (AMD64) (push) Successful in 20m4s
CI / Build (ARMHF) (push) Has been cancelled

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

View File

@ -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 = "";
{

View File

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

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

View File

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

View File

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

View File

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

View File

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

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(disable_crash_report, "disable-crash-report", -1, "Disable Crash Report Dialog")
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";
// 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, ...) \

View File

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

View File

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

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
#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();

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

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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;
@ -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)
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_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");
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -32,6 +32,7 @@ __attribute__((constructor)) static void init() {
init_bucket();
init_cake();
init_home();
init_override();
if (!reborn_is_server()) {
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) {
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);

View File

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

View File

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

View File

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

View File

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

View File

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