diff --git a/dependencies/imgui/CMakeLists.txt b/dependencies/imgui/CMakeLists.txt index 5dd3adcbfb..56fad54bbc 100644 --- a/dependencies/imgui/CMakeLists.txt +++ b/dependencies/imgui/CMakeLists.txt @@ -11,12 +11,14 @@ add_library(imgui SHARED src/imgui_draw.cpp src/imgui_tables.cpp src/imgui_widgets.cpp + src/misc/cpp/imgui_stdlib.cpp src/backends/imgui_impl_glfw.cpp src/backends/imgui_impl_opengl2.cpp ) setup_header_dirs(imgui "${CMAKE_CURRENT_SOURCE_DIR}/src" "${CMAKE_CURRENT_SOURCE_DIR}/src/backends" + "${CMAKE_CURRENT_SOURCE_DIR}/src/misc/cpp" ) find_package(OpenGL REQUIRED) target_link_libraries(imgui PUBLIC glfw OpenGL::GL) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index fc9f2293a5..4fda7b8897 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -76,8 +76,9 @@ * `Force Survival Mode Inventory UI` (Disabled By Default) * `Force Survival Mode Inventory Behavior` (Disabled By Default) * `Maximize Creative Mode Inventory Stack Size` (Disabled By Default) -* Rename `Disable Buggy Held Item Caching` Feature Flag To `Fix Held Item Caching` -* Rename `Disable 'gui_blocks' Atlas` Feature Flag To `Regenerate "gui_blocks" Atlas` +* Rename Feature Flags + * `Disable Buggy Held Item Caching` To `Fix Held Item Caching` + * `Disable 'gui_blocks' Atlas` To `Regenerate "gui_blocks" Atlas` * Add Milk Buckets * Included In The `Add Buckets` Feature Flag * Removed `Property Scale Animated Textures` Feature Flag diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index ee8062e102..e52ea57143 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -16,9 +16,12 @@ add_executable(launcher src/ui/frame.cpp src/client/configuration.cpp src/client/cache.cpp - src/client/available-feature-flags # Show In IDE + src/client/ui.cpp + src/client/flags/node.cpp + src/client/flags/flags.cpp + src/client/flags/available-feature-flags # Show In IDE ) -embed_resource(launcher src/client/available-feature-flags) +embed_resource(launcher src/client/flags/available-feature-flags) target_link_libraries(launcher reborn-util LIB_LIEF diff --git a/launcher/src/client/available-feature-flags b/launcher/src/client/available-feature-flags deleted file mode 100644 index c3b76ffed9..0000000000 --- a/launcher/src/client/available-feature-flags +++ /dev/null @@ -1,114 +0,0 @@ -FALSE Full Touch GUI -TRUE Fix Bow & Arrow -TRUE Fix Attacking -FALSE Force Mob Spawning -TRUE Disable Autojump By Default -TRUE Display Nametags By Default -TRUE Fix Sign Placement -TRUE Show Block Outlines -FALSE Expand Creative Mode Inventory -FALSE Remove Creative Mode Restrictions -FALSE Display Slot Count In Creative Mode -FALSE Force Survival Mode Inventory UI -FALSE Force Survival Mode Inventory Behavior -FALSE Maximize Creative Mode Inventory Stack Size -TRUE Animated Water -TRUE Animated Lava -TRUE Animated Fire -TRUE Regenerate "gui_blocks" Atlas -TRUE Fix Camera Rendering -TRUE Implement Chat -FALSE Hide Chat Messages -TRUE Implement Death Messages -TRUE Implement Game-Mode Switching -TRUE Allow Joining Survival Mode Servers -TRUE Miscellaneous Input Fixes -TRUE Bind "Q" Key To Item Dropping -TRUE Bind Common Toggleable Options To Function Keys -TRUE Render Selected Item Text -TRUE External Server Support -TRUE Load Language Files -TRUE Implement Sound Engine -TRUE Close Current Screen On Death -FALSE Disable Raw Mouse Motion (Not Recommended) -TRUE Fix Furnace Not Checking Item Auxiliary -TRUE Improved Cursor Rendering -TRUE Disable V-Sync -TRUE Fix Options Screen -TRUE Force Touch GUI Inventory -TRUE Fix Pause Menu -TRUE Add Title Screen Background -TRUE Force Touch GUI Button Behavior -TRUE Improved Button Hover Behavior -TRUE Implement Create World Dialog -FALSE Remove Forced GUI Lag (Can Break Joining Servers) -TRUE Add Buckets -TRUE Classic HUD -TRUE Translucent Toolbar -TRUE Improved Classic Title Screen -FALSE Disable Speed Bridging -FALSE Disable Creative Mode Mining Delay -FALSE Add Biome Colors To Grass -TRUE Generate Caves -FALSE Disable Block Tinting -TRUE Disable Hostile AI In Creative Mode -TRUE Load Custom Skins -TRUE 3D Chest Model -TRUE Replace Block Highlight With Outline -TRUE Add Cake -TRUE Use Java Beta 1.3 Light Ramp -TRUE Send Full Level When Hosting Game -FALSE Food Overlay -TRUE Add Splashes -TRUE Display Date In Select World Screen -TRUE Optimized Chunk Sorting -TRUE Fix Held Item Caching -TRUE Add Reborn Info To Options -FALSE Log FPS -TRUE Add Welcome Screen -TRUE F3 Debug Information -TRUE Multidraw Rendering -TRUE Add Missing Language Strings -TRUE Fix Pigmen Burning In The Sun -TRUE Fix Carried Grass's Bottom Texture -TRUE Hide Crosshair In Third-Person -TRUE Fix Camera Legs -TRUE Implement Crafting Remainders -TRUE Fix Door Duplication -TRUE Fix Cobweb Lighting -TRUE Fix Sneaking Syncing -TRUE Fix Fire Immunity -TRUE Fix Fire Syncing -TRUE Fix Sunlight Not Properly Setting Mobs On Fire -TRUE Stop Creative Players From Burning -TRUE Render Fire In Third-Person -TRUE Improved Water Rendering -TRUE Classic Item Count UI -TRUE Fix Screen Rendering When Hiding HUD -TRUE Sanitize Usernames -TRUE Patch RakNet Security Bug -TRUE Log RakNet Startup Errors -TRUE Prevent Unnecessary Server Pinging -TRUE Proper OpenGL Buffer Generation -TRUE Fix Furnace Screen Visual Bug -TRUE Fix Text Wrapping -TRUE Fullscreen Support -TRUE Always Save Chest Tile Entities -TRUE Fix Transferring Durability When Using Items -TRUE Fix Switching Perspective While Sneaking -TRUE Log Chat Messages -TRUE Log Game Status -TRUE Screenshot Support -TRUE Fix Camera Functionality -TRUE Allow High-Resolution Title -TRUE Improved Classic Title Positioning -TRUE Use Updated Title -TRUE Hide Block Outline When GUI Is Hidden -TRUE Fix Crash When Generating Certain Seeds -TRUE Click Buttons On Mouse Down -TRUE 3D Dropped Items -TRUE Render Entity Shadows -TRUE Render Vignette -TRUE Increase Render Chunk Size -TRUE Proper Entity Shading -TRUE Fix Sugar Position In Hand \ No newline at end of file diff --git a/launcher/src/client/cache.cpp b/launcher/src/client/cache.cpp index 9d028de536..e52bde5e99 100644 --- a/launcher/src/client/cache.cpp +++ b/launcher/src/client/cache.cpp @@ -8,8 +8,9 @@ #include -#include "configuration.h" #include "cache.h" +#include "configuration.h" +#include "flags/flags.h" // Get Cache Path static std::string get_cache_path() { @@ -37,7 +38,7 @@ launcher_cache load_cache() { std::ifstream stream(get_cache_path(), std::ios::in | std::ios::binary); if (!stream) { // Fail - struct stat s; + struct stat s = {}; // No Warning If File Doesn't Exist if (stat(get_cache_path().c_str(), &s) == 0) { WARN("Unable To Open Launcher Cache For Loading"); @@ -49,14 +50,12 @@ launcher_cache load_cache() { // Check Version unsigned char cache_version; stream.read((char *) &cache_version, 1); - if (stream.eof() || cache_version != (unsigned char) CACHE_VERSION) { - // Fail - if (!stream.eof()) { - WARN("Invalid Launcher Cache Version (Expected: %i, Actual: %i)", (int) CACHE_VERSION, (int) cache_version); - } else { - WARN("Unable To Read Launcher Cache Version"); - } - stream.close(); + if (stream.eof()) { + // Unable To Read Version + WARN("Unable To Read Launcher Cache Version"); + } else if (cache_version != (unsigned char) CACHE_VERSION) { + // Invalid Version + WARN("Invalid Launcher Cache Version (Expected: %i, Actual: %i)", CACHE_VERSION, (int) cache_version); } else { // Load Username And Render Distance launcher_cache cache; @@ -66,16 +65,15 @@ launcher_cache load_cache() { // Load Feature Flags std::string flag; while (!stream.eof() && std::getline(stream, flag, '\0')) { - if (flag.length() > 0) { - unsigned char is_enabled = 0; - stream.read((char *) &is_enabled, 1); - cache.feature_flags[flag] = is_enabled != (unsigned char) 0; + if (!flag.empty()) { + bool is_enabled = false; + stream.read((char *) &is_enabled, sizeof(bool)); + cache.feature_flags[flag] = is_enabled; } stream.peek(); } // Finish - stream.close(); if (!stream) { // Fail WARN("Failure While Loading Launcher Cache"); @@ -85,6 +83,9 @@ launcher_cache load_cache() { } } + // Close + stream.close(); + // Unlock File unlock_file(get_cache_path().c_str(), lock_fd); } @@ -94,15 +95,10 @@ launcher_cache load_cache() { } // Save -#define write_env_to_stream(stream, env) \ - { \ - const char *env_value = getenv(env); \ - if (env == NULL) { \ - IMPOSSIBLE(); \ - } \ - stream.write(env_value, strlen(env_value) + 1); \ - } -void save_cache() { +static void write_env_to_stream(std::ofstream &stream, const std::string &value) { + stream.write(value.c_str(), int(value.size()) + 1); +} +void save_cache(const State &state) { // Log DEBUG("Saving Launcher Cache..."); @@ -116,41 +112,23 @@ void save_cache() { int lock_fd = lock_file(get_cache_path().c_str()); // Save Cache Version - unsigned char cache_version = (unsigned char) CACHE_VERSION; + constexpr unsigned char cache_version = CACHE_VERSION; stream.write((const char *) &cache_version, 1); // Save Username And Render Distance - write_env_to_stream(stream, MCPI_USERNAME_ENV); - write_env_to_stream(stream, MCPI_RENDER_DISTANCE_ENV); + write_env_to_stream(stream, state.username); + write_env_to_stream(stream, state.render_distance); // Save Feature Flags - std::unordered_map flags; - load_available_feature_flags([&flags](std::string flag) { - std::string stripped_flag = strip_feature_flag_default(flag, nullptr); - flags[stripped_flag] = false; - }); - { - const char *enabled_flags = getenv(MCPI_FEATURE_FLAGS_ENV); - if (enabled_flags == nullptr) { - IMPOSSIBLE(); - } - std::istringstream enabled_flags_stream(enabled_flags); - std::string flag; - while (std::getline(enabled_flags_stream, flag, '|')) { - if (flag.length() > 0) { - flags[flag] = true; - } - } - } - for (auto &it : flags) { - stream.write(it.first.c_str(), it.first.size() + 1); - unsigned char val = it.second ? (unsigned char) 1 : (unsigned char) 0; - stream.write((const char *) &val, 1); + const std::unordered_map flags_cache = state.flags.to_cache(); + for (const std::pair &it : flags_cache) { + stream.write(it.first.c_str(), int(it.first.size()) + 1); + stream.write((const char *) &it.second, sizeof(bool)); } // Finish stream.close(); - if (!stream.good()) { + if (!stream) { WARN("Failure While Saving Launcher Cache"); } diff --git a/launcher/src/client/cache.h b/launcher/src/client/cache.h index 1637acbda7..6b5c3f7473 100644 --- a/launcher/src/client/cache.h +++ b/launcher/src/client/cache.h @@ -16,7 +16,8 @@ extern launcher_cache empty_cache; launcher_cache load_cache(); // Save Cache -void save_cache(); +struct State; +void save_cache(const State &state); // Wipe Cache void wipe_cache(); diff --git a/launcher/src/client/configuration.cpp b/launcher/src/client/configuration.cpp index 9d049fe210..7ed0b543f6 100644 --- a/launcher/src/client/configuration.cpp +++ b/launcher/src/client/configuration.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -12,134 +11,40 @@ #include "../util/util.h" #include "configuration.h" #include "cache.h" -#include "../ui/frame.h" -// Strip Feature Flag Default -std::string strip_feature_flag_default(const std::string &flag, bool *default_ret) { - // 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 != nullptr) { - *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 != nullptr) { - *default_ret = false; - } - return flag.substr(false_str.length(), std::string::npos); +// State +State::State(const launcher_cache &cache): flags("") { + username = cache.username; + render_distance = cache.render_distance; + flags = Flags::get(); + flags.from_cache(cache.feature_flags); +} +template +static void update_from_env(const char *env, T &value, const bool save) { + if (save) { + const std::string str = static_cast(value); + set_and_print_env(env, str.c_str()); } else { - // Invalid - ERR("Invalid Feature Flag Default"); - } -} - -// Load Available Feature Flags -EMBEDDED_RESOURCE(available_feature_flags); -void load_available_feature_flags(const std::function &callback) { - // Load Data - const std::string data(available_feature_flags, available_feature_flags + available_feature_flags_len); - std::stringstream stream(data); - // Store Lines - std::vector lines; - // Read File - { - std::string line; - while (std::getline(stream, line)) { - if (!line.empty()) { - // Verify Line - if (line.find('|') == std::string::npos) { - lines.push_back(line); - } else { - // Invalid Line - ERR("Feature Flag Contains Invalid '|'"); - } - } - } - } - // Sort - std::sort(lines.begin(), lines.end(), [](const std::string &a, const std::string &b) { - // Strip Defaults - 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; - }); - // Run Callbacks - for (const std::string &line : lines) { - callback(line); - } -} - -// Run Command And Set Environmental Variable -static void run_command_and_set_env(const char *env_name, const char *command[]) { - // Only Run If Environmental Variable Is NULL - if (getenv(env_name) == nullptr) { - // Check $DISPLAY - reborn_check_display(); - // Run - int return_code; - const std::vector *output = run_command(command, &return_code); - std::string output_str = (const char *) output->data(); - delete output; - // Trim - const std::string::size_type length = output_str.length(); - if (length > 0 && output_str[length - 1] == '\n') { - output_str.pop_back(); - } - // Set - set_and_print_env(env_name, output_str.c_str()); - // Check Return Code - if (!is_exit_status_success(return_code)) { - // Launch Interrupted - exit(EXIT_SUCCESS); + const char *env_value = getenv(env); + if (env_value != nullptr) { + value = env_value; } } } +void State::update(const bool save) { + update_from_env(MCPI_FEATURE_FLAGS_ENV, flags, save); + update_from_env(MCPI_USERNAME_ENV, username, save); + update_from_env(MCPI_RENDER_DISTANCE_ENV, render_distance, save); +} -// Use Zenity To Set Environmental Variable #define DIALOG_TITLE "Launcher" -static void run_zenity_and_set_env(const char *env_name, std::vector command) { - // Create Full Command - std::vector full_command; - full_command.push_back("zenity"); - full_command.push_back("--title"); - full_command.push_back(DIALOG_TITLE); - full_command.push_back("--name"); - full_command.push_back(MCPI_APP_ID); - 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::size_type i = 0; i < full_command.size(); i++) { - full_command_array[i] = full_command[i].c_str(); - } - full_command_array[full_command.size()] = nullptr; - // Run - run_command_and_set_env(env_name, full_command_array); -} - -// Set Variable If Not Already Set -static void set_env_if_unset(const char *env_name, const std::function &callback) { - if (getenv(env_name) == nullptr) { - char *value = strdup(callback().c_str()); - ALLOC_CHECK(value); - set_and_print_env(env_name, value); - free(value); - } -} // Handle Non-Launch Commands void handle_non_launch_client_only_commands(const options_t &options) { // Print Available Feature Flags if (options.print_available_feature_flags) { - load_available_feature_flags([](const std::string &line) { - printf("%s\n", line.c_str()); - fflush(stdout); - }); + const Flags flags = Flags::get(); + flags.print(); exit(EXIT_SUCCESS); } // Wipe Cache If Needed @@ -149,131 +54,31 @@ void handle_non_launch_client_only_commands(const options_t &options) { } } -struct Test final : Frame { - Test(): Frame(DIALOG_TITLE, 640, 480) {} - int render() override { - ImGui::Button("Hello World!"); - ImGui::PushFont(monospace); - ImGui::Button("Custom Font"); - ImGui::PopFont(); - return 0; - } -}; - // Configure Client Options -#define LIST_DIALOG_SIZE "400" void configure_client(const options_t &options) { // Load Cache - launcher_cache cache = options.no_cache ? empty_cache : load_cache(); + const launcher_cache cache = options.no_cache ? empty_cache : load_cache(); - Test *test = new Test; - test->run(); - delete test; + // Setup State + State state(cache); + state.update(false); // --default - if (options.use_default) { - // Use Default Feature Flags - 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; - // Strip Default Value - std::string stripped_flag = strip_feature_flag_default(flag, &value); - // Use Cache - if (cache.feature_flags.count(stripped_flag) > 0) { - value = cache.feature_flags[stripped_flag]; - } - // Specify Default Value - if (value) { - // Enabled By Default - feature_flags += stripped_flag + '|'; - } - }); - if (!feature_flags.empty() && feature_flags[feature_flags.length() - 1] == '|') { - feature_flags.pop_back(); - } - return feature_flags; - }); - set_env_if_unset(MCPI_RENDER_DISTANCE_ENV, [&cache]() { - return cache.render_distance; - }); - set_env_if_unset(MCPI_USERNAME_ENV, [&cache]() { - return cache.username; - }); - } - - // Setup MCPI_FEATURE_FLAGS - { - std::vector command; - command.push_back("--list"); - command.push_back("--checklist"); - command.push_back("--width"); - command.push_back(LIST_DIALOG_SIZE); - command.push_back("--height"); - command.push_back(LIST_DIALOG_SIZE); - command.push_back("--column"); - command.push_back("Enabled"); - command.push_back("--column"); - command.push_back("Feature"); - load_available_feature_flags([&command, &cache](const std::string &flag) { - bool value; - // Strip Default Value - std::string stripped_flag = strip_feature_flag_default(flag, &value); - // Use Cache - if (cache.feature_flags.count(stripped_flag) > 0) { - value = cache.feature_flags[stripped_flag]; - } - // Specify Default Value - if (value) { - // Enabled By Default - command.push_back("TRUE"); - } else { - // Disabled By Default - command.push_back("FALSE"); - } - // Specify Name - command.push_back(stripped_flag); - }); - // Run - run_zenity_and_set_env(MCPI_FEATURE_FLAGS_ENV, command); - } - // Setup MCPI_RENDER_DISTANCE - { - std::vector command; - command.push_back("--list"); - command.push_back("--radiolist"); - command.push_back("--width"); - command.push_back(LIST_DIALOG_SIZE); - command.push_back("--height"); - command.push_back(LIST_DIALOG_SIZE); - command.push_back("--text"); - command.push_back("Select Minecraft Render Distance:"); - command.push_back("--column"); - command.push_back("Selected"); - command.push_back("--column"); - command.push_back("Name"); - std::string render_distances[] = {"Far", "Normal", "Short", "Tiny"}; - for (std::string &render_distance : render_distances) { - command.push_back(render_distance == cache.render_distance ? "TRUE" : "FALSE"); - command.push_back(render_distance); + if (!options.use_default) { + // Show UI + ConfigurationUI *ui = new ConfigurationUI(state); + const int ret = ui->run(); + delete ui; + if (ret <= 0) { + exit(EXIT_SUCCESS); } - // Run - run_zenity_and_set_env(MCPI_RENDER_DISTANCE_ENV, command); - } - // Setup MCPI_USERNAME - { - std::vector command; - command.push_back("--entry"); - command.push_back("--text"); - command.push_back("Enter Minecraft Username:"); - command.push_back("--entry-text"); - command.push_back(cache.username); - // Run - run_zenity_and_set_env(MCPI_USERNAME_ENV, command); } // Save Cache if (!options.no_cache) { - save_cache(); + save_cache(state); } + + // Update Environment + state.update(true); } diff --git a/launcher/src/client/configuration.h b/launcher/src/client/configuration.h index e7c20cea0e..d856813218 100644 --- a/launcher/src/client/configuration.h +++ b/launcher/src/client/configuration.h @@ -1,17 +1,38 @@ #pragma once #include -#include #include "../options/parser.h" +#include "cache.h" +#include "flags/flags.h" +#include "../ui/frame.h" -// Defaults +// Default Configuration #define DEFAULT_USERNAME "StevePi" #define DEFAULT_RENDER_DISTANCE "Short" -// Feature Flags -std::string strip_feature_flag_default(const std::string& flag, bool *default_ret); -void load_available_feature_flags(const std::function &callback); +// State +struct State { + explicit State(const launcher_cache &cache); + // Methods + void update(bool save); + // Properties + std::string username; + std::string render_distance; + Flags flags; +}; + +// UI +struct ConfigurationUI final : Frame { + explicit ConfigurationUI(State &state_); + int render() override; +private: + void draw_main(); + void draw_advanced() const; + static void draw_category(FlagNode &category); + State &state; + int render_distance_index; +}; // Handle Non-Launch Commands void handle_non_launch_client_only_commands(const options_t &options); diff --git a/launcher/src/client/flags/available-feature-flags b/launcher/src/client/flags/available-feature-flags new file mode 100644 index 0000000000..923065f479 --- /dev/null +++ b/launcher/src/client/flags/available-feature-flags @@ -0,0 +1,135 @@ +CATEGORY Creative Mode + CATEGORY Inventory + FALSE Expand Creative Mode Inventory + FALSE Maximize Creative Mode Inventory Stack Size + FALSE Force Survival Mode Inventory UI + FALSE Force Survival Mode Inventory Behavior + FALSE Display Slot Count In Creative Mode + FALSE Remove Creative Mode Restrictions + TRUE Disable Hostile AI In Creative Mode + TRUE Stop Creative Players From Burning +CATEGORY User Interface + CATEGORY Screens + CATEGORY Title + TRUE Add Title Screen Background + TRUE Improved Classic Title Screen + TRUE Add Splashes + TRUE Allow High-Resolution Title + TRUE Improved Classic Title Positioning + TRUE Use Updated Title + CATEGORY Options + TRUE Fix Options Screen + TRUE Add Reborn Info To Options + TRUE Force Touch GUI Inventory + TRUE Fix Pause Menu + TRUE Close Current Screen On Death + TRUE Implement Create World Dialog + TRUE Display Date In Select World Screen + TRUE Add Welcome Screen + CATEGORY HUD + TRUE Classic HUD + TRUE Translucent Toolbar + FALSE Food Overlay + TRUE Render Selected Item Text + TRUE F3 Debug Information + TRUE Hide Crosshair In Third-Person + CATEGORY Text + TRUE Load Language Files + TRUE Add Missing Language Strings + FALSE Full Touch GUI + TRUE Force Touch GUI Button Behavior + TRUE Improved Cursor Rendering + FALSE Remove Forced GUI Lag (Can Break Joining Servers) + TRUE Improved Button Hover Behavior + TRUE Classic Item Count UI + TRUE Click Buttons On Mouse Down +CATEGORY Rendering + CATEGORY Optimizations + TRUE Optimized Chunk Sorting + TRUE Multidraw Rendering + TRUE Increase Render Chunk Size + CATEGORY Camera + TRUE Fix Camera Rendering + TRUE Fix Camera Legs + CATEGORY Block Outline + TRUE Show Block Outlines + TRUE Replace Block Highlight With Outline + TRUE Hide Block Outline When GUI Is Hidden + CATEGORY Block Tinting + FALSE Add Biome Colors To Grass + FALSE Disable Block Tinting + TRUE Display Nametags By Default + TRUE Disable V-Sync + TRUE 3D Chest Model + TRUE Use Java Beta 1.3 Light Ramp + TRUE Render Fire In Third-Person + TRUE Improved Water Rendering + TRUE Proper OpenGL Buffer Generation + TRUE 3D Dropped Items + TRUE Render Entity Shadows + TRUE Render Vignette + TRUE Proper Entity Shading +CATEGORY Textures + TRUE Regenerate "gui_blocks" Atlas + TRUE Animated Water + TRUE Animated Lava + TRUE Animated Fire +CATEGORY Input + TRUE Fix Bow & Arrow + TRUE Fix Attacking + TRUE Disable Autojump By Default + TRUE Miscellaneous Input Fixes + TRUE Bind "Q" Key To Item Dropping + TRUE Bind Common Toggleable Options To Function Keys + FALSE Disable Raw Mouse Motion (Not Recommended) + FALSE Disable Speed Bridging + FALSE Disable Creative Mode Mining Delay +CATEGORY Multiplayer + CATEGORY Chat + TRUE Implement Chat + FALSE Hide Chat Messages + TRUE Allow Joining Survival Mode Servers + TRUE External Server Support + TRUE Send Full Level When Hosting Game + TRUE Sanitize Usernames +CATEGORY Gameplay + TRUE Implement Death Messages + TRUE Implement Game-Mode Switching + FALSE Force Mob Spawning + TRUE Fix Sign Placement + TRUE Add Buckets + TRUE Load Custom Skins + TRUE Add Cake + TRUE Implement Crafting Remainders + TRUE Implement Sound Engine + TRUE Generate Caves +CATEGORY Bug Fixes + TRUE Fix Furnace Not Checking Item Auxiliary + TRUE Fix Held Item Caching + TRUE Fix Pigmen Burning In The Sun + TRUE Fix Carried Grass's Bottom Texture + TRUE Fix Screen Rendering When Hiding HUD + TRUE Fix Door Duplication + TRUE Fix Cobweb Lighting + TRUE Fix Sneaking Syncing + TRUE Fix Fire Immunity + TRUE Fix Fire Syncing + TRUE Fix Sunlight Not Properly Setting Mobs On Fire + TRUE Patch RakNet Security Bug + TRUE Prevent Unnecessary Server Pinging + TRUE Fix Furnace Screen Visual Bug + TRUE Fix Text Wrapping + TRUE Fix Transferring Durability When Using Items + TRUE Fix Switching Perspective While Sneaking + TRUE Fix Crash When Generating Certain Seeds + TRUE Fix Sugar Position In Hand +CATEGORY Logging + FALSE Log FPS + TRUE Log Chat Messages + TRUE Log Game Status + TRUE Log RakNet Startup Errors +CATEGORY Miscellaneous + TRUE Fullscreen Support + TRUE Always Save Chest Tile Entities + TRUE Screenshot Support + TRUE Fix Camera Functionality \ No newline at end of file diff --git a/launcher/src/client/flags/flags.cpp b/launcher/src/client/flags/flags.cpp new file mode 100644 index 0000000000..066b576fde --- /dev/null +++ b/launcher/src/client/flags/flags.cpp @@ -0,0 +1,117 @@ +#include +#include +#include + +#include + +#include "flags.h" + +// All Flags +static unsigned int find_indent_level(std::string &str) { + constexpr unsigned int INDENT = 4; + unsigned int count = 0; + for (const char c : str) { + if (c == ' ') { + count++; + } else { + break; + } + } + str = str.substr(count); + return count / INDENT; +} +Flags::Flags(const std::string &data) { + // Read Lines + std::stringstream stream(data); + std::string line; + std::vector stack = {&root}; + while (std::getline(stream, line)) { + if (!line.empty()) { + // Get Parent + const unsigned int indent = find_indent_level(line); + if (indent >= stack.size()) { + ERR("Bad Feature Flag Indent: %s", line.c_str()); + } + stack.resize(indent + 1); + FlagNode &parent = *stack.back(); + // Add New Node + if (FlagNode::handle_line_prefix("CATEGORY", line)) { + // New Category + FlagNode &category = parent.add_category(line); + stack.push_back(&category); + } else { + // Add Flag + if (indent == 0) { + ERR("Feature Flag Outside Of Category: %s", line.c_str()); + } + parent.add_flag(line); + } + } + } + // Sort + root.sort(); +} +Flags::operator std::string() const { + std::string out; + root.for_each_const([&out](const FlagNode &flag) { + if (flag.value) { + if (!out.empty()) { + out += SEPERATOR_CHAR; + } + out += flag.name; + } + }); + return out; +} +Flags &Flags::operator=(const std::string &str) { + // Find Flags To Enable + std::unordered_set to_enable; + std::stringstream stream(str); + std::string flag_name; + while (std::getline(stream, flag_name, SEPERATOR_CHAR)) { + if (!flag_name.empty()) { + to_enable.insert(flag_name); + } + } + // Update Flags + root.for_each([&to_enable](FlagNode &flag) { + flag.value = to_enable.contains(flag.name); + }); + return *this; +} +std::unordered_map Flags::to_cache() const { + std::unordered_map out; + root.for_each_const([&out](const FlagNode &flag) { + out[flag.name] = flag.value; + }); + return out; +} +void Flags::from_cache(const std::unordered_map &cache) { + root.for_each([&cache](FlagNode &flag) { + if (cache.contains(flag.name)) { + flag.value = cache.at(flag.name); + } + }); +} +void Flags::print() const { + root.for_each_const([](const FlagNode &flag) { + std::string prefix; + for (const std::pair &it : FlagNode::flag_prefixes) { + if (it.second == flag.value) { + prefix = it.first; + break; + } + } + if (prefix.empty()) { + IMPOSSIBLE(); + } + printf("%s %s\n", prefix.c_str(), flag.name.c_str()); + fflush(stdout); + }); +} + +// Instance +EMBEDDED_RESOURCE(available_feature_flags); +Flags Flags::get() { + return Flags(std::string(available_feature_flags, available_feature_flags + available_feature_flags_len)); +} \ No newline at end of file diff --git a/launcher/src/client/flags/flags.h b/launcher/src/client/flags/flags.h new file mode 100644 index 0000000000..b07ff3e41e --- /dev/null +++ b/launcher/src/client/flags/flags.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include +#include + +// Seperator +#define SEPERATOR_CHAR '|' + +// Flag +struct FlagNode { +private: + explicit FlagNode(const std::string &name_); +public: + FlagNode(); + // Methods + void sort(); + void for_each(const std::function &callback); + void for_each_const(const std::function &callback) const; + void add_flag(std::string line); + FlagNode &add_category(const std::string &new_name); + // Properties + std::string name; + bool value; + std::vector children; + int id; + // Internal + static bool handle_line_prefix(const std::string &prefix, std::string &line); + static std::unordered_map flag_prefixes; +}; + +// All Flags +struct Flags { + explicit Flags(const std::string &data); + static Flags get(); + // To/From Strings + explicit operator std::string() const; + Flags &operator=(const std::string &str); + // To/From Cache + [[nodiscard]] std::unordered_map to_cache() const; + void from_cache(const std::unordered_map &cache); + // Print + void print() const; + // Properties + FlagNode root; +}; \ No newline at end of file diff --git a/launcher/src/client/flags/node.cpp b/launcher/src/client/flags/node.cpp new file mode 100644 index 0000000000..1407814b19 --- /dev/null +++ b/launcher/src/client/flags/node.cpp @@ -0,0 +1,87 @@ +#include + +#include + +#include "flags.h" + +// Flag +static int next_id = 1; +FlagNode::FlagNode(const std::string &name_) { + name = name_; + value = false; + id = next_id++; +} +FlagNode::FlagNode(): FlagNode("Root") {} +void FlagNode::sort() { + // Sort + std::ranges::sort(children, [](const FlagNode &a, const FlagNode &b) { + // Place Categories Before Flags + if (a.children.empty() != b.children.empty()) { + return a.children.empty() < b.children.empty(); + } else { + // Sort Alphabetically + return a.name < b.name; + } + }); + // Recurse + for (FlagNode &child : children) { + child.sort(); + } +} + +// Iteration +void FlagNode::for_each(const std::function &callback) { + for (FlagNode &child : children) { + if (child.children.empty()) { + callback(child); + } else { + child.for_each(callback); + } + } +} +void FlagNode::for_each_const(const std::function &callback) const { + const_cast(*this).for_each(callback); +} + +// Parsing +bool FlagNode::handle_line_prefix(const std::string &prefix, std::string &line) { + const std::string full_prefix = prefix + ' '; + if (line.starts_with(full_prefix)) { + line = line.substr(full_prefix.size()); + return true; + } else { + return false; + } +} +std::unordered_map FlagNode::flag_prefixes = { + {"TRUE", true}, + {"FALSE", false} +}; +void FlagNode::add_flag(std::string line) { + // Parse + bool value_set = false; + bool new_value = false; + for (const std::pair &it : flag_prefixes) { + if (handle_line_prefix(it.first, line)) { + new_value = it.second; + value_set = true; + break; + } + } + // Check + if (!value_set) { + ERR("Invalid Feature Flag Line: %s", line.c_str()); + } + if (line.rfind(SEPERATOR_CHAR, 0) != std::string::npos) { + ERR("Feature Flag Contains Invalid Character"); + } + // Create + FlagNode out(line); + out.value = new_value; + children.push_back(out); +} +FlagNode &FlagNode::add_category(const std::string &new_name) { + const FlagNode out(new_name); + children.push_back(out); + return children.back(); +} \ No newline at end of file diff --git a/launcher/src/client/ui.cpp b/launcher/src/client/ui.cpp new file mode 100644 index 0000000000..f53e5d9509 --- /dev/null +++ b/launcher/src/client/ui.cpp @@ -0,0 +1,118 @@ +#include + +#include "configuration.h" + +#include + +// Render Distances +static std::vector render_distances = { + "Far", + "Normal", + "Short", + "Tiny" +}; + +// Construct +static constexpr int size = 400; +ConfigurationUI::ConfigurationUI(State &state_): + Frame("Launcher", size, size), + state(state_) { + render_distance_index = 0; + for (std::vector::size_type i = 0; i < render_distances.size(); i++) { + if (std::string(render_distances[i]) == state.render_distance) { + render_distance_index = int(i); + break; + } + } +} + +// Render +int ConfigurationUI::render() { + const ImGuiStyle &style = ImGui::GetStyle(); + if (ImGui::BeginChild("Main", ImVec2(0, -ImGui::GetFrameHeightWithSpacing() /* Leave Room For One Line */), ImGuiChildFlags_None, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) { + // Tabs + if (ImGui::BeginTabBar("tab_bar", ImGuiTabBarFlags_None)) { + if (ImGui::BeginTabItem("Main", nullptr, ImGuiTabItemFlags_None)) { + // Main Tab + draw_main(); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Advanced", nullptr, ImGuiTabItemFlags_None)) { + // Advanced Tab + draw_advanced(); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); + } + } + ImGui::EndChild(); + // Bottom Row + const char *bottom_row_text[] = {"Quit", "Launch"}; + float width_needed = 0; + for (const char *text : bottom_row_text) { + if (width_needed > 0) { + width_needed += style.ItemSpacing.x; + } + width_needed += ImGui::CalcTextSize(text).x + style.FramePadding.x * 2.f; + } + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - width_needed); + if (ImGui::Button(bottom_row_text[0])) { + // Quit + return -1; + } + ImGui::SameLine(); + if (ImGui::Button(bottom_row_text[1])) { + // Launch + return 1; + } + // Return + return 0; +} + +// Main Tab +void ConfigurationUI::draw_main() { + const ImGuiStyle &style = ImGui::GetStyle(); + const char *labels[] = {"Username", "Render Distance"}; + // Calculate Label Size + float label_size = 0; + for (const char *label : labels) { + label_size = std::max(label_size, ImGui::CalcTextSize(label).x + style.ItemInnerSpacing.x); + } + ImGui::PushItemWidth(-label_size); + // Options + ImGui::InputText(labels[0], &state.username); + ImGui::Combo(labels[1], &render_distance_index, render_distances.data(), int(render_distances.size())); + state.render_distance = render_distances[render_distance_index]; + ImGui::PopItemWidth(); +} + +// Advanced Tab +static std::string get_label(const FlagNode &node) { + return node.name + "##" + std::to_string(node.id); +} +void ConfigurationUI::draw_advanced() const { + if (ImGui::BeginChild("Features", ImVec2(0, 0), ImGuiChildFlags_Borders, ImGuiWindowFlags_HorizontalScrollbar)) { + for (FlagNode &category : state.flags.root.children) { + std::string label = get_label(category); + if (ImGui::CollapsingHeader(label.c_str())) { + draw_category(category); + } + } + } + ImGui::EndChild(); +} + +// Feature Categories +void ConfigurationUI::draw_category(FlagNode &category) { + for (FlagNode &child : category.children) { + std::string label = get_label(child); + if (!child.children.empty()) { + if (ImGui::TreeNode(label.c_str())) { + draw_category(child); + ImGui::TreePop(); + } + } else { + ImGui::Checkbox(label.c_str(), &child.value); + } + } +} \ No newline at end of file diff --git a/launcher/src/ui/frame.cpp b/launcher/src/ui/frame.cpp index a1661d9474..469074ed61 100644 --- a/launcher/src/ui/frame.cpp +++ b/launcher/src/ui/frame.cpp @@ -1,3 +1,5 @@ +#include + #include "frame.h" #include @@ -34,7 +36,7 @@ Frame::~Frame() { int Frame::run() { int ret = 0; constexpr ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); - while (!glfwWindowShouldClose(window) || ret != 0) { + while (!glfwWindowShouldClose(window) && ret == 0) { glfwPollEvents(); // Update Style static float last_scale = -1.0f; @@ -53,7 +55,7 @@ int Frame::run() { int width, height; glfwGetFramebufferSize(window, &width, &height); ImGui::SetNextWindowSize({float(width), float(height)}); - if (ImGui::Begin("###Main", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse)) { + if (ImGui::Begin("###Frame", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoScrollWithMouse)) { ret = render(); } ImGui::End(); @@ -76,8 +78,8 @@ void Frame::setup_style(const float scale) { io.Fonts->Clear(); ImFontConfig font_cfg; font_cfg.FontDataOwnedByAtlas = false; - io.Fonts->AddFontFromMemoryTTF(Roboto_Medium_ttf, int(Roboto_Medium_ttf_len), 24.0f * scale, &font_cfg); - monospace = io.Fonts->AddFontFromMemoryTTF(Cousine_Regular_ttf, int(Cousine_Regular_ttf_len), 18.0f * scale, &font_cfg); + io.Fonts->AddFontFromMemoryTTF(Roboto_Medium_ttf, int(Roboto_Medium_ttf_len), std::floor(20.0f * scale), &font_cfg); + monospace = io.Fonts->AddFontFromMemoryTTF(Cousine_Regular_ttf, int(Cousine_Regular_ttf_len), std::floor(18.0f * scale), &font_cfg); // Style ImGuiStyle &style = ImGui::GetStyle(); style = ImGuiStyle(); diff --git a/launcher/src/ui/frame.h b/launcher/src/ui/frame.h index 66c7eb5fc8..0a60420c2b 100644 --- a/launcher/src/ui/frame.h +++ b/launcher/src/ui/frame.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include // UI Frame diff --git a/mods/src/misc/misc.cpp b/mods/src/misc/misc.cpp index b0879dd9e2..062e19dc11 100644 --- a/mods/src/misc/misc.cpp +++ b/mods/src/misc/misc.cpp @@ -35,7 +35,7 @@ static void LoginPacket_read_injection(LoginPacket_read_t original, LoginPacket const char *c_str = shared_string->c_str; // Sanitize std::string new_username = c_str; - sanitize_string(new_username, MAX_USERNAME_LENGTH, 0); + sanitize_string(new_username, MAX_USERNAME_LENGTH, false); // Set New Username rak_string->Assign(new_username.c_str()); }