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 Force Touch GUI Button 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
#define LIST_DIALOG_SIZE "400"
int main(int argc, char *argv[]) {
// Pre-Bootstrap
pre_bootstrap();
@ -165,9 +166,9 @@ int main(int argc, char *argv[]) {
command.push_back("--list");
command.push_back("--checklist");
command.push_back("--width");
command.push_back("400");
command.push_back(LIST_DIALOG_SIZE);
command.push_back("--height");
command.push_back("400");
command.push_back(LIST_DIALOG_SIZE);
command.push_back("--column");
command.push_back("Enabled");
command.push_back("--column");
@ -196,11 +197,11 @@ int main(int argc, char *argv[]) {
command.push_back("--list");
command.push_back("--radiolist");
command.push_back("--width");
command.push_back("400");
command.push_back(LIST_DIALOG_SIZE);
command.push_back("--height");
command.push_back("400");
command.push_back(LIST_DIALOG_SIZE);
command.push_back("--text");
command.push_back("Minecraft Render Distance:");
command.push_back("Select Minecraft Render Distance:");
command.push_back("--column");
command.push_back("Selected");
command.push_back("--column");
@ -221,7 +222,7 @@ int main(int argc, char *argv[]) {
std::vector<std::string> command;
command.push_back("--entry");
command.push_back("--text");
command.push_back("Minecraft Username:");
command.push_back("Enter Minecraft Username:");
command.push_back("--entry-text");
command.push_back("StevePi");
// Run

View File

@ -19,18 +19,23 @@ add_library(version SHARED src/version/version.cpp)
target_link_libraries(version reborn-patch symbols)
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)
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)
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)
else()
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)
target_link_libraries(multiplayer reborn-patch symbols home feature)
@ -65,9 +70,6 @@ else()
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)
target_link_libraries(death reborn-patch symbols feature)

View File

@ -2,10 +2,15 @@
#include <cstring>
#include <cstdio>
#include <vector>
#ifndef MCPI_SERVER_MODE
#include <pthread.h>
#endif
#include <libreborn/libreborn.h>
#include <symbols/minecraft.h>
#ifndef MCPI_SERVER_MODE
#include <media-layer/core.h>
#endif
#include "../init/init.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
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) {
// Grab Mouse
input_set_mouse_grab_state(-1);
// Unlock UI
media_set_interactable(1);
}
old_chat_counter = new_chat_counter;
// Loop

View File

@ -5,9 +5,9 @@
#include <string.h>
#include <libreborn/libreborn.h>
#include <media-layer/core.h>
#include "chat.h"
#include "../input/input.h"
// Run Command
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
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() {
return chat_counter;
}
// Chat Thread
#define DIALOG_TITLE "Chat"
static void *chat_thread(__attribute__((unused)) void *nop) {
// Open
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);
// Handle Message
if (output != NULL) {
@ -62,8 +70,8 @@ static void *chat_thread(__attribute__((unused)) void *nop) {
// Create Chat Thead
void chat_open() {
if (_chat_enabled) {
// Release Mouse
input_set_mouse_grab_state(1);
// Lock UI
media_set_interactable(0);
// Update Counter
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
overwrite_calls((void *) Minecraft_getCreator, (void *) Minecraft_getCreator_injection);
}
// Init C++
_init_game_mode_cpp();
// Create World Dialog
if (feature_has("Implement Create World Dialog", server_disabled)) {
// Init UI
_init_game_mode_ui();
}
// 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" {
#endif
__attribute__((visibility("internal"))) void _init_game_mode_cpp();
__attribute__((visibility("internal"))) void _init_game_mode_ui();
#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
void *screen = ::operator new(PROGRESS_SCREEN_SIZE);
unsigned char *screen = (unsigned char *) ::operator new(PROGRESS_SCREEN_SIZE);
ALLOC_CHECK(screen);
screen = (*ProgressScreen)((unsigned char *) screen);
(*Minecraft_setScreen)(minecraft, (unsigned char *) screen);
screen = (*ProgressScreen)(screen);
(*Minecraft_setScreen)(minecraft, screen);
}
// 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
typedef void *(*ProgressScreen_t)(unsigned char *obj);
typedef unsigned char *(*ProgressScreen_t)(unsigned char *obj);
static ProgressScreen_t ProgressScreen = (ProgressScreen_t) 0x37044;
// OptionsScreen