2022-07-14 03:35:05 +00:00
|
|
|
#include <sstream>
|
2021-06-17 21:32:24 +00:00
|
|
|
#include <cstring>
|
|
|
|
#include <cerrno>
|
|
|
|
#include <sys/wait.h>
|
2021-08-16 03:11:03 +00:00
|
|
|
#include <sys/stat.h>
|
2021-06-17 21:32:24 +00:00
|
|
|
#include <vector>
|
|
|
|
#include <functional>
|
2022-03-16 23:51:45 +00:00
|
|
|
#include <algorithm>
|
2021-06-17 21:32:24 +00:00
|
|
|
|
|
|
|
#include <libreborn/libreborn.h>
|
|
|
|
|
2023-11-25 03:16:13 +00:00
|
|
|
#include "../util.h"
|
2021-06-17 21:32:24 +00:00
|
|
|
#include "../bootstrap.h"
|
2022-09-22 21:43:21 +00:00
|
|
|
#include "launcher.h"
|
|
|
|
#include "cache.h"
|
2021-06-17 21:32:24 +00:00
|
|
|
|
2022-03-16 23:51:45 +00:00
|
|
|
// Strip Feature Flag Default
|
2022-09-22 21:43:21 +00:00
|
|
|
std::string strip_feature_flag_default(std::string flag, bool *default_ret) {
|
2022-03-16 23:51:45 +00:00
|
|
|
// Valid Values
|
|
|
|
std::string true_str = "TRUE ";
|
|
|
|
std::string false_str = "FALSE ";
|
|
|
|
// Test
|
|
|
|
if (flag.rfind(true_str, 0) == 0) {
|
|
|
|
// Enabled By Default
|
|
|
|
if (default_ret != NULL) {
|
|
|
|
*default_ret = true;
|
|
|
|
}
|
|
|
|
return flag.substr(true_str.length(), std::string::npos);
|
|
|
|
} else if (flag.rfind(false_str, 0) == 0) {
|
|
|
|
// Disabled By Default
|
|
|
|
if (default_ret != NULL) {
|
|
|
|
*default_ret = false;
|
|
|
|
}
|
|
|
|
return flag.substr(false_str.length(), std::string::npos);
|
|
|
|
} else {
|
|
|
|
// Invalid
|
2022-04-15 01:12:42 +00:00
|
|
|
ERR("Invalid Feature Flag Default");
|
2022-03-16 23:51:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-17 21:32:24 +00:00
|
|
|
// Load Available Feature Flags
|
2022-07-14 03:35:05 +00:00
|
|
|
extern unsigned char available_feature_flags[];
|
2022-07-30 02:13:03 +00:00
|
|
|
extern size_t available_feature_flags_len;
|
2022-09-22 21:43:21 +00:00
|
|
|
void load_available_feature_flags(std::function<void(std::string)> callback) {
|
2021-06-17 21:32:24 +00:00
|
|
|
// Get Path
|
|
|
|
char *binary_directory = get_binary_directory();
|
|
|
|
std::string path = std::string(binary_directory) + "/available-feature-flags";
|
|
|
|
free(binary_directory);
|
|
|
|
// Load File
|
2022-07-14 03:35:05 +00:00
|
|
|
std::string data(available_feature_flags, available_feature_flags + available_feature_flags_len);
|
|
|
|
std::stringstream stream(data);
|
|
|
|
// Store Lines
|
|
|
|
std::vector<std::string> lines;
|
|
|
|
// Read File
|
|
|
|
{
|
|
|
|
std::string line;
|
|
|
|
while (std::getline(stream, line)) {
|
|
|
|
if (line.length() > 0) {
|
|
|
|
// Verify Line
|
|
|
|
if (line.find('|') == std::string::npos) {
|
|
|
|
lines.push_back(line);
|
|
|
|
} else {
|
|
|
|
// Invalid Line
|
|
|
|
ERR("Feature Flag Contains Invalid '|'");
|
2021-06-17 21:32:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-07-14 03:35:05 +00:00
|
|
|
}
|
|
|
|
// Sort
|
|
|
|
std::sort(lines.begin(), lines.end(), [](std::string a, std::string b) {
|
|
|
|
// Strip Defaults
|
|
|
|
std::string stripped_a = strip_feature_flag_default(a, NULL);
|
|
|
|
std::string stripped_b = strip_feature_flag_default(b, NULL);
|
2022-03-16 23:51:45 +00:00
|
|
|
// Sort
|
2022-07-14 03:35:05 +00:00
|
|
|
return stripped_a < stripped_b;
|
|
|
|
});
|
|
|
|
// Run Callbacks
|
|
|
|
for (std::string &line : lines) {
|
|
|
|
callback(line);
|
2021-06-17 21:32:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run Command And Set Environmental Variable
|
2022-03-14 23:09:25 +00:00
|
|
|
static void run_command_and_set_env(const char *env_name, const char *command[]) {
|
2021-06-17 21:32:24 +00:00
|
|
|
// Only Run If Environmental Variable Is NULL
|
|
|
|
if (getenv(env_name) == NULL) {
|
|
|
|
// Run
|
|
|
|
int return_code;
|
2023-11-11 05:44:26 +00:00
|
|
|
char *output = run_command(command, &return_code, NULL);
|
2021-06-17 21:32:24 +00:00
|
|
|
if (output != NULL) {
|
|
|
|
// Trim
|
|
|
|
int length = strlen(output);
|
|
|
|
if (output[length - 1] == '\n') {
|
|
|
|
output[length - 1] = '\0';
|
|
|
|
}
|
|
|
|
// Set
|
2022-03-10 03:08:47 +00:00
|
|
|
set_and_print_env(env_name, output);
|
2022-03-14 23:09:25 +00:00
|
|
|
// Free
|
|
|
|
free(output);
|
2021-06-17 21:32:24 +00:00
|
|
|
}
|
|
|
|
// Check Return Code
|
2022-05-15 17:51:28 +00:00
|
|
|
if (!is_exit_status_success(return_code)) {
|
2022-10-01 05:37:20 +00:00
|
|
|
// Launch Interrupted
|
2022-05-14 02:36:12 +00:00
|
|
|
exit(EXIT_SUCCESS);
|
2021-06-17 21:32:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use Zenity To Set Environmental Variable
|
2022-06-10 01:31:40 +00:00
|
|
|
#define DIALOG_TITLE "Launcher"
|
2021-06-17 21:32:24 +00:00
|
|
|
static void run_zenity_and_set_env(const char *env_name, std::vector<std::string> command) {
|
|
|
|
// Create Full Command
|
|
|
|
std::vector<std::string> full_command;
|
|
|
|
full_command.push_back("zenity");
|
2022-06-10 01:31:40 +00:00
|
|
|
full_command.push_back("--title");
|
|
|
|
full_command.push_back(DIALOG_TITLE);
|
2022-05-14 03:27:06 +00:00
|
|
|
full_command.push_back("--name");
|
2022-07-30 02:13:03 +00:00
|
|
|
full_command.push_back(MCPI_APP_ID);
|
2021-06-17 21:32:24 +00:00
|
|
|
full_command.insert(full_command.end(), command.begin(), command.end());
|
|
|
|
// Convert To C Array
|
|
|
|
const char *full_command_array[full_command.size() + 1];
|
|
|
|
for (std::vector<std::string>::size_type i = 0; i < full_command.size(); i++) {
|
|
|
|
full_command_array[i] = full_command[i].c_str();
|
|
|
|
}
|
|
|
|
full_command_array[full_command.size()] = NULL;
|
|
|
|
// Run
|
2022-03-14 23:09:25 +00:00
|
|
|
run_command_and_set_env(env_name, full_command_array);
|
2021-06-17 21:32:24 +00:00
|
|
|
}
|
|
|
|
|
2022-07-08 17:57:48 +00:00
|
|
|
// Set Variable If Not Already Set
|
|
|
|
static void set_env_if_unset(const char *env_name, std::function<std::string()> callback) {
|
|
|
|
if (getenv(env_name) == NULL) {
|
|
|
|
char *value = strdup(callback().c_str());
|
|
|
|
ALLOC_CHECK(value);
|
|
|
|
set_and_print_env(env_name, value);
|
|
|
|
free(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-17 21:32:24 +00:00
|
|
|
// Launch
|
2022-04-28 03:38:30 +00:00
|
|
|
#define LIST_DIALOG_SIZE "400"
|
2021-06-17 21:32:24 +00:00
|
|
|
int main(int argc, char *argv[]) {
|
2022-06-25 00:37:52 +00:00
|
|
|
// Don't Run As Root
|
2022-07-16 00:09:51 +00:00
|
|
|
if (getenv("_MCPI_SKIP_ROOT_CHECK") == NULL && (getuid() == 0 || geteuid() == 0)) {
|
2022-06-25 00:37:52 +00:00
|
|
|
ERR("Don't Run As Root");
|
|
|
|
}
|
|
|
|
|
2022-09-22 21:43:21 +00:00
|
|
|
// Ensure HOME
|
|
|
|
if (getenv("HOME") == NULL) {
|
|
|
|
ERR("$HOME Isn't Set");
|
|
|
|
}
|
|
|
|
|
2023-12-02 19:23:28 +00:00
|
|
|
// Check For Display
|
|
|
|
#ifndef MCPI_HEADLESS_MODE
|
|
|
|
if (getenv("DISPLAY") == NULL || getenv("WAYLAND_DISPLAY") == NULL) {
|
|
|
|
ERR("No display attached! Make sure $DISPLAY or $WAYLAND_DISPLAY is set.");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2021-06-17 21:32:24 +00:00
|
|
|
// Print Features
|
|
|
|
for (int i = 1; i < argc; i++) {
|
|
|
|
if (strcmp(argv[i], "--print-available-feature-flags") == 0) {
|
|
|
|
// Print Available Feature Flags
|
|
|
|
load_available_feature_flags([](std::string line) {
|
|
|
|
printf("%s\n", line.c_str());
|
|
|
|
fflush(stdout);
|
|
|
|
});
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-23 04:31:42 +00:00
|
|
|
// Pre-Bootstrap
|
|
|
|
pre_bootstrap(argc, argv);
|
|
|
|
|
|
|
|
// Create ~/.minecraft-pi If Needed
|
|
|
|
{
|
|
|
|
char *minecraft_folder = NULL;
|
|
|
|
safe_asprintf(&minecraft_folder, "%s" HOME_SUBDIRECTORY_FOR_GAME_DATA, getenv("HOME"));
|
|
|
|
const char *const command[] = {"mkdir", "-p", minecraft_folder, NULL};
|
|
|
|
run_simple_command(command, "Unable To Create Data Directory");
|
|
|
|
free(minecraft_folder);
|
|
|
|
}
|
|
|
|
|
|
|
|
// --wipe-cache
|
|
|
|
for (int i = 1; i < argc; i++) {
|
|
|
|
if (strcmp(argv[i], "--wipe-cache") == 0) {
|
|
|
|
wipe_cache();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// --no-cache
|
|
|
|
bool no_cache = false;
|
|
|
|
for (int i = 1; i < argc; i++) {
|
|
|
|
if (strcmp(argv[i], "--no-cache") == 0) {
|
|
|
|
no_cache = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Load Cache
|
|
|
|
launcher_cache cache = no_cache ? empty_cache : load_cache();
|
|
|
|
|
2022-07-08 17:57:48 +00:00
|
|
|
// --default
|
|
|
|
for (int i = 1; i < argc; i++) {
|
|
|
|
if (strcmp(argv[i], "--default") == 0) {
|
|
|
|
// Use Default Feature Flags
|
2022-09-23 04:31:42 +00:00
|
|
|
set_env_if_unset("MCPI_FEATURE_FLAGS", [&cache]() {
|
2022-07-08 17:57:48 +00:00
|
|
|
std::string feature_flags = "";
|
2022-09-23 04:31:42 +00:00
|
|
|
load_available_feature_flags([&feature_flags, &cache](std::string flag) {
|
|
|
|
bool value;
|
2022-07-08 17:57:48 +00:00
|
|
|
// Strip Default Value
|
2022-09-23 04:31:42 +00:00
|
|
|
std::string stripped_flag = strip_feature_flag_default(flag, &value);
|
|
|
|
// Use Cache
|
|
|
|
if (cache.feature_flags.count(stripped_flag) > 0) {
|
|
|
|
value = cache.feature_flags[stripped_flag];
|
|
|
|
}
|
2022-07-08 17:57:48 +00:00
|
|
|
// Specify Default Value
|
2022-09-23 04:31:42 +00:00
|
|
|
if (value) {
|
2022-07-08 17:57:48 +00:00
|
|
|
// Enabled By Default
|
|
|
|
feature_flags += stripped_flag + '|';
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (feature_flags.length() > 0 && feature_flags[feature_flags.length() - 1] == '|') {
|
|
|
|
feature_flags.pop_back();
|
|
|
|
}
|
|
|
|
return feature_flags;
|
|
|
|
});
|
2022-09-23 04:31:42 +00:00
|
|
|
set_env_if_unset("MCPI_RENDER_DISTANCE", [&cache]() {
|
|
|
|
return cache.render_distance;
|
2022-07-08 17:57:48 +00:00
|
|
|
});
|
2022-09-23 04:31:42 +00:00
|
|
|
set_env_if_unset("MCPI_USERNAME", [&cache]() {
|
|
|
|
return cache.username;
|
2022-07-08 17:57:48 +00:00
|
|
|
});
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-17 21:32:24 +00:00
|
|
|
// Setup MCPI_FEATURE_FLAGS
|
|
|
|
{
|
|
|
|
std::vector<std::string> command;
|
|
|
|
command.push_back("--list");
|
|
|
|
command.push_back("--checklist");
|
|
|
|
command.push_back("--width");
|
2022-04-28 03:38:30 +00:00
|
|
|
command.push_back(LIST_DIALOG_SIZE);
|
2021-06-17 21:32:24 +00:00
|
|
|
command.push_back("--height");
|
2022-04-28 03:38:30 +00:00
|
|
|
command.push_back(LIST_DIALOG_SIZE);
|
2021-06-17 21:32:24 +00:00
|
|
|
command.push_back("--column");
|
|
|
|
command.push_back("Enabled");
|
|
|
|
command.push_back("--column");
|
|
|
|
command.push_back("Feature");
|
2022-09-22 21:43:21 +00:00
|
|
|
load_available_feature_flags([&command, &cache](std::string flag) {
|
|
|
|
bool value;
|
2022-03-16 23:51:45 +00:00
|
|
|
// Strip Default Value
|
2022-09-22 21:43:21 +00:00
|
|
|
std::string stripped_flag = strip_feature_flag_default(flag, &value);
|
|
|
|
// Use Cache
|
|
|
|
if (cache.feature_flags.count(stripped_flag) > 0) {
|
|
|
|
value = cache.feature_flags[stripped_flag];
|
|
|
|
}
|
2022-03-16 23:51:45 +00:00
|
|
|
// Specify Default Value
|
2022-09-22 21:43:21 +00:00
|
|
|
if (value) {
|
2021-06-17 21:32:24 +00:00
|
|
|
// Enabled By Default
|
|
|
|
command.push_back("TRUE");
|
2022-03-16 23:51:45 +00:00
|
|
|
} else {
|
2021-06-17 21:32:24 +00:00
|
|
|
// Disabled By Default
|
|
|
|
command.push_back("FALSE");
|
|
|
|
}
|
2022-03-16 23:51:45 +00:00
|
|
|
// Specify Name
|
|
|
|
command.push_back(stripped_flag);
|
2021-06-17 21:32:24 +00:00
|
|
|
});
|
|
|
|
// Run
|
|
|
|
run_zenity_and_set_env("MCPI_FEATURE_FLAGS", command);
|
|
|
|
}
|
|
|
|
// Setup MCPI_RENDER_DISTANCE
|
|
|
|
{
|
|
|
|
std::vector<std::string> command;
|
|
|
|
command.push_back("--list");
|
|
|
|
command.push_back("--radiolist");
|
|
|
|
command.push_back("--width");
|
2022-04-28 03:38:30 +00:00
|
|
|
command.push_back(LIST_DIALOG_SIZE);
|
2021-06-17 21:32:24 +00:00
|
|
|
command.push_back("--height");
|
2022-04-28 03:38:30 +00:00
|
|
|
command.push_back(LIST_DIALOG_SIZE);
|
2021-06-17 21:32:24 +00:00
|
|
|
command.push_back("--text");
|
2022-04-28 03:38:30 +00:00
|
|
|
command.push_back("Select Minecraft Render Distance:");
|
2021-06-17 21:32:24 +00:00
|
|
|
command.push_back("--column");
|
|
|
|
command.push_back("Selected");
|
|
|
|
command.push_back("--column");
|
|
|
|
command.push_back("Name");
|
2022-07-08 17:57:48 +00:00
|
|
|
std::string render_distances[] = {"Far", "Normal", "Short", "Tiny"};
|
|
|
|
for (std::string &render_distance : render_distances) {
|
2022-09-22 21:43:21 +00:00
|
|
|
command.push_back(render_distance.compare(cache.render_distance) == 0 ? "TRUE" : "FALSE");
|
2022-07-08 17:57:48 +00:00
|
|
|
command.push_back(render_distance);
|
|
|
|
}
|
2021-06-17 21:32:24 +00:00
|
|
|
// Run
|
|
|
|
run_zenity_and_set_env("MCPI_RENDER_DISTANCE", command);
|
|
|
|
}
|
2022-04-13 00:38:44 +00:00
|
|
|
// Setup MCPI_USERNAME
|
|
|
|
{
|
|
|
|
std::vector<std::string> command;
|
|
|
|
command.push_back("--entry");
|
|
|
|
command.push_back("--text");
|
2022-04-28 03:38:30 +00:00
|
|
|
command.push_back("Enter Minecraft Username:");
|
2022-04-13 00:38:44 +00:00
|
|
|
command.push_back("--entry-text");
|
2022-09-22 21:43:21 +00:00
|
|
|
command.push_back(cache.username);
|
2022-04-13 00:38:44 +00:00
|
|
|
// Run
|
|
|
|
run_zenity_and_set_env("MCPI_USERNAME", command);
|
|
|
|
}
|
2021-06-17 21:32:24 +00:00
|
|
|
|
2022-09-22 21:43:21 +00:00
|
|
|
// Save Cache
|
|
|
|
if (!no_cache) {
|
|
|
|
save_cache();
|
|
|
|
}
|
|
|
|
|
2021-06-17 21:32:24 +00:00
|
|
|
// Bootstrap
|
|
|
|
bootstrap(argc, argv);
|
|
|
|
}
|