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

View File

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

View File

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

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 "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<std::string, bool> 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<std::string, bool> flags_cache = state.flags.to_cache();
for (const std::pair<const std::string, bool> &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");
}

View File

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

View File

@ -1,6 +1,5 @@
#include <sstream>
#include <cstring>
#include <cerrno>
#include <sys/wait.h>
#include <sys/stat.h>
#include <vector>
@ -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 <typename T>
static void update_from_env(const char *env, T &value, const bool save) {
if (save) {
const std::string str = static_cast<std::string>(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<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);
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<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
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<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);
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<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
if (!options.no_cache) {
save_cache();
save_cache(state);
}
// Update Environment
state.update(true);
}

View File

@ -1,17 +1,38 @@
#pragma once
#include <string>
#include <functional>
#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<void(std::string)> &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);

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

View File

@ -1,6 +1,7 @@
#pragma once
#include <imgui.h>
#include <string>
#include <GLFW/glfw3.h>
// 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;
// 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());
}