New Create World Dialog

This commit is contained in:
TheBrokenRail 2022-04-27 23:38:30 -04:00
parent 186728ca5f
commit 46a53ba3cf
12 changed files with 352 additions and 86 deletions

@ -1 +1 @@
Subproject commit cdcc55e55f08956f6c5a5f3d63fce4614c75e8d4 Subproject commit 3dbcdbb34a0c92297155de48ed491ea3e587b208

View File

@ -36,3 +36,4 @@ TRUE Fix Pause Menu
TRUE Improved Title Background TRUE Improved Title Background
TRUE Force Touch GUI Button Behavior TRUE Force Touch GUI Button Behavior
TRUE Improved Button Hover Behavior TRUE Improved Button Hover Behavior
TRUE Implement Create World Dialog

View File

@ -123,6 +123,7 @@ static void run_zenity_and_set_env(const char *env_name, std::vector<std::string
} }
// Launch // Launch
#define LIST_DIALOG_SIZE "400"
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
// Pre-Bootstrap // Pre-Bootstrap
pre_bootstrap(); pre_bootstrap();
@ -165,9 +166,9 @@ int main(int argc, char *argv[]) {
command.push_back("--list"); command.push_back("--list");
command.push_back("--checklist"); command.push_back("--checklist");
command.push_back("--width"); command.push_back("--width");
command.push_back("400"); command.push_back(LIST_DIALOG_SIZE);
command.push_back("--height"); command.push_back("--height");
command.push_back("400"); command.push_back(LIST_DIALOG_SIZE);
command.push_back("--column"); command.push_back("--column");
command.push_back("Enabled"); command.push_back("Enabled");
command.push_back("--column"); command.push_back("--column");
@ -196,11 +197,11 @@ int main(int argc, char *argv[]) {
command.push_back("--list"); command.push_back("--list");
command.push_back("--radiolist"); command.push_back("--radiolist");
command.push_back("--width"); command.push_back("--width");
command.push_back("400"); command.push_back(LIST_DIALOG_SIZE);
command.push_back("--height"); command.push_back("--height");
command.push_back("400"); command.push_back(LIST_DIALOG_SIZE);
command.push_back("--text"); command.push_back("--text");
command.push_back("Minecraft Render Distance:"); command.push_back("Select Minecraft Render Distance:");
command.push_back("--column"); command.push_back("--column");
command.push_back("Selected"); command.push_back("Selected");
command.push_back("--column"); command.push_back("--column");
@ -221,7 +222,7 @@ int main(int argc, char *argv[]) {
std::vector<std::string> command; std::vector<std::string> command;
command.push_back("--entry"); command.push_back("--entry");
command.push_back("--text"); command.push_back("--text");
command.push_back("Minecraft Username:"); command.push_back("Enter Minecraft Username:");
command.push_back("--entry-text"); command.push_back("--entry-text");
command.push_back("StevePi"); command.push_back("StevePi");
// Run // Run

View File

@ -19,18 +19,23 @@ add_library(version SHARED src/version/version.cpp)
target_link_libraries(version reborn-patch symbols) target_link_libraries(version reborn-patch symbols)
add_library(chat SHARED src/chat/chat.cpp src/chat/ui.c) add_library(chat SHARED src/chat/chat.cpp src/chat/ui.c)
target_link_libraries(chat reborn-patch symbols feature pthread) target_link_libraries(chat reborn-patch symbols feature)
add_library(creative SHARED src/creative/creative.cpp) add_library(creative SHARED src/creative/creative.cpp)
target_link_libraries(creative reborn-patch symbols feature) target_link_libraries(creative reborn-patch symbols feature)
add_library(game-mode SHARED src/game-mode/game-mode.c src/game-mode/ui.cpp)
target_link_libraries(game-mode reborn-patch symbols feature)
if(MCPI_SERVER_MODE) if(MCPI_SERVER_MODE)
add_library(server SHARED src/server/server.cpp src/server/server_properties.cpp) add_library(server SHARED src/server/server.cpp src/server/server_properties.cpp)
target_link_libraries(server reborn-patch symbols feature home misc compat dl media-layer-core pthread) target_link_libraries(server reborn-patch symbols feature home misc compat dl media-layer-core pthread)
else() else()
target_link_libraries(compat input sign chat home dl) target_link_libraries(compat input sign chat home dl)
target_link_libraries(chat input) target_link_libraries(chat input media-layer-core pthread)
target_link_libraries(game-mode pthread media-layer-core)
add_library(multiplayer SHARED src/multiplayer/multiplayer.cpp) add_library(multiplayer SHARED src/multiplayer/multiplayer.cpp)
target_link_libraries(multiplayer reborn-patch symbols home feature) target_link_libraries(multiplayer reborn-patch symbols home feature)
@ -65,9 +70,6 @@ else()
endif() endif()
endif() endif()
add_library(game-mode SHARED src/game-mode/game-mode.c src/game-mode/game-mode.cpp)
target_link_libraries(game-mode reborn-patch symbols feature)
add_library(death SHARED src/death/death.cpp) add_library(death SHARED src/death/death.cpp)
target_link_libraries(death reborn-patch symbols feature) target_link_libraries(death reborn-patch symbols feature)

View File

@ -2,10 +2,15 @@
#include <cstring> #include <cstring>
#include <cstdio> #include <cstdio>
#include <vector> #include <vector>
#ifndef MCPI_SERVER_MODE
#include <pthread.h> #include <pthread.h>
#endif
#include <libreborn/libreborn.h> #include <libreborn/libreborn.h>
#include <symbols/minecraft.h> #include <symbols/minecraft.h>
#ifndef MCPI_SERVER_MODE
#include <media-layer/core.h>
#endif
#include "../init/init.h" #include "../init/init.h"
#include "../feature/feature.h" #include "../feature/feature.h"
@ -103,8 +108,8 @@ static void send_queued_messages(unsigned char *minecraft) {
// If Message Was Submitted, No Other Chat Windows Are Open, And The Game Is Not Paused, Then Re-Lock Cursor // If Message Was Submitted, No Other Chat Windows Are Open, And The Game Is Not Paused, Then Re-Lock Cursor
unsigned int new_chat_counter = chat_get_counter(); unsigned int new_chat_counter = chat_get_counter();
if (old_chat_counter > new_chat_counter && new_chat_counter == 0 && (*(unsigned char **) (minecraft + Minecraft_screen_property_offset)) == NULL) { if (old_chat_counter > new_chat_counter && new_chat_counter == 0 && (*(unsigned char **) (minecraft + Minecraft_screen_property_offset)) == NULL) {
// Grab Mouse // Unlock UI
input_set_mouse_grab_state(-1); media_set_interactable(1);
} }
old_chat_counter = new_chat_counter; old_chat_counter = new_chat_counter;
// Loop // Loop

View File

@ -5,9 +5,9 @@
#include <string.h> #include <string.h>
#include <libreborn/libreborn.h> #include <libreborn/libreborn.h>
#include <media-layer/core.h>
#include "chat.h" #include "chat.h"
#include "../input/input.h"
// Run Command // Run Command
static char *run_command_proper(const char *command[], int *return_code) { static char *run_command_proper(const char *command[], int *return_code) {
@ -21,16 +21,24 @@ static char *run_command_proper(const char *command[], int *return_code) {
// Count Chat Windows // Count Chat Windows
static pthread_mutex_t chat_counter_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t chat_counter_lock = PTHREAD_MUTEX_INITIALIZER;
static unsigned int chat_counter = 0; static volatile unsigned int chat_counter = 0;
unsigned int chat_get_counter() { unsigned int chat_get_counter() {
return chat_counter; return chat_counter;
} }
// Chat Thread // Chat Thread
#define DIALOG_TITLE "Chat"
static void *chat_thread(__attribute__((unused)) void *nop) { static void *chat_thread(__attribute__((unused)) void *nop) {
// Open // Open
int return_code; int return_code;
const char *command[] = {"zenity", "--title", "Chat", "--class", GUI_TITLE, "--entry", "--text", "Enter Chat Message:", NULL}; const char *command[] = {
"zenity",
"--title", DIALOG_TITLE,
"--class", GUI_TITLE,
"--entry",
"--text", "Enter Chat Message:",
NULL
};
char *output = run_command_proper(command, &return_code); char *output = run_command_proper(command, &return_code);
// Handle Message // Handle Message
if (output != NULL) { if (output != NULL) {
@ -62,8 +70,8 @@ static void *chat_thread(__attribute__((unused)) void *nop) {
// Create Chat Thead // Create Chat Thead
void chat_open() { void chat_open() {
if (_chat_enabled) { if (_chat_enabled) {
// Release Mouse // Lock UI
input_set_mouse_grab_state(1); media_set_interactable(0);
// Update Counter // Update Counter
pthread_mutex_lock(&chat_counter_lock); pthread_mutex_lock(&chat_counter_lock);

View File

@ -62,9 +62,12 @@ void init_game_mode() {
// Disable CreatorMode-Specific API Features (Polling Block Hits) In SurvivalMode, This Is Preferable To Crashing // Disable CreatorMode-Specific API Features (Polling Block Hits) In SurvivalMode, This Is Preferable To Crashing
overwrite_calls((void *) Minecraft_getCreator, (void *) Minecraft_getCreator_injection); overwrite_calls((void *) Minecraft_getCreator, (void *) Minecraft_getCreator_injection);
}
// Init C++ // Create World Dialog
_init_game_mode_cpp(); if (feature_has("Implement Create World Dialog", server_disabled)) {
// Init UI
_init_game_mode_ui();
} }
// Allow Joining Survival Servers // Allow Joining Survival Servers

View File

@ -1,59 +0,0 @@
#include <libreborn/libreborn.h>
#include "game-mode.h"
#include <symbols/minecraft.h>
// Get Minecraft From Screen
static unsigned char *get_minecraft_from_screen(unsigned char *screen) {
return *(unsigned char **) (screen + Screen_minecraft_property_offset);
}
// Redirect Create World Button To SimpleLevelChooseScreen
#define WORLD_NAME "world"
static void SelectWorldScreen_tick_injection(unsigned char *screen) {
bool create_world = *(bool *) (screen + SelectWorldScreen_should_create_world_property_offset);
if (create_world) {
// Get New World Name
std::string new_name = (*SelectWorldScreen_getUniqueLevelName)(screen, WORLD_NAME);
// Create SimpleLevelChooseScreen
unsigned char *new_screen = (unsigned char *) ::operator new(SIMPLE_LEVEL_CHOOSE_SCREEN_SIZE);
ALLOC_CHECK(new_screen);
(*SimpleChooseLevelScreen)(new_screen, new_name);
// Set Screen
unsigned char *minecraft = get_minecraft_from_screen(screen);
(*Minecraft_setScreen)(minecraft, new_screen);
// Finish
*(bool *) (screen + SelectWorldScreen_world_created_property_offset) = true;
} else {
(*SelectWorldScreen_tick)(screen);
}
}
static void Touch_SelectWorldScreen_tick_injection(unsigned char *screen) {
bool create_world = *(bool *) (screen + Touch_SelectWorldScreen_should_create_world_property_offset);
if (create_world) {
// Get New World Name
std::string new_name = (*Touch_SelectWorldScreen_getUniqueLevelName)(screen, WORLD_NAME);
// Create SimpleLevelChooseScreen
unsigned char *new_screen = (unsigned char *) ::operator new(SIMPLE_LEVEL_CHOOSE_SCREEN_SIZE);
ALLOC_CHECK(new_screen);
(*SimpleChooseLevelScreen)(new_screen, new_name);
// Set Screen
unsigned char *minecraft = get_minecraft_from_screen(screen);
(*Minecraft_setScreen)(minecraft, new_screen);
// Finish
*(bool *) (screen + Touch_SelectWorldScreen_world_created_property_offset) = true;
} else {
(*Touch_SelectWorldScreen_tick)(screen);
}
}
void _init_game_mode_cpp() {
// Hijack Create World Button
patch_address(SelectWorldScreen_tick_vtable_addr, (void *) SelectWorldScreen_tick_injection);
patch_address(Touch_SelectWorldScreen_tick_vtable_addr, (void *) Touch_SelectWorldScreen_tick_injection);
// Make The SimpleChooseLevelScreen Back Button Go To SelectWorldScreen Instead Of StartMenuScreen
unsigned char simple_choose_level_screen_back_button_patch[4] = {0x05, 0x10, 0xa0, 0xe3}; // "mov r1, #0x5"
patch((void *) 0x31144, simple_choose_level_screen_back_button_patch);
patch((void *) 0x3134c, simple_choose_level_screen_back_button_patch);
}

View File

@ -4,8 +4,8 @@
extern "C" { extern "C" {
#endif #endif
__attribute__((visibility("internal"))) void _init_game_mode_cpp(); __attribute__((visibility("internal"))) void _init_game_mode_ui();
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

305
mods/src/game-mode/ui.cpp Normal file
View File

@ -0,0 +1,305 @@
#ifndef MCPI_SERVER_MODE
#include <pthread.h>
#include <cstring>
#include <ctime>
#include <string>
#include <stdexcept>
#include <libreborn/libreborn.h>
#include <symbols/minecraft.h>
#include <media-layer/core.h>
#include "game-mode.h"
// Run Command
static char *run_command_proper(const char *command[], bool allow_empty) {
// Prepare Environment
RESET_ENVIRONMENTAL_VARIABLE("LD_LIBRARY_PATH");
RESET_ENVIRONMENTAL_VARIABLE("LD_PRELOAD");
// Run
int return_code;
char *output = run_command(command, &return_code);
// Handle Message
if (output != NULL) {
// Check Return Code
if (return_code == 0) {
// Remove Ending Newline
int length = strlen(output);
if (output[length - 1] == '\n') {
output[length - 1] = '\0';
}
length = strlen(output);
// Don't Allow Empty Strings
if (allow_empty || length > 0) {
// Return
return output;
}
}
// Free Output
free(output);
}
// Return
return return_code != 0 ? NULL : run_command_proper(command, allow_empty);
}
// Track Create World State
static pthread_mutex_t create_world_state_lock = PTHREAD_MUTEX_INITIALIZER;
typedef enum {
DIALOG_CLOSED,
DIALOG_OPEN,
DIALOG_SUCCESS
} create_world_state_dialog_t;
typedef struct {
volatile create_world_state_dialog_t dialog_state = DIALOG_CLOSED;
volatile char *name = NULL;
volatile int32_t game_mode = 0;
volatile int32_t seed = 0;
} create_world_state_t;
static create_world_state_t create_world_state;
// Destructor
__attribute__((destructor)) static void _free_create_world_state_name() {
free((void *) create_world_state.name);
}
// Reset State (Assume Lock)
static void reset_create_world_state() {
create_world_state.dialog_state = DIALOG_CLOSED;
if (create_world_state.name != NULL) {
free((void *) create_world_state.name);
}
create_world_state.name = NULL;
create_world_state.game_mode = 0;
create_world_state.seed = 0;
}
// Chat Thread
#define DEFAULT_WORLD_NAME "Unnamed world"
#define DIALOG_TITLE "Create World"
#define GAME_MODE_DIALOG_SIZE "200"
static void *create_world_thread(__attribute__((unused)) void *nop) {
// Run Dialogs
{
// World Name
char *world_name = NULL;
{
// Open
const char *command[] = {
"zenity",
"--title", DIALOG_TITLE,
"--class", GUI_TITLE,
"--entry",
"--text", "Enter World Name:",
"--entry-text", DEFAULT_WORLD_NAME,
NULL
};
char *output = run_command_proper(command, false);
// Handle Message
if (output != NULL) {
// Store
world_name = strdup(output);
ALLOC_CHECK(world_name);
// Free
free(output);
} else {
// Fail
goto fail;
}
}
// Game Mode
int game_mode = 0;
{
// Open
const char *command[] = {
"zenity",
"--title", DIALOG_TITLE,
"--class", GUI_TITLE,
"--list",
"--radiolist",
"--width", GAME_MODE_DIALOG_SIZE,
"--height", GAME_MODE_DIALOG_SIZE,
"--text", "Select Game Mode:",
"--column","Selected",
"--column", "Name",
"TRUE", "Creative",
"FALSE", "Survival",
NULL
};
char *output = run_command_proper(command, false);
// Handle Message
if (output != NULL) {
// Store
game_mode = strcmp(output, "Creative") == 0;
// Free
free(output);
} else {
// Fail
goto fail;
}
}
// Seed
int32_t seed = 0;
get_seed:
{
// Open
const char *command[] = {
"zenity",
"--title", DIALOG_TITLE,
"--class", GUI_TITLE,
"--entry",
"--only-numerical",
"--text", "Enter Seed (Leave Blank For Random):",
NULL
};
char *output = run_command_proper(command, true);
// Handle Message
if (output != NULL) {
// Store
bool valid = true;
try {
seed = strlen(output) == 0 ? time(NULL) : std::stoi(output);
} catch (std::invalid_argument &e) {
// Invalid Seed
WARN("Invalid Seed: %s", output);
valid = false;
} catch (std::out_of_range &e) {
// Out-Of-Range Seed
WARN("Seed Out-Of-Range: %s", output);
valid = false;
}
// Free
free(output);
// Retry If Invalid
if (!valid) {
goto get_seed;
}
} else {
// Fail
goto fail;
}
}
// Update State
pthread_mutex_lock(&create_world_state_lock);
reset_create_world_state();
create_world_state.dialog_state = DIALOG_SUCCESS;
create_world_state.name = world_name;
create_world_state.game_mode = game_mode;
create_world_state.seed = seed;
pthread_mutex_unlock(&create_world_state_lock);
// Return
return NULL;
}
fail:
// Update State
pthread_mutex_lock(&create_world_state_lock);
reset_create_world_state();
pthread_mutex_unlock(&create_world_state_lock);
// Return
return NULL;
}
// Create Chat Thead
static void open_create_world() {
// Update State (Assume Lock)
create_world_state.dialog_state = DIALOG_OPEN;
// Start Thread
pthread_t thread;
pthread_create(&thread, NULL, create_world_thread, NULL);
}
// Get Minecraft From Screen
static unsigned char *get_minecraft_from_screen(unsigned char *screen) {
return *(unsigned char **) (screen + Screen_minecraft_property_offset);
}
// Create World
static void create_world(unsigned char *host_screen, std::string world_name) {
// Get Minecraft
unsigned char *minecraft = get_minecraft_from_screen(host_screen);
// Settings
LevelSettings settings;
settings.game_type = create_world_state.game_mode;
settings.seed = create_world_state.seed;
// Create World
(*Minecraft_selectLevel)(minecraft, world_name, world_name, settings);
// Multiplayer
(*Minecraft_hostMultiplayer)(minecraft, 19132);
// Open ProgressScreen
unsigned char *screen = (unsigned char *) ::operator new(PROGRESS_SCREEN_SIZE);
ALLOC_CHECK(screen);
screen = (*ProgressScreen)(screen);
(*Minecraft_setScreen)(minecraft, screen);
// Reset
reset_create_world_state();
}
// Redirect Create World Button
#define create_SelectWorldScreen_tick_injection(prefix) \
static void prefix##SelectWorldScreen_tick_injection(unsigned char *screen) { \
/* Lock */ \
pthread_mutex_lock(&create_world_state_lock); \
\
bool *should_create_world = (bool *) (screen + prefix##SelectWorldScreen_should_create_world_property_offset); \
if (*should_create_world) { \
/* Check State */ \
if (create_world_state.dialog_state == DIALOG_CLOSED) { \
/* Open Dialog */ \
open_create_world(); \
} \
\
/* Finish */ \
*should_create_world = false; \
} else { \
/* Call Original Method */ \
(*prefix##SelectWorldScreen_tick)(screen); \
} \
\
/* Create World If Dialog Succeeded */ \
if (create_world_state.dialog_state == DIALOG_SUCCESS) { \
/* Create World Dialog Finished */ \
\
/* Get New World Name */ \
std::string name = (char *) create_world_state.name; \
std::string new_name = (*prefix##SelectWorldScreen_getUniqueLevelName)(screen, name); \
\
/* Create World */ \
create_world(screen, new_name); \
} \
\
/* Lock/Unlock UI */ \
if (create_world_state.dialog_state != DIALOG_OPEN) { \
/* Dialog Closed, Unlock UI */ \
media_set_interactable(1); \
} else { \
/* Dialog Open, Lock UI */ \
media_set_interactable(0); \
} \
\
/* Unlock */ \
pthread_mutex_unlock(&create_world_state_lock); \
}
create_SelectWorldScreen_tick_injection()
create_SelectWorldScreen_tick_injection(Touch_)
// Init
void _init_game_mode_ui() {
// Hijack Create World Button
patch_address(SelectWorldScreen_tick_vtable_addr, (void *) SelectWorldScreen_tick_injection);
patch_address(Touch_SelectWorldScreen_tick_vtable_addr, (void *) Touch_SelectWorldScreen_tick_injection);
}
#else
void _init_game_mode_ui() {
}
#endif

View File

@ -91,10 +91,10 @@ static void start_world(unsigned char *minecraft) {
} }
// Open ProgressScreen // Open ProgressScreen
void *screen = ::operator new(PROGRESS_SCREEN_SIZE); unsigned char *screen = (unsigned char *) ::operator new(PROGRESS_SCREEN_SIZE);
ALLOC_CHECK(screen); ALLOC_CHECK(screen);
screen = (*ProgressScreen)((unsigned char *) screen); screen = (*ProgressScreen)(screen);
(*Minecraft_setScreen)(minecraft, (unsigned char *) screen); (*Minecraft_setScreen)(minecraft, screen);
} }
// Check If Running In Whitelist Mode // Check If Running In Whitelist Mode

View File

@ -448,7 +448,7 @@ static void *TextEditScreen_updateEvents_vtable_addr = (void *) 0x10531c;
#define PROGRESS_SCREEN_SIZE 0x4c #define PROGRESS_SCREEN_SIZE 0x4c
typedef void *(*ProgressScreen_t)(unsigned char *obj); typedef unsigned char *(*ProgressScreen_t)(unsigned char *obj);
static ProgressScreen_t ProgressScreen = (ProgressScreen_t) 0x37044; static ProgressScreen_t ProgressScreen = (ProgressScreen_t) 0x37044;
// OptionsScreen // OptionsScreen