New Launcher UI

This commit is contained in:
TheBrokenRail 2024-11-21 02:16:25 -05:00
parent a6cc0b88b5
commit 4a91937b0a
16 changed files with 615 additions and 411 deletions

View File

@ -11,12 +11,14 @@ add_library(imgui SHARED
src/imgui_draw.cpp src/imgui_draw.cpp
src/imgui_tables.cpp src/imgui_tables.cpp
src/imgui_widgets.cpp src/imgui_widgets.cpp
src/misc/cpp/imgui_stdlib.cpp
src/backends/imgui_impl_glfw.cpp src/backends/imgui_impl_glfw.cpp
src/backends/imgui_impl_opengl2.cpp src/backends/imgui_impl_opengl2.cpp
) )
setup_header_dirs(imgui setup_header_dirs(imgui
"${CMAKE_CURRENT_SOURCE_DIR}/src" "${CMAKE_CURRENT_SOURCE_DIR}/src"
"${CMAKE_CURRENT_SOURCE_DIR}/src/backends" "${CMAKE_CURRENT_SOURCE_DIR}/src/backends"
"${CMAKE_CURRENT_SOURCE_DIR}/src/misc/cpp"
) )
find_package(OpenGL REQUIRED) find_package(OpenGL REQUIRED)
target_link_libraries(imgui PUBLIC glfw OpenGL::GL) target_link_libraries(imgui PUBLIC glfw OpenGL::GL)

View File

@ -76,8 +76,9 @@
* `Force Survival Mode Inventory UI` (Disabled By Default) * `Force Survival Mode Inventory UI` (Disabled By Default)
* `Force Survival Mode Inventory Behavior` (Disabled By Default) * `Force Survival Mode Inventory Behavior` (Disabled By Default)
* `Maximize Creative Mode Inventory Stack Size` (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 Feature Flags
* Rename `Disable 'gui_blocks' Atlas` Feature Flag To `Regenerate "gui_blocks" Atlas` * `Disable Buggy Held Item Caching` To `Fix Held Item Caching`
* `Disable 'gui_blocks' Atlas` To `Regenerate "gui_blocks" Atlas`
* Add Milk Buckets * Add Milk Buckets
* Included In The `Add Buckets` Feature Flag * Included In The `Add Buckets` Feature Flag
* Removed `Property Scale Animated Textures` Feature Flag * Removed `Property Scale Animated Textures` Feature Flag

View File

@ -16,9 +16,12 @@ add_executable(launcher
src/ui/frame.cpp src/ui/frame.cpp
src/client/configuration.cpp src/client/configuration.cpp
src/client/cache.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 target_link_libraries(launcher
reborn-util reborn-util
LIB_LIEF LIB_LIEF

View File

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

View File

@ -8,8 +8,9 @@
#include <libreborn/libreborn.h> #include <libreborn/libreborn.h>
#include "configuration.h"
#include "cache.h" #include "cache.h"
#include "configuration.h"
#include "flags/flags.h"
// Get Cache Path // Get Cache Path
static std::string 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); std::ifstream stream(get_cache_path(), std::ios::in | std::ios::binary);
if (!stream) { if (!stream) {
// Fail // Fail
struct stat s; struct stat s = {};
// No Warning If File Doesn't Exist // No Warning If File Doesn't Exist
if (stat(get_cache_path().c_str(), &s) == 0) { if (stat(get_cache_path().c_str(), &s) == 0) {
WARN("Unable To Open Launcher Cache For Loading"); WARN("Unable To Open Launcher Cache For Loading");
@ -49,14 +50,12 @@ launcher_cache load_cache() {
// Check Version // Check Version
unsigned char cache_version; unsigned char cache_version;
stream.read((char *) &cache_version, 1); stream.read((char *) &cache_version, 1);
if (stream.eof() || cache_version != (unsigned char) CACHE_VERSION) { if (stream.eof()) {
// Fail // Unable To Read Version
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"); WARN("Unable To Read Launcher Cache Version");
} } else if (cache_version != (unsigned char) CACHE_VERSION) {
stream.close(); // Invalid Version
WARN("Invalid Launcher Cache Version (Expected: %i, Actual: %i)", CACHE_VERSION, (int) cache_version);
} else { } else {
// Load Username And Render Distance // Load Username And Render Distance
launcher_cache cache; launcher_cache cache;
@ -66,16 +65,15 @@ launcher_cache load_cache() {
// Load Feature Flags // Load Feature Flags
std::string flag; std::string flag;
while (!stream.eof() && std::getline(stream, flag, '\0')) { while (!stream.eof() && std::getline(stream, flag, '\0')) {
if (flag.length() > 0) { if (!flag.empty()) {
unsigned char is_enabled = 0; bool is_enabled = false;
stream.read((char *) &is_enabled, 1); stream.read((char *) &is_enabled, sizeof(bool));
cache.feature_flags[flag] = is_enabled != (unsigned char) 0; cache.feature_flags[flag] = is_enabled;
} }
stream.peek(); stream.peek();
} }
// Finish // Finish
stream.close();
if (!stream) { if (!stream) {
// Fail // Fail
WARN("Failure While Loading Launcher Cache"); WARN("Failure While Loading Launcher Cache");
@ -85,6 +83,9 @@ launcher_cache load_cache() {
} }
} }
// Close
stream.close();
// Unlock File // Unlock File
unlock_file(get_cache_path().c_str(), lock_fd); unlock_file(get_cache_path().c_str(), lock_fd);
} }
@ -94,15 +95,10 @@ launcher_cache load_cache() {
} }
// Save // Save
#define write_env_to_stream(stream, env) \ static void write_env_to_stream(std::ofstream &stream, const std::string &value) {
{ \ stream.write(value.c_str(), int(value.size()) + 1);
const char *env_value = getenv(env); \
if (env == NULL) { \
IMPOSSIBLE(); \
} \
stream.write(env_value, strlen(env_value) + 1); \
} }
void save_cache() { void save_cache(const State &state) {
// Log // Log
DEBUG("Saving Launcher Cache..."); DEBUG("Saving Launcher Cache...");
@ -116,41 +112,23 @@ void save_cache() {
int lock_fd = lock_file(get_cache_path().c_str()); int lock_fd = lock_file(get_cache_path().c_str());
// Save Cache Version // 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); stream.write((const char *) &cache_version, 1);
// Save Username And Render Distance // Save Username And Render Distance
write_env_to_stream(stream, MCPI_USERNAME_ENV); write_env_to_stream(stream, state.username);
write_env_to_stream(stream, MCPI_RENDER_DISTANCE_ENV); write_env_to_stream(stream, state.render_distance);
// Save Feature Flags // Save Feature Flags
std::unordered_map<std::string, bool> flags; const std::unordered_map<std::string, bool> flags_cache = state.flags.to_cache();
load_available_feature_flags([&flags](std::string flag) { for (const std::pair<const std::string, bool> &it : flags_cache) {
std::string stripped_flag = strip_feature_flag_default(flag, nullptr); stream.write(it.first.c_str(), int(it.first.size()) + 1);
flags[stripped_flag] = false; stream.write((const char *) &it.second, sizeof(bool));
});
{
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);
} }
// Finish // Finish
stream.close(); stream.close();
if (!stream.good()) { if (!stream) {
WARN("Failure While Saving Launcher Cache"); WARN("Failure While Saving Launcher Cache");
} }

View File

@ -16,7 +16,8 @@ extern launcher_cache empty_cache;
launcher_cache load_cache(); launcher_cache load_cache();
// Save Cache // Save Cache
void save_cache(); struct State;
void save_cache(const State &state);
// Wipe Cache // Wipe Cache
void wipe_cache(); void wipe_cache();

View File

@ -1,6 +1,5 @@
#include <sstream> #include <sstream>
#include <cstring> #include <cstring>
#include <cerrno>
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <vector> #include <vector>
@ -12,134 +11,40 @@
#include "../util/util.h" #include "../util/util.h"
#include "configuration.h" #include "configuration.h"
#include "cache.h" #include "cache.h"
#include "../ui/frame.h"
// Strip Feature Flag Default // State
std::string strip_feature_flag_default(const std::string &flag, bool *default_ret) { State::State(const launcher_cache &cache): flags("") {
// Valid Values username = cache.username;
std::string true_str = "TRUE "; render_distance = cache.render_distance;
std::string false_str = "FALSE "; flags = Flags::get();
// Test flags.from_cache(cache.feature_flags);
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); template <typename T>
} else if (flag.rfind(false_str, 0) == 0) { static void update_from_env(const char *env, T &value, const bool save) {
// Disabled By Default if (save) {
if (default_ret != nullptr) { const std::string str = static_cast<std::string>(value);
*default_ret = false; set_and_print_env(env, str.c_str());
}
return flag.substr(false_str.length(), std::string::npos);
} else { } else {
// Invalid const char *env_value = getenv(env);
ERR("Invalid Feature Flag Default"); 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);
}
// Load Available Feature Flags
EMBEDDED_RESOURCE(available_feature_flags);
void load_available_feature_flags(const std::function<void(std::string)> &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<std::string> 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<unsigned char> *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);
}
}
}
// Use Zenity To Set Environmental Variable
#define DIALOG_TITLE "Launcher" #define DIALOG_TITLE "Launcher"
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");
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<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()] = 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<std::string()> &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 // Handle Non-Launch Commands
void handle_non_launch_client_only_commands(const options_t &options) { void handle_non_launch_client_only_commands(const options_t &options) {
// Print Available Feature Flags // Print Available Feature Flags
if (options.print_available_feature_flags) { if (options.print_available_feature_flags) {
load_available_feature_flags([](const std::string &line) { const Flags flags = Flags::get();
printf("%s\n", line.c_str()); flags.print();
fflush(stdout);
});
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
// Wipe Cache If Needed // 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 // Configure Client Options
#define LIST_DIALOG_SIZE "400"
void configure_client(const options_t &options) { void configure_client(const options_t &options) {
// Load Cache // 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; // Setup State
test->run(); State state(cache);
delete test; state.update(false);
// --default // --default
if (options.use_default) { if (!options.use_default) {
// Use Default Feature Flags // Show UI
set_env_if_unset(MCPI_FEATURE_FLAGS_ENV, [&cache]() { ConfigurationUI *ui = new ConfigurationUI(state);
std::string feature_flags; const int ret = ui->run();
load_available_feature_flags([&feature_flags, &cache](const std::string &flag) { delete ui;
bool value; if (ret <= 0) {
// Strip Default Value exit(EXIT_SUCCESS);
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<std::string> 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<std::string> 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);
}
// Run
run_zenity_and_set_env(MCPI_RENDER_DISTANCE_ENV, command);
}
// Setup MCPI_USERNAME
{
std::vector<std::string> 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 // Save Cache
if (!options.no_cache) { if (!options.no_cache) {
save_cache(); save_cache(state);
} }
// Update Environment
state.update(true);
} }

View File

@ -1,17 +1,38 @@
#pragma once #pragma once
#include <string> #include <string>
#include <functional>
#include "../options/parser.h" #include "../options/parser.h"
#include "cache.h"
#include "flags/flags.h"
#include "../ui/frame.h"
// Defaults // Default Configuration
#define DEFAULT_USERNAME "StevePi" #define DEFAULT_USERNAME "StevePi"
#define DEFAULT_RENDER_DISTANCE "Short" #define DEFAULT_RENDER_DISTANCE "Short"
// Feature Flags // State
std::string strip_feature_flag_default(const std::string& flag, bool *default_ret); struct State {
void load_available_feature_flags(const std::function<void(std::string)> &callback); 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 // Handle Non-Launch Commands
void handle_non_launch_client_only_commands(const options_t &options); void handle_non_launch_client_only_commands(const options_t &options);

View File

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

View File

@ -0,0 +1,117 @@
#include <sstream>
#include <algorithm>
#include <unordered_set>
#include <libreborn/libreborn.h>
#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<std::string> 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<std::string, bool> Flags::to_cache() const {
std::unordered_map<std::string, bool> out;
root.for_each_const([&out](const FlagNode &flag) {
out[flag.name] = flag.value;
});
return out;
}
void Flags::from_cache(const std::unordered_map<std::string, bool> &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<const std::string, bool> &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));
}

View File

@ -0,0 +1,47 @@
#pragma once
#include <string>
#include <vector>
#include <unordered_map>
#include <functional>
// 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<void(FlagNode &)> &callback);
void for_each_const(const std::function<void(const FlagNode &)> &callback) const;
void add_flag(std::string line);
FlagNode &add_category(const std::string &new_name);
// Properties
std::string name;
bool value;
std::vector<FlagNode> children;
int id;
// Internal
static bool handle_line_prefix(const std::string &prefix, std::string &line);
static std::unordered_map<std::string, bool> 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<std::string, bool> to_cache() const;
void from_cache(const std::unordered_map<std::string, bool> &cache);
// Print
void print() const;
// Properties
FlagNode root;
};

View File

@ -0,0 +1,87 @@
#include <algorithm>
#include <libreborn/libreborn.h>
#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<void(FlagNode &)> &callback) {
for (FlagNode &child : children) {
if (child.children.empty()) {
callback(child);
} else {
child.for_each(callback);
}
}
}
void FlagNode::for_each_const(const std::function<void(const FlagNode &)> &callback) const {
const_cast<FlagNode &>(*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<std::string, bool> 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<const std::string, bool> &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();
}

118
launcher/src/client/ui.cpp Normal file
View File

@ -0,0 +1,118 @@
#include <vector>
#include "configuration.h"
#include <imgui_stdlib.h>
// 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<std::string>::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);
}
}
}

View File

@ -1,3 +1,5 @@
#include <cmath>
#include "frame.h" #include "frame.h"
#include <imgui_impl_glfw.h> #include <imgui_impl_glfw.h>
@ -34,7 +36,7 @@ Frame::~Frame() {
int Frame::run() { int Frame::run() {
int ret = 0; int ret = 0;
constexpr ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); constexpr ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
while (!glfwWindowShouldClose(window) || ret != 0) { while (!glfwWindowShouldClose(window) && ret == 0) {
glfwPollEvents(); glfwPollEvents();
// Update Style // Update Style
static float last_scale = -1.0f; static float last_scale = -1.0f;
@ -53,7 +55,7 @@ int Frame::run() {
int width, height; int width, height;
glfwGetFramebufferSize(window, &width, &height); glfwGetFramebufferSize(window, &width, &height);
ImGui::SetNextWindowSize({float(width), float(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(); ret = render();
} }
ImGui::End(); ImGui::End();
@ -76,8 +78,8 @@ void Frame::setup_style(const float scale) {
io.Fonts->Clear(); io.Fonts->Clear();
ImFontConfig font_cfg; ImFontConfig font_cfg;
font_cfg.FontDataOwnedByAtlas = false; font_cfg.FontDataOwnedByAtlas = false;
io.Fonts->AddFontFromMemoryTTF(Roboto_Medium_ttf, int(Roboto_Medium_ttf_len), 24.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), 18.0f * scale, &font_cfg); monospace = io.Fonts->AddFontFromMemoryTTF(Cousine_Regular_ttf, int(Cousine_Regular_ttf_len), std::floor(18.0f * scale), &font_cfg);
// Style // Style
ImGuiStyle &style = ImGui::GetStyle(); ImGuiStyle &style = ImGui::GetStyle();
style = ImGuiStyle(); style = ImGuiStyle();

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <imgui.h> #include <imgui.h>
#include <string>
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
// UI Frame // UI Frame

View File

@ -35,7 +35,7 @@ static void LoginPacket_read_injection(LoginPacket_read_t original, LoginPacket
const char *c_str = shared_string->c_str; const char *c_str = shared_string->c_str;
// Sanitize // Sanitize
std::string new_username = c_str; 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 // Set New Username
rak_string->Assign(new_username.c_str()); rak_string->Assign(new_username.c_str());
} }