From 472f5d67a5c31f7d120ef5a7c2727ae922770f28 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Sat, 13 Nov 2021 23:29:48 -0500 Subject: [PATCH] 2.2.8 --- VERSION | 2 +- docs/CHANGELOG.md | 10 ++ docs/COMMAND_LINE.md | 5 + images/start.png | Bin 31460 -> 31462 bytes .../available-feature-flags | 2 + media-layer/core/src/base.cpp | 5 + media-layer/core/src/media.c | 167 +++++++++++------- media-layer/include/GLES/gl.h | 2 + media-layer/include/media-layer/core.h | 4 + media-layer/proxy/src/media-layer-core.c | 29 +++ media-layer/proxy/src/server/server.cpp | 74 +++----- mods/CMakeLists.txt | 21 ++- mods/src/benchmark/README.md | 2 + mods/src/benchmark/benchmark.cpp | 147 +++++++++++++++ mods/src/creative/creative.cpp | 34 ++++ mods/src/creative/creative.h | 11 ++ mods/src/init/init.c | 8 +- mods/src/init/init.h | 5 +- mods/src/input/drop.cpp | 3 +- mods/src/input/misc.c | 11 +- mods/src/misc/logging.cpp | 108 +++++++++++ mods/src/misc/misc.c | 61 ++++--- mods/src/misc/misc.cpp | 45 ++--- mods/src/misc/misc.h | 6 + mods/src/server/server.cpp | 80 ++------- mods/src/sound/sound.cpp | 2 +- mods/src/version/version.cpp | 3 + symbols/include/symbols/minecraft.h | 10 +- 28 files changed, 617 insertions(+), 240 deletions(-) create mode 100644 mods/src/benchmark/README.md create mode 100644 mods/src/benchmark/benchmark.cpp create mode 100644 mods/src/creative/creative.h create mode 100644 mods/src/misc/logging.cpp diff --git a/VERSION b/VERSION index 5bc1cc43d..23a63f524 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.2.7 +2.2.8 diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index f43fb192e..f07477608 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +**2.2.8** +* Add "Hide Chat Messages" Optional Feature Flag +* Add "Remove Creative Restrictions" Optional Feature Flag +* Improve GLFW->SDL Mouse Motion Event Conversion +* Performance Optimizations +* Make Majority Of Server-Specific Logging Code Also Apply To The Client +* Simple Benchmark Mode +* Fix Typo When Audio Source File Doesn't Exist +* Improve Build System + **2.2.7** * Fix Crash When OpenAL Is Unavailable * Fix Command Input In Server diff --git a/docs/COMMAND_LINE.md b/docs/COMMAND_LINE.md index 358c065da..9e078f9c5 100644 --- a/docs/COMMAND_LINE.md +++ b/docs/COMMAND_LINE.md @@ -9,3 +9,8 @@ FALSE This Flag Is Off By Default ## ``--only-generate`` (Server Mode Only) If you run MCPI-Reborn with ``--only-generate``, it will immediately exit once world generation has completed. This is mainly used for automatically testing MCPI-Reborn. + +## ``--benchmark`` (Client Mode Only) +If you run MCPI-Reborn with ``--benchmark``, it will enter a simple benchmark mode. This means automatically loading a newly generated world, then rotating the camera for a period of time. When it has finished, it will then exit and print the average FPS while the world was loaded. In this mode, all user input is blocked. However you can still modify rendering settings by changing feature flags. + +The world used will always be re-created on start and uses a hard-coded seed. diff --git a/images/start.png b/images/start.png index 7201171320d188cdba2a0d91fe15e42877d2444e..0292d38cf7d18a5efea97d5a76c8c65e071682ff 100644 GIT binary patch delta 12450 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 diff --git a/launcher/client-data/opt/minecraft-pi-reborn-client/available-feature-flags b/launcher/client-data/opt/minecraft-pi-reborn-client/available-feature-flags index c961bf6dd..6495726c5 100644 --- a/launcher/client-data/opt/minecraft-pi-reborn-client/available-feature-flags +++ b/launcher/client-data/opt/minecraft-pi-reborn-client/available-feature-flags @@ -8,6 +8,7 @@ TRUE Display Nametags By Default TRUE Fix Sign Placement TRUE Show Block Outlines FALSE Expand Creative Inventory +FALSE Remove Creative Restrictions FALSE Peaceful Mode TRUE Animated Water TRUE Remove Invalid Item Background @@ -16,6 +17,7 @@ TRUE Smooth Lighting FALSE 3D Anaglyph TRUE Fix Camera Rendering TRUE Implement Chat +FALSE Hide Chat Messages TRUE Implement Death Messages TRUE Implement Game-Mode Switching TRUE Allow Joining Survival Servers diff --git a/media-layer/core/src/base.cpp b/media-layer/core/src/base.cpp index 491d57242..f8d972b2a 100644 --- a/media-layer/core/src/base.cpp +++ b/media-layer/core/src/base.cpp @@ -5,6 +5,7 @@ #include #include +#include // SDL Is Replaced With GLFW @@ -41,3 +42,7 @@ int SDL_PushEvent(SDL_Event *event) { pthread_mutex_unlock(&queue_mutex); return 1; } + +void media_ensure_loaded() { + // NOP +} diff --git a/media-layer/core/src/media.c b/media-layer/core/src/media.c index d09fa32b2..e892bdb69 100644 --- a/media-layer/core/src/media.c +++ b/media-layer/core/src/media.c @@ -16,6 +16,14 @@ #include "audio/engine.h" #endif // #ifndef MCPI_HEADLESS_MODE +// Allow Disabling Interaction +static void update_cursor(); +static int is_interactable = 1; +void media_set_interactable(int toggle) { + is_interactable = toggle; + update_cursor(); +} + // GLFW Code Not Needed In Headless Mode #ifndef MCPI_HEADLESS_MODE @@ -139,63 +147,75 @@ static SDLMod glfw_modifier_to_sdl_modifier(int mods) { // Pass Key Presses To SDL static void glfw_key(__attribute__((unused)) GLFWwindow *window, int key, int scancode, int action, __attribute__((unused)) int mods) { - SDL_Event event; - int up = action == GLFW_RELEASE; - event.type = up ? SDL_KEYUP : SDL_KEYDOWN; - event.key.state = up ? SDL_RELEASED : SDL_PRESSED; - event.key.keysym.scancode = scancode; - event.key.keysym.mod = glfw_modifier_to_sdl_modifier(mods); - event.key.keysym.sym = glfw_key_to_sdl_key(key); - SDL_PushEvent(&event); - if (key == GLFW_KEY_BACKSPACE && !up) { - character_event((char) '\b'); + if (is_interactable) { + SDL_Event event; + int up = action == GLFW_RELEASE; + event.type = up ? SDL_KEYUP : SDL_KEYDOWN; + event.key.state = up ? SDL_RELEASED : SDL_PRESSED; + event.key.keysym.scancode = scancode; + event.key.keysym.mod = glfw_modifier_to_sdl_modifier(mods); + event.key.keysym.sym = glfw_key_to_sdl_key(key); + SDL_PushEvent(&event); + if (key == GLFW_KEY_BACKSPACE && !up) { + character_event((char) '\b'); + } } } // Pass Text To Minecraft static void glfw_char(__attribute__((unused)) GLFWwindow *window, unsigned int codepoint) { - character_event((char) codepoint); + if (is_interactable) { + character_event((char) codepoint); + } } +// Last Mouse Location static double last_mouse_x = 0; static double last_mouse_y = 0; -static int ignore_relative_mouse = 1; +// Ignore Relative Cursor Motion +static int ignore_relative_motion = 0; // Pass Mouse Movement To SDL static void glfw_motion(__attribute__((unused)) GLFWwindow *window, double xpos, double ypos) { - SDL_Event event; - event.type = SDL_MOUSEMOTION; - event.motion.x = xpos; - event.motion.y = ypos; - event.motion.xrel = !ignore_relative_mouse ? (xpos - last_mouse_x) : 0; - event.motion.yrel = !ignore_relative_mouse ? (ypos - last_mouse_y) : 0; - ignore_relative_mouse = 0; + if (is_interactable) { + SDL_Event event; + event.type = SDL_MOUSEMOTION; + event.motion.x = xpos; + event.motion.y = ypos; + event.motion.xrel = !ignore_relative_motion ? (xpos - last_mouse_x) : 0; + event.motion.yrel = !ignore_relative_motion ? (ypos - last_mouse_y) : 0; + SDL_PushEvent(&event); + } + ignore_relative_motion = 0; last_mouse_x = xpos; last_mouse_y = ypos; - SDL_PushEvent(&event); } // Create And Push SDL Mouse Click Event static void click_event(int button, int up) { - SDL_Event event; - event.type = up ? SDL_MOUSEBUTTONUP : SDL_MOUSEBUTTONDOWN; - event.button.x = last_mouse_x; - event.button.y = last_mouse_y; - event.button.state = up ? SDL_RELEASED : SDL_PRESSED; - event.button.button = button; - SDL_PushEvent(&event); + if (is_interactable) { + SDL_Event event; + event.type = up ? SDL_MOUSEBUTTONUP : SDL_MOUSEBUTTONDOWN; + event.button.x = last_mouse_x; + event.button.y = last_mouse_y; + event.button.state = up ? SDL_RELEASED : SDL_PRESSED; + event.button.button = button; + SDL_PushEvent(&event); + } } // Pass Mouse Click To SDL static void glfw_click(__attribute__((unused)) GLFWwindow *window, int button, int action, __attribute__((unused)) int mods) { - int up = action == GLFW_RELEASE; - int sdl_button = button == GLFW_MOUSE_BUTTON_RIGHT ? SDL_BUTTON_RIGHT : (button == GLFW_MOUSE_BUTTON_LEFT ? SDL_BUTTON_LEFT : SDL_BUTTON_MIDDLE); - click_event(sdl_button, up); + if (is_interactable) { + int up = action == GLFW_RELEASE; + int sdl_button = button == GLFW_MOUSE_BUTTON_RIGHT ? SDL_BUTTON_RIGHT : (button == GLFW_MOUSE_BUTTON_LEFT ? SDL_BUTTON_LEFT : SDL_BUTTON_MIDDLE); + click_event(sdl_button, up); + } } // Pass Mouse Scroll To SDL static void glfw_scroll(__attribute__((unused)) GLFWwindow *window, __attribute__((unused)) double xoffset, double yoffset) { - if (yoffset != 0) { + if (is_interactable && yoffset != 0) { int sdl_button = yoffset > 0 ? SDL_BUTTON_WHEELUP : SDL_BUTTON_WHEELDOWN; click_event(sdl_button, 0); click_event(sdl_button, 1); @@ -207,6 +227,17 @@ static void glfw_scroll(__attribute__((unused)) GLFWwindow *window, __attribute_ // Track Media Layer State static int is_running = 0; +// Disable V-Sync +static int disable_vsync = 0; +void media_disable_vsync() { + disable_vsync = 1; +#ifndef MCPI_HEADLESS_MODE + if (is_running) { + glfwSwapInterval(0); + } +#endif // #ifndef MCPI_HEADLESS_MODE +} + // Init Media Layer void SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *icon) { // Don't Enable GLFW In Headless Mode @@ -250,6 +281,12 @@ void SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *ic // Set State is_running = 1; + + // Update State + update_cursor(); + if (disable_vsync) { + media_disable_vsync(); + } } void media_swap_buffers() { @@ -341,37 +378,53 @@ static int cursor_grabbed = 0; static int cursor_visible = 1; // Update GLFW Cursor State (Client Only) +static void update_cursor() { #ifndef MCPI_HEADLESS_MODE -static void update_glfw_cursor() { - // Store Old Mode - int old_mode = glfwGetInputMode(glfw_window, GLFW_CURSOR); + if (is_running) { + // Get New State + int new_cursor_visible = is_interactable ? cursor_visible : 1; + int new_cursor_grabbed = is_interactable ? cursor_grabbed : 0; - // Handle Cursor Visibility - int new_mode; - if (!cursor_visible) { - if (cursor_grabbed) { - new_mode = GLFW_CURSOR_DISABLED; + // Store Old Mode + int old_mode = glfwGetInputMode(glfw_window, GLFW_CURSOR); + + // Handle Cursor Visibility + int new_mode; + if (!new_cursor_visible) { + if (new_cursor_grabbed) { + new_mode = GLFW_CURSOR_DISABLED; + } else { + new_mode = GLFW_CURSOR_HIDDEN; + } } else { - new_mode = GLFW_CURSOR_HIDDEN; + new_mode = GLFW_CURSOR_NORMAL; } - } else { - new_mode = GLFW_CURSOR_NORMAL; - } - if (new_mode != old_mode) { - // Set New Mode - glfwSetInputMode(glfw_window, GLFW_CURSOR, new_mode); + if (new_mode != old_mode) { + // Ignore Relative Cursor Motion When Locking + if (new_mode == GLFW_CURSOR_DISABLED && old_mode != GLFW_CURSOR_DISABLED) { + ignore_relative_motion = 1; + } - // Handle Cursor Lock/Unlock - if ((new_mode == GLFW_CURSOR_DISABLED && old_mode != GLFW_CURSOR_DISABLED) || (new_mode != GLFW_CURSOR_DISABLED && old_mode == GLFW_CURSOR_DISABLED)) { - // Use Raw Mouse Motion - glfwSetInputMode(glfw_window, GLFW_RAW_MOUSE_MOTION, new_mode == GLFW_CURSOR_DISABLED ? GLFW_TRUE : GLFW_FALSE); + // Set New Mode + glfwSetInputMode(glfw_window, GLFW_CURSOR, new_mode); - // Reset Last Mouse Position - ignore_relative_mouse = 1; + // Handle Cursor Lock/Unlock + if ((new_mode == GLFW_CURSOR_DISABLED && old_mode != GLFW_CURSOR_DISABLED) || (new_mode != GLFW_CURSOR_DISABLED && old_mode == GLFW_CURSOR_DISABLED)) { + // Use Raw Mouse Motion + glfwSetInputMode(glfw_window, GLFW_RAW_MOUSE_MOTION, new_mode == GLFW_CURSOR_DISABLED ? GLFW_TRUE : GLFW_FALSE); + } + + // Reset Mouse Position When Unlocking + if (new_mode != GLFW_CURSOR_DISABLED && old_mode == GLFW_CURSOR_DISABLED) { + double cursor_x; + double cursor_y; + glfwGetCursorPos(glfw_window, &cursor_x, &cursor_y); + glfw_motion(glfw_window, cursor_x, cursor_y); + } } } +#endif // #ifndef MCPI_HEADLESS_MODE } -#endif // Fix SDL Cursor Visibility/Grabbing SDL_GrabMode SDL_WM_GrabInput(SDL_GrabMode mode) { @@ -386,9 +439,7 @@ SDL_GrabMode SDL_WM_GrabInput(SDL_GrabMode mode) { cursor_grabbed = 0; } // Update Cursor GLFW State (Client Only) -#ifndef MCPI_HEADLESS_MODE - update_glfw_cursor(); -#endif + update_cursor(); // Return return mode; } @@ -406,9 +457,7 @@ int SDL_ShowCursor(int toggle) { cursor_visible = 0; } // Update Cursor GLFW State (Client Only) -#ifndef MCPI_HEADLESS_MODE - update_glfw_cursor(); -#endif + update_cursor(); // Return return toggle; } diff --git a/media-layer/include/GLES/gl.h b/media-layer/include/GLES/gl.h index 935917b21..77a0df146 100644 --- a/media-layer/include/GLES/gl.h +++ b/media-layer/include/GLES/gl.h @@ -17,6 +17,8 @@ extern "C" { #define GL_DEPTH_TEST 0xb71 #define GL_PACK_ALIGNMENT 0xd05 #define GL_UNPACK_ALIGNMENT 0xcf5 +#define GL_SRC_ALPHA 0x302 +#define GL_ONE_MINUS_SRC_ALPHA 0x303 #include #include diff --git a/media-layer/include/media-layer/core.h b/media-layer/include/media-layer/core.h index 724cecd39..1eddc5e57 100644 --- a/media-layer/include/media-layer/core.h +++ b/media-layer/include/media-layer/core.h @@ -8,11 +8,15 @@ extern "C" { #define DEFAULT_WIDTH 840 #define DEFAULT_HEIGHT 480 +void media_ensure_loaded(); + void media_take_screenshot(char *home); void media_toggle_fullscreen(); void media_swap_buffers(); void media_cleanup(); void media_get_framebuffer_size(int *width, int *height); +void media_set_interactable(int is_interactable); +void media_disable_vsync(); #ifdef __cplusplus } diff --git a/media-layer/proxy/src/media-layer-core.c b/media-layer/proxy/src/media-layer-core.c index a7fffae02..b01d356e3 100644 --- a/media-layer/proxy/src/media-layer-core.c +++ b/media-layer/proxy/src/media-layer-core.c @@ -307,3 +307,32 @@ CALL(60, media_audio_play, void, (const char *source, const char *name, float x, free(name); #endif } + +CALL(62, media_set_interactable, void, (int is_interactable)) { +#if defined(MEDIA_LAYER_PROXY_SERVER) + // Lock Proxy + start_proxy_call(); + + // Arguments + write_int(is_interactable); + + // Release Proxy + end_proxy_call(); +#else + int is_interactable = read_int(); + // Run + media_set_interactable(is_interactable); +#endif +} + +CALL(63, media_disable_vsync, void, ()) { +#if defined(MEDIA_LAYER_PROXY_SERVER) + // Lock Proxy + start_proxy_call(); + // Release Proxy + end_proxy_call(); +#else + // Run + media_disable_vsync(); +#endif +} diff --git a/media-layer/proxy/src/server/server.cpp b/media-layer/proxy/src/server/server.cpp index a0a1b380f..12c91e559 100644 --- a/media-layer/proxy/src/server/server.cpp +++ b/media-layer/proxy/src/server/server.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include "../common/common.h" // Track Client State @@ -85,58 +87,36 @@ static void start_media_layer_proxy_client(int read, int write) { update_client_state(1, 0); } -// Maximize Pipe Buffer Size -static void maximize_pipe_fd_size(int fd) { - // Read Maximum Pipe Size - std::ifstream max_size_file("/proc/sys/fs/pipe-max-size"); - if (!max_size_file.good()) { - PROXY_ERR("%s", "Unable To Open Maximum Pipe Size File"); - } - // Read One Line - int max_size; - std::string line; - if (std::getline(max_size_file, line) && line.size() > 0) { - max_size = std::stoi(line); - } else { - PROXY_ERR("%s", "Unable To Read Maximum Pipe Size File"); - } - // Set Maximum Pipe Size - errno = 0; - if (fcntl(fd, F_SETPIPE_SZ, max_size) < max_size) { - PROXY_ERR("Unable To Set Maximum Pipe Size: %s", errno != 0 ? strerror(errno) : "Unknown Error"); - } -} -static void maximize_pipe_size(int pipe[2]) { - maximize_pipe_fd_size(pipe[0]); - maximize_pipe_fd_size(pipe[1]); -} - // Start Server -__attribute__((constructor)) static void init_media_layer_proxy_server() { - PROXY_INFO("%s", "Starting..."); +static int loaded = 0; +__attribute__((constructor)) void media_ensure_loaded() { + if (!loaded) { + loaded = 1; - // Create Connection - int server_to_client_pipe[2]; - safe_pipe2(server_to_client_pipe, 0); - maximize_pipe_size(server_to_client_pipe); - int client_to_server_pipe[2]; - safe_pipe2(client_to_server_pipe, 0); - maximize_pipe_size(client_to_server_pipe); - // Set Connection - set_connection(client_to_server_pipe[0], server_to_client_pipe[1]); + // Log + PROXY_INFO("%s", "Starting..."); - // Start Client - start_media_layer_proxy_client(server_to_client_pipe[0], client_to_server_pipe[1]); + // Create Connection + int server_to_client_pipe[2]; + safe_pipe2(server_to_client_pipe, 0); + int client_to_server_pipe[2]; + safe_pipe2(client_to_server_pipe, 0); + // Set Connection + set_connection(client_to_server_pipe[0], server_to_client_pipe[1]); - // Wait For Connection Message - char *str = read_string(); - if (strcmp(str, CONNECTED_MSG) == 0) { - PROXY_INFO("%s", "Connected"); - } else { - PROXY_ERR("%s", "Unable To Connect"); + // Start Client + start_media_layer_proxy_client(server_to_client_pipe[0], client_to_server_pipe[1]); + + // Wait For Connection Message + char *str = read_string(); + if (strcmp(str, CONNECTED_MSG) == 0) { + PROXY_INFO("%s", "Connected"); + } else { + PROXY_ERR("%s", "Unable To Connect"); + } + // Free + free(str); } - // Free - free(str); } // Assign Unique ID To Function diff --git a/mods/CMakeLists.txt b/mods/CMakeLists.txt index d6e16b06a..8231239d5 100644 --- a/mods/CMakeLists.txt +++ b/mods/CMakeLists.txt @@ -23,7 +23,7 @@ target_link_libraries(chat reborn symbols feature pthread) if(MCPI_SERVER_MODE) add_library(server SHARED src/server/server.cpp src/server/server_properties.cpp) - target_link_libraries(server reborn symbols feature home compat version dl media-layer-core pthread) + target_link_libraries(server reborn symbols feature home misc compat dl media-layer-core pthread) else() target_link_libraries(compat input sign chat home dl) @@ -39,7 +39,7 @@ else() target_link_libraries(camera reborn symbols media-layer-core feature home) add_library(input SHARED src/input/input.cpp src/input/bow.c src/input/attack.c src/input/toggle.c src/input/misc.c src/input/drop.cpp) - target_link_libraries(input reborn symbols feature media-layer-core) + target_link_libraries(input reborn symbols creative feature media-layer-core) add_library(sign SHARED src/sign/sign.cpp) target_link_libraries(sign reborn symbols feature input) @@ -58,6 +58,11 @@ else() add_library(atlas SHARED src/atlas/atlas.cpp) target_link_libraries(atlas reborn symbols feature GLESv1_CM) + + if(NOT MCPI_HEADLESS_MODE) + add_library(benchmark SHARED src/benchmark/benchmark.cpp) + target_link_libraries(benchmark reborn symbols compat misc media-layer-core) + endif() endif() add_library(game-mode SHARED src/game-mode/game-mode.c src/game-mode/game-mode.cpp) @@ -66,8 +71,8 @@ target_link_libraries(game-mode reborn symbols feature) add_library(death SHARED src/death/death.cpp) target_link_libraries(death reborn symbols feature) -add_library(misc SHARED src/misc/misc.c src/misc/misc.cpp) -target_link_libraries(misc reborn symbols feature) +add_library(misc SHARED src/misc/misc.c src/misc/misc.cpp src/misc/logging.cpp) +target_link_libraries(misc reborn symbols feature GLESv1_CM) add_library(options SHARED src/options/options.c) target_link_libraries(options reborn symbols feature) @@ -79,11 +84,14 @@ add_library(test SHARED src/test/test.c) target_link_libraries(test reborn home) add_library(init SHARED src/init/init.c) -target_link_libraries(init compat game-mode misc death options chat home version test) +target_link_libraries(init compat game-mode misc death options chat home version test media-layer-core) if(MCPI_SERVER_MODE) target_link_libraries(init server) else() target_link_libraries(init multiplayer sound camera input sign creative touch textures atlas) + if(NOT MCPI_HEADLESS_MODE) + target_link_libraries(init benchmark) + endif() endif() ## Install Mods @@ -92,4 +100,7 @@ if(MCPI_SERVER_MODE) install(TARGETS server DESTINATION "${MCPI_INSTALL_DIR}/mods") else() install(TARGETS multiplayer sound override camera input sign creative touch textures atlas DESTINATION "${MCPI_INSTALL_DIR}/mods") + if(NOT MCPI_HEADLESS_MODE) + install(TARGETS benchmark DESTINATION "${MCPI_INSTALL_DIR}/mods") + endif() endif() diff --git a/mods/src/benchmark/README.md b/mods/src/benchmark/README.md new file mode 100644 index 000000000..f4a67123c --- /dev/null +++ b/mods/src/benchmark/README.md @@ -0,0 +1,2 @@ +# ``benchmark`` Mod +This mod contain a simple game benchmark. diff --git a/mods/src/benchmark/benchmark.cpp b/mods/src/benchmark/benchmark.cpp new file mode 100644 index 000000000..30433785a --- /dev/null +++ b/mods/src/benchmark/benchmark.cpp @@ -0,0 +1,147 @@ +#include + +#include +#include + +#include +#include + +#include "../init/init.h" +#include "../compat/compat.h" +#include "../misc/misc.h" + +// --benchmark: Activate Benchmark +static bool active = false; +__attribute__((constructor)) static void _init_active(int argc, char *argv[]) { + // Iterate Arguments + for (int i = 1; i < argc; i++) { + // Check Argument + if (strcmp(argv[i], "--benchmark") == 0) { + // Enabled + active = true; + break; + } + } +} + +// Constants +#define NANOSECONDS_IN_SECOND 1000000000ll + +// Config +#define BENCHMARK_GAME_MODE 1 // Creative Mode +#define BENCHMARK_SEED 2048 // Random Number +#define BENCHMARK_WORLD_NAME "_Benchmark" // Random Number +#define BENCHMARK_LENGTH (180ll * NANOSECONDS_IN_SECOND) // 3 Minutes +#define BENCHMARK_ROTATION_INTERVAL ((long long int) (0.02f * NANOSECONDS_IN_SECOND)) +#define BENCHMARK_ROTATION_AMOUNT 10 + +// Create/Start World +static void start_world(unsigned char *minecraft) { + // Log + INFO("%s", "Loading Benchmark"); + + // Specify Level Settings + LevelSettings settings; + settings.game_type = BENCHMARK_GAME_MODE; + settings.seed = BENCHMARK_SEED; + + // Delete World If It Already Exists + unsigned char *level_source = (*Minecraft_getLevelSource)(minecraft); + unsigned char *level_source_vtable = *(unsigned char **) level_source; + ExternalFileLevelStorageSource_deleteLevel_t ExternalFileLevelStorageSource_deleteLevel = *(ExternalFileLevelStorageSource_deleteLevel_t *) (level_source_vtable + ExternalFileLevelStorageSource_deleteLevel_vtable_offset); + (*ExternalFileLevelStorageSource_deleteLevel)(level_source, BENCHMARK_WORLD_NAME); + + // Select Level + (*Minecraft_selectLevel)(minecraft, BENCHMARK_WORLD_NAME, BENCHMARK_WORLD_NAME, settings); + + // Open ProgressScreen + void *screen = ::operator new(PROGRESS_SCREEN_SIZE); + ALLOC_CHECK(screen); + screen = (*ProgressScreen)((unsigned char *) screen); + (*Minecraft_setScreen)(minecraft, (unsigned char *) screen); +} + +// Track Frames +static unsigned long long int frames = 0; +HOOK(media_swap_buffers, void, ()) { + ensure_media_swap_buffers(); + (*real_media_swap_buffers)(); + frames++; +} + +// Get Time +static long long int get_time() { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC_RAW, &ts); + long long int a = (long long int) ts.tv_nsec; + long long int b = ((long long int) ts.tv_sec) * NANOSECONDS_IN_SECOND; + return a + b; +} + +// Store Time When World Loaded +static int world_loaded = 0; +static long long int world_loaded_time; +static unsigned long long int world_loaded_frames; + +// Runs Every Tick +static bool loaded = false; +static bool exit_requested = false; +static void Minecraft_update_injection(unsigned char *minecraft) { + // Create/Start World + if (!loaded) { + start_world(minecraft); + loaded = true; + } + + // Detect World Loaded + if (!world_loaded && (*Minecraft_isLevelGenerated)(minecraft)) { + world_loaded = 1; + world_loaded_time = get_time(); + world_loaded_frames = frames; + } + + // Run Benchmark + if (!exit_requested && world_loaded) { + // Get Time + long long int current_time = get_time() - world_loaded_time; + unsigned long long int current_frames = frames - world_loaded_frames; + + // Rotate Player + static long long int rotate_point = BENCHMARK_ROTATION_INTERVAL; + if (current_time >= rotate_point) { + SDL_Event event; + event.type = SDL_MOUSEMOTION; + event.motion.x = 0; + event.motion.y = 0; + event.motion.xrel = BENCHMARK_ROTATION_AMOUNT; + event.motion.yrel = 0; + SDL_PushEvent(&event); + // Reset Rotation Timer + rotate_point += BENCHMARK_ROTATION_INTERVAL; + } + + // Check If Benchmark Is Over + if (current_time >= BENCHMARK_LENGTH) { + // Request Exit + compat_request_exit(); + // Disable Special Behavior After Requesting Exit + exit_requested = true; + + // Calculate FPS + static double frames_per_nanosecond = ((double) current_frames) / ((double) current_time); + static double frames_per_second = frames_per_nanosecond * NANOSECONDS_IN_SECOND; + INFO("Benchmark Completed After %llu Frames In %lld Nanoseconds, Average FPS: %f", current_frames, current_time, frames_per_second); + } + } +} + +// Init Benchmark +void init_benchmark() { + if (active) { + misc_run_on_update(Minecraft_update_injection); + // Disable Interaction + media_set_interactable(0); + // Disable V-Sync + media_disable_vsync(); + } +} diff --git a/mods/src/creative/creative.cpp b/mods/src/creative/creative.cpp index 998e592ba..89b9d90d1 100644 --- a/mods/src/creative/creative.cpp +++ b/mods/src/creative/creative.cpp @@ -3,6 +3,7 @@ #include "../init/init.h" #include "../feature/feature.h" +#include "creative.h" // Add Item To Inventory static void inventory_add_item(unsigned char *inventory, unsigned char *item, bool is_tile) { @@ -51,10 +52,43 @@ static int32_t Inventory_setupDefault_FillingContainer_addItem_call_injection(un return ret; } +// Check Restriction Status +static int is_restricted = 1; +int creative_is_restricted() { + return is_restricted; +} + // Init void init_creative() { // Add Extra Items To Creative Inventory (Only Replace Specific Function Call) if (feature_has("Expand Creative Inventory", 0)) { overwrite_call((void *) 0x8e0fc, (void *) Inventory_setupDefault_FillingContainer_addItem_call_injection); } + + // Remove Creative Restrictions (Opening Chests, Crafting, Etc) + if (feature_has("Remove Creative Restrictions", 0)) { + unsigned char nop_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop" + patch((void *) 0x1e3f4, nop_patch); + unsigned char slot_count_patch[4] = {0x18, 0x00, 0x00, 0xea}; // "b 0x27110" + patch((void *) 0x270a8, slot_count_patch); + patch((void *) 0x341c0, nop_patch); + patch((void *) 0x3adb4, nop_patch); + patch((void *) 0x3b374, nop_patch); + patch((void *) 0x43ee8, nop_patch); + patch((void *) 0x43f3c, nop_patch); + patch((void *) 0x43fd0, nop_patch); + patch((void *) 0x43fd8, nop_patch); + patch((void *) 0x8d080, nop_patch); + patch((void *) 0x8d090, nop_patch); + patch((void *) 0x91d48, nop_patch); + patch((void *) 0x92098, nop_patch); + unsigned char FillingContainer_removeResource_creative_check_patch[4] = {0x03, 0x00, 0x53, 0xe1}; // "cmp r3, r3" + patch((void *) 0x923c0, FillingContainer_removeResource_creative_check_patch); + patch((void *) 0x92828, nop_patch); + // Maximize Creative Inventory Stack Size + unsigned char maximize_stack_patch[4] = {0xff, 0xc0, 0xa0, 0xe3}; // "mov r12, 0xff" + patch((void *) 0x8e104, maximize_stack_patch); + // Disable Other Restrictions + is_restricted = 0; + } } diff --git a/mods/src/creative/creative.h b/mods/src/creative/creative.h new file mode 100644 index 000000000..84a353e25 --- /dev/null +++ b/mods/src/creative/creative.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +int creative_is_restricted(); + +#ifdef __cplusplus +} +#endif diff --git a/mods/src/init/init.c b/mods/src/init/init.c index a3117c331..c1a0adbeb 100644 --- a/mods/src/init/init.c +++ b/mods/src/init/init.c @@ -1,7 +1,11 @@ #include "init.h" +#include + __attribute__((constructor)) static void init() { + media_ensure_loaded(); run_tests(); + init_version(); init_compat(); #ifdef MCPI_SERVER_MODE init_server(); @@ -22,5 +26,7 @@ __attribute__((constructor)) static void init() { init_options(); init_chat(); init_home(); - init_version(); +#if !defined(MCPI_SERVER_MODE) && !defined(MCPI_HEADLESS_MODE) + init_benchmark(); +#endif } diff --git a/mods/src/init/init.h b/mods/src/init/init.h index 226e14396..db001036b 100644 --- a/mods/src/init/init.h +++ b/mods/src/init/init.h @@ -5,6 +5,7 @@ extern "C" { #endif void run_tests(); +void init_version(); void init_compat(); #ifdef MCPI_SERVER_MODE void init_server(); @@ -25,7 +26,9 @@ void init_death(); void init_options(); void init_chat(); void init_home(); -void init_version(); +#if !defined(MCPI_SERVER_MODE) && !defined(MCPI_HEADLESS_MODE) +void init_benchmark(); +#endif #ifdef __cplusplus } diff --git a/mods/src/input/drop.cpp b/mods/src/input/drop.cpp index ef051b34b..ee680c4c9 100644 --- a/mods/src/input/drop.cpp +++ b/mods/src/input/drop.cpp @@ -3,6 +3,7 @@ #include "input.h" #include "../feature/feature.h" +#include "../creative/creative.h" // Enable Item Dropping static int enable_drop = 0; @@ -22,7 +23,7 @@ void input_drop(int drop_slot) { // Handle Drop Item Presses static void _handle_drop(unsigned char *minecraft) { - if (!(*Minecraft_isCreativeMode)(minecraft) && (drop_item_presses > 0 || drop_slot_pressed)) { + if ((!creative_is_restricted() || !(*Minecraft_isCreativeMode)(minecraft)) && (drop_item_presses > 0 || drop_slot_pressed)) { // Get Player unsigned char *player = *(unsigned char **) (minecraft + Minecraft_player_property_offset); if (player != NULL) { diff --git a/mods/src/input/misc.c b/mods/src/input/misc.c index 18cfa9c2b..92fc79134 100644 --- a/mods/src/input/misc.c +++ b/mods/src/input/misc.c @@ -3,6 +3,7 @@ #include "input.h" #include "../feature/feature.h" +#include "../creative/creative.h" // Enable Miscellaneous Input Fixes static int enable_misc = 0; @@ -59,9 +60,9 @@ static void _handle_mouse_grab(unsigned char *minecraft) { // Block UI Interaction When Mouse Is Locked static bool Gui_tickItemDrop_Minecraft_isCreativeMode_call_injection(unsigned char *minecraft) { - if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_OFF) { + if (!enable_misc || SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_OFF) { // Call Original Method - return (*Minecraft_isCreativeMode)(minecraft); + return creative_is_restricted() && (*Minecraft_isCreativeMode)(minecraft); } else { // Disable Item Drop Ticking return 1; @@ -82,12 +83,12 @@ void _init_misc() { if (enable_misc) { // Fix OptionsScreen Ignoring The Back Button patch_address(OptionsScreen_handleBackEvent_vtable_addr, (void *) OptionsScreen_handleBackEvent_injection); - - // Disable Item Dropping Using The Cursor When Cursor Is Hidden - overwrite_call((void *) 0x27800, (void *) Gui_tickItemDrop_Minecraft_isCreativeMode_call_injection); // Disable Opening Inventory Using The Cursor When Cursor Is Hidden overwrite_calls((void *) Gui_handleClick, (void *) Gui_handleClick_injection); } + // Disable Item Dropping Using The Cursor When Cursor Is Hidden + overwrite_call((void *) 0x27800, (void *) Gui_tickItemDrop_Minecraft_isCreativeMode_call_injection); + input_run_on_tick(_handle_back); input_run_on_tick(_handle_mouse_grab); } diff --git a/mods/src/misc/logging.cpp b/mods/src/misc/logging.cpp new file mode 100644 index 000000000..4acd8f1f7 --- /dev/null +++ b/mods/src/misc/logging.cpp @@ -0,0 +1,108 @@ +#include + +#include +#include + +#include "misc.h" + +// Print Chat To Log +static bool Gui_addMessage_recursing = false; +static void Gui_addMessage_injection(unsigned char *gui, std::string const& text) { + // Sanitize Message + char *new_message = strdup(text.c_str()); + ALLOC_CHECK(new_message); + sanitize_string(&new_message, -1, 1); + + // Process Message + if (!Gui_addMessage_recursing) { + // Start Recursing + Gui_addMessage_recursing = true; + + // Print Log Message + fprintf(stderr, "[CHAT]: %s\n", new_message); + + // Call Original Method + (*Gui_addMessage)(gui, std::string(new_message)); + + // End Recursing + Gui_addMessage_recursing = false; + } else { + // Call Original Method + (*Gui_addMessage)(gui, std::string(new_message)); + } + + // Free + free(new_message); +} + +// Check If Two Percentages Are Different Enough To Be Logged +#define SIGNIFICANT_PROGRESS 5 +static bool is_progress_difference_significant(int32_t new_val, int32_t old_val) { + if (new_val != old_val) { + if (new_val == -1 || old_val == -1) { + return true; + } else if (new_val == 0 || new_val == 100) { + return true; + } else { + return new_val - old_val >= SIGNIFICANT_PROGRESS; + } + } else { + return false; + } +} + +// Print Progress Reports +static int last_progress = -1; +static const char *last_message = NULL; +static void print_progress(unsigned char *minecraft) { + const char *message = (*Minecraft_getProgressMessage)(minecraft); + int32_t progress = *(int32_t *) (minecraft + Minecraft_progress_property_offset); + if ((*Minecraft_isLevelGenerated)(minecraft)) { + message = "Ready"; + progress = -1; + } + if (message != NULL) { + bool message_different = message != last_message; + bool progress_significant = is_progress_difference_significant(progress, last_progress); + if (message_different || progress_significant) { + if (progress != -1) { + INFO("Status: %s: %i%%", message, progress); + } else { + INFO("Status: %s", message); + } + if (message_different) { + last_message = message; + } + if (progress_significant) { + last_progress = progress; + } + } + } +} + +// Print Progress Reports Regularly +static void Minecraft_update_injection(unsigned char *minecraft) { + // Print Progress Reports + print_progress(minecraft); +} + +// Log When Game Is Saved +void Level_saveLevelData_injection(unsigned char *level) { + // Print Log Message + INFO("%s", "Saving Game"); + + // Call Original Method + (*Level_saveLevelData)(level); +} + +// Init +void _init_misc_logging() { + // Print Chat To Log + overwrite_calls((void *) Gui_addMessage, (void *) Gui_addMessage_injection); + + // Print Progress Reports + misc_run_on_update(Minecraft_update_injection); + + // Print Log On Game Save + overwrite_calls((void *) Level_saveLevelData, (void *) Level_saveLevelData_injection); +} diff --git a/mods/src/misc/misc.c b/mods/src/misc/misc.c index 3a936cc15..6660737f7 100644 --- a/mods/src/misc/misc.c +++ b/mods/src/misc/misc.c @@ -2,6 +2,8 @@ #include #include +#include + #include #include @@ -12,36 +14,52 @@ // Maximum Username Length #define MAX_USERNAME_LENGTH 16 -// Render Selected Item Text -static void Gui_renderChatMessages_injection(unsigned char *gui, int32_t param_1, uint32_t param_2, uint32_t param_3, unsigned char *font) { +// Additional GUI Rendering +static int hide_chat_messages = 0; +static int render_selected_item_text = 0; +static void Gui_renderChatMessages_injection(unsigned char *gui, int32_t y_offset, uint32_t max_messages, bool disable_fading, unsigned char *font) { // Call Original Method - (*Gui_renderChatMessages)(gui, param_1, param_2, param_3, font); - // Calculate Selected Item Text Scale - unsigned char *minecraft = *(unsigned char **) (gui + Gui_minecraft_property_offset); - int32_t screen_width = *(int32_t *) (minecraft + Minecraft_screen_width_property_offset); - float scale = ((float) screen_width) * *InvGuiScale; + if (!hide_chat_messages) { + (*Gui_renderChatMessages)(gui, y_offset, max_messages, disable_fading, font); + } + // Render Selected Item Text - (*Gui_renderOnSelectItemNameText)(gui, (int32_t) scale, font, param_1 - 0x13); + if (render_selected_item_text) { + // Fix GL Mode + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + // Calculate Selected Item Text Scale + unsigned char *minecraft = *(unsigned char **) (gui + Gui_minecraft_property_offset); + int32_t screen_width = *(int32_t *) (minecraft + Minecraft_screen_width_property_offset); + float scale = ((float) screen_width) * *InvGuiScale; + // Render Selected Item Text + (*Gui_renderOnSelectItemNameText)(gui, (int32_t) scale, font, y_offset - 0x13); + } } // Reset Selected Item Text Timer On Slot Select static uint32_t reset_selected_item_text_timer = 0; static void Gui_tick_injection(unsigned char *gui) { // Call Original Method (*Gui_tick)(gui); + // Handle Reset - float *selected_item_text_timer = (float *) (gui + Gui_selected_item_text_timer_property_offset); - if (reset_selected_item_text_timer) { - // Reset - *selected_item_text_timer = 0; - reset_selected_item_text_timer = 0; + if (render_selected_item_text) { + float *selected_item_text_timer = (float *) (gui + Gui_selected_item_text_timer_property_offset); + if (reset_selected_item_text_timer) { + // Reset + *selected_item_text_timer = 0; + reset_selected_item_text_timer = 0; + } } } // Trigger Reset Selected Item Text Timer On Slot Select static void Inventory_selectSlot_injection(unsigned char *inventory, int32_t slot) { // Call Original Method (*Inventory_selectSlot)(inventory, slot); + // Trigger Reset Selected Item Text Timer - reset_selected_item_text_timer = 1; + if (render_selected_item_text) { + reset_selected_item_text_timer = 1; + } } // Sanitize Username @@ -125,12 +143,12 @@ void init_misc() { patch((void *) 0x63c98, invalid_item_background_patch); } - // Fix Selected Item Text - if (feature_has("Render Selected Item Text", 0)) { - overwrite_calls((void *) Gui_renderChatMessages, (void *) Gui_renderChatMessages_injection); - overwrite_calls((void *) Gui_tick, (void *) Gui_tick_injection); - overwrite_calls((void *) Inventory_selectSlot, (void *) Inventory_selectSlot_injection); - } + // Render Selected Item Text + Hide Chat Messages + hide_chat_messages = feature_has("Hide Chat Messages", 0); + render_selected_item_text = feature_has("Render Selected Item Text", 0); + overwrite_calls((void *) Gui_renderChatMessages, (void *) Gui_renderChatMessages_injection); + overwrite_calls((void *) Gui_tick, (void *) Gui_tick_injection); + overwrite_calls((void *) Inventory_selectSlot, (void *) Inventory_selectSlot_injection); // Sanitize Username patch_address(LoginPacket_read_vtable_addr, (void *) LoginPacket_read_injection); @@ -144,6 +162,7 @@ void init_misc() { // Fix Bug Where RakNetInstance Starts Pinging Potential Servers Before The "Join Game" Screen Is Opened overwrite_calls((void *) RakNetInstance, (void *) RakNetInstance_injection); - // Init C++ + // Init C++ And Logging _init_misc_cpp(); + _init_misc_logging(); } diff --git a/mods/src/misc/misc.cpp b/mods/src/misc/misc.cpp index 7291a0731..00313348a 100644 --- a/mods/src/misc/misc.cpp +++ b/mods/src/misc/misc.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include @@ -24,34 +25,24 @@ static AppPlatform_readAssetFile_return_value AppPlatform_readAssetFile_injectio return ret; } -// Print Chat To Log -static bool Gui_addMessage_recursing = false; -static void Gui_addMessage_injection(unsigned char *gui, std::string const& text) { - // Sanitize Message - char *new_message = strdup(text.c_str()); - ALLOC_CHECK(new_message); - sanitize_string(&new_message, -1, 1); +// Run Functions On Input Tick +static std::vector &get_misc_update_functions() { + static std::vector functions; + return functions; +} +void misc_run_on_update(misc_update_function_t function) { + get_misc_update_functions().push_back(function); +} - // Process Message - if (!Gui_addMessage_recursing) { - // Start Recursing - Gui_addMessage_recursing = true; +// Handle Custom Update Behavior +static void Minecraft_update_injection(unsigned char *minecraft) { + // Call Original Method + (*Minecraft_update)(minecraft); - // Print Log Message - fprintf(stderr, "[CHAT]: %s\n", new_message); - - // Call Original Method - (*Gui_addMessage)(gui, std::string(new_message)); - - // End Recursing - Gui_addMessage_recursing = false; - } else { - // Call Original Method - (*Gui_addMessage)(gui, std::string(new_message)); + // Run Input Tick Functions + for (misc_update_function_t function : get_misc_update_functions()) { + (*function)(minecraft); } - - // Free - free(new_message); } // Init @@ -61,6 +52,6 @@ void _init_misc_cpp() { overwrite((void *) AppPlatform_readAssetFile, (void *) AppPlatform_readAssetFile_injection); } - // Print Chat To Log - overwrite_calls((void *) Gui_addMessage, (void *) Gui_addMessage_injection); + // Handle Custom Update Behavior + overwrite_calls((void *) Minecraft_update, (void *) Minecraft_update_injection); } diff --git a/mods/src/misc/misc.h b/mods/src/misc/misc.h index a2d89bfd9..4df9eb9dc 100644 --- a/mods/src/misc/misc.h +++ b/mods/src/misc/misc.h @@ -4,7 +4,13 @@ extern "C" { #endif +typedef void (*misc_update_function_t)(unsigned char *minecraft); +void misc_run_on_update(misc_update_function_t function); + +void Level_saveLevelData_injection(unsigned char *level); + __attribute__((visibility("internal"))) void _init_misc_cpp(); +__attribute__((visibility("internal"))) void _init_misc_logging(); #ifdef __cplusplus } diff --git a/mods/src/server/server.cpp b/mods/src/server/server.cpp index ba898f956..f9eb8114c 100644 --- a/mods/src/server/server.cpp +++ b/mods/src/server/server.cpp @@ -16,6 +16,7 @@ #include #include +#include #include "server_properties.h" @@ -23,9 +24,7 @@ #include "../init/init.h" #include "../home/home.h" #include "../compat/compat.h" -#include "../version/version.h" - -#include +#include "../misc/misc.h" // --only-generate: Ony Generate World And Then Exit static bool only_generate = false; @@ -66,17 +65,20 @@ static std::string get_world_name() { // Create/Start World static void start_world(unsigned char *minecraft) { - INFO("Starting Minecraft: Pi Edition: Dedicated Server (%s)", version_get()); + // Get World Name + std::string world_name = get_world_name(); + + // Log + INFO("Loading World: %s", world_name.c_str()); // Specify Level Settings LevelSettings settings; - settings.game_type = get_server_properties().get_int("game-mode", DEFAULT_GAME_MODE);; + settings.game_type = get_server_properties().get_int("game-mode", DEFAULT_GAME_MODE); std::string seed_str = get_server_properties().get_string("seed", DEFAULT_SEED); int32_t seed = seed_str.length() > 0 ? std::stoi(seed_str) : time(NULL); settings.seed = seed; // Select Level - std::string world_name = get_world_name(); (*Minecraft_selectLevel)(minecraft, world_name, world_name, settings); // Don't Open Port When Using --only-generate @@ -94,51 +96,6 @@ static void start_world(unsigned char *minecraft) { (*Minecraft_setScreen)(minecraft, (unsigned char *) screen); } -// Check If Two Percentages Are Different Enough To Be Logged -#define SIGNIFICANT_PROGRESS 5 -static bool is_progress_difference_significant(int32_t new_val, int32_t old_val) { - if (new_val != old_val) { - if (new_val == -1 || old_val == -1) { - return true; - } else if (new_val == 0 || new_val == 100) { - return true; - } else { - return new_val - old_val >= SIGNIFICANT_PROGRESS; - } - } else { - return false; - } -} - -// Print Progress Reports -static int last_progress = -1; -static const char *last_message = NULL; -static void print_progress(unsigned char *minecraft) { - const char *message = (*Minecraft_getProgressMessage)(minecraft); - int32_t progress = *(int32_t *) (minecraft + Minecraft_progress_property_offset); - if ((*Minecraft_isLevelGenerated)(minecraft)) { - message = "Ready"; - progress = -1; - } - if (message != NULL) { - bool message_different = message != last_message; - bool progress_significant = is_progress_difference_significant(progress, last_progress); - if (message_different || progress_significant) { - if (progress != -1) { - INFO("Status: %s: %i%%", message, progress); - } else { - INFO("Status: %s", message); - } - if (message_different) { - last_message = message; - } - if (progress_significant) { - last_progress = progress; - } - } - } -} - // Check If Running In Whitelist Mode static bool is_whitelist() { return get_server_properties().get_bool("whitelist", DEFAULT_WHITELIST); @@ -244,15 +201,6 @@ static void list_callback(unsigned char *minecraft, std::string username, unsign INFO(" - %s (%s)", username.c_str(), get_player_ip(minecraft, player)); } -// Log When Game Is Saved -static void Level_saveLevelData_injection(unsigned char *level) { - // Print Log Message - INFO("%s", "Saving Game"); - - // Call Original Method - (*Level_saveLevelData)(level); -} - // Handle Server Stop static void handle_server_stop(unsigned char *minecraft) { if (compat_check_exit_requested()) { @@ -384,12 +332,6 @@ static void Minecraft_update_injection(unsigned char *minecraft) { only_generate = false; } - // Print Progress Reports - print_progress(minecraft); - - // Call Original Method - (*Minecraft_update)(minecraft); - // Handle Commands handle_commands(minecraft); @@ -494,9 +436,9 @@ static void server_init() { // Open Properties File std::string file(home_get()); file.append("/server.properties"); - std::ifstream properties_file(file); + // Check Properties File if (!properties_file.good()) { // Write Defaults std::ofstream properties_file_output(file); @@ -551,9 +493,7 @@ static void server_init() { unsigned char player_patch[4] = {0x00, 0x20, 0xa0, 0xe3}; // "mov r2, #0x0" patch((void *) 0x1685c, player_patch); // Start World On Launch - overwrite_calls((void *) Minecraft_update, (void *) Minecraft_update_injection); - // Print Log On Game Save - overwrite_calls((void *) Level_saveLevelData, (void *) Level_saveLevelData_injection); + misc_run_on_update(Minecraft_update_injection); // Set Max Players unsigned char max_players_patch[4] = {get_max_players(), 0x30, 0xa0, 0xe3}; // "mov r3, #MAX_PLAYERS" patch((void *) 0x166d0, max_players_patch); diff --git a/mods/src/sound/sound.cpp b/mods/src/sound/sound.cpp index 1f63122bd..4f845ef19 100644 --- a/mods/src/sound/sound.cpp +++ b/mods/src/sound/sound.cpp @@ -42,7 +42,7 @@ std::string _sound_get_source_file() { // Check If Sound Exists if (access(full_path, F_OK) == -1) { // Fail - WARN("%s", "Audio Source File Doesn't Exists: " SOURCE_FILE_BASE); + WARN("%s", "Audio Source File Doesn't Exist: " SOURCE_FILE_BASE); source.assign(""); } else { // Set diff --git a/mods/src/version/version.cpp b/mods/src/version/version.cpp index 4ed14e37c..ca310a1d2 100644 --- a/mods/src/version/version.cpp +++ b/mods/src/version/version.cpp @@ -31,4 +31,7 @@ void init_version() { overwrite((void *) Common_getGameVersionString, (void *) Common_getGameVersionString_injection); // Normal GUI patch_address((void *) minecraft_pi_version, version_get()); + + // Log + INFO("Starting Minecraft: Pi Edition (%s)", version_get()); } diff --git a/symbols/include/symbols/minecraft.h b/symbols/include/symbols/minecraft.h index 8673ffb50..a74080515 100644 --- a/symbols/include/symbols/minecraft.h +++ b/symbols/include/symbols/minecraft.h @@ -160,6 +160,9 @@ static uint32_t Minecraft_handleBack_vtable_offset = 0x34; typedef unsigned char *(*Minecraft_getCreator_t)(unsigned char *minecraft); static Minecraft_getCreator_t Minecraft_getCreator = (Minecraft_getCreator_t) 0x17538; +typedef unsigned char *(*Minecraft_getLevelSource_t)(unsigned char *minecraft); +static Minecraft_getLevelSource_t Minecraft_getLevelSource = (Minecraft_getLevelSource_t) 0x16e84; + static uint32_t Minecraft_screen_width_property_offset = 0x20; // int32_t static uint32_t Minecraft_network_handler_property_offset = 0x174; // NetEventCallback * static uint32_t Minecraft_rak_net_instance_property_offset = 0x170; // RakNetInstance * @@ -327,7 +330,7 @@ static Gui_handleClick_t Gui_handleClick = (Gui_handleClick_t) 0x2599c; typedef void (*Gui_renderOnSelectItemNameText_t)(unsigned char *gui, int32_t param_1, unsigned char *font, int32_t param_2); static Gui_renderOnSelectItemNameText_t Gui_renderOnSelectItemNameText = (Gui_renderOnSelectItemNameText_t) 0x26aec; -typedef void (*Gui_renderChatMessages_t)(unsigned char *gui, int32_t param_1, uint32_t param_2, bool param_3, unsigned char *font); +typedef void (*Gui_renderChatMessages_t)(unsigned char *gui, int32_t y_offset, uint32_t max_messages, bool disable_fading, unsigned char *font); static Gui_renderChatMessages_t Gui_renderChatMessages = (Gui_renderChatMessages_t) 0x273d8; static uint32_t Gui_minecraft_property_offset = 0x9f4; // Minecraft * @@ -631,6 +634,11 @@ static AppPlatform_readAssetFile_t AppPlatform_readAssetFile = (AppPlatform_read typedef void (*Minecraft_selectLevel_t)(unsigned char *minecraft, std::string const& level_dir, std::string const& level_name, LevelSettings const& vsettings); static Minecraft_selectLevel_t Minecraft_selectLevel = (Minecraft_selectLevel_t) 0x16f38; +// ExternalFileLevelStorageSource + +typedef void (*ExternalFileLevelStorageSource_deleteLevel_t)(unsigned char *external_file_level_storage_source, std::string const& level_name); +static uint32_t ExternalFileLevelStorageSource_deleteLevel_vtable_offset = 0x20; + // CommandServer typedef std::string (*CommandServer_parse_t)(unsigned char *command_server, struct ConnectedClient &client, std::string const& command);