Compare commits

..

7 Commits

Author SHA1 Message Date
Bigjango13 cfce26b3ba More chunk symbols 2024-04-16 10:19:39 -07:00
Bigjango13 7566890c7c Add ChunkSource vtable 2024-04-08 15:54:20 -07:00
Bigjango13 d7c6771cdd Fix HOOKing mods 2024-04-07 20:29:23 -07:00
Bigjango13 e03caceb6d Merge branch 'master' of https://gitea.thebrokenrail.com/minecraft-pi-reborn/minecraft-pi-reborn 2024-04-07 20:04:41 -07:00
Bigjango13 2401fa3a6f Fix Tile_lightEmission 2024-04-06 22:16:31 -07:00
Bigjango13 0feef96eca Add TileRenderer_tesselateTorch 2024-04-04 13:20:25 -07:00
Bigjango13 d9c25f22ad More symbols, remove Entity_moveTo 2024-04-02 15:03:38 -07:00
115 changed files with 3774 additions and 2872 deletions

View File

@ -120,7 +120,7 @@ if(BUILD_NATIVE_COMPONENTS)
endif()
# Install Prebuilt ARMHF Toolchain Sysroot
if(BUILD_NATIVE_COMPONENTS AND MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN)
if(BUILD_ARM_COMPONENTS AND MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN)
install_arm_sysroot()
endif()
@ -152,13 +152,13 @@ if(BUILD_NATIVE_COMPONENTS)
set(ARM_OPTIONS "${MCPI_OPTIONS}")
list(APPEND ARM_OPTIONS "-DMCPI_BUILD_MODE:STRING=arm")
list(APPEND ARM_OPTIONS "-DCMAKE_INSTALL_MESSAGE:STRING=NEVER")
list(APPEND ARM_OPTIONS "-DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>")
list(APPEND ARM_OPTIONS "-DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>/install")
if(NOT MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN)
if(DEFINED CMAKE_TOOLCHAIN_FILE)
list(APPEND ARM_OPTIONS "-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${CMAKE_TOOLCHAIN_FILE}")
endif()
else()
list(APPEND ARM_OPTIONS "-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${MCPI_CMAKE_TOOLCHAIN_FILE}")
list(APPEND ARM_OPTIONS "-DMCPI_USE_PREBUILT_ARMHF_TOOLCHAIN:BOOL=TRUE")
endif()
list(APPEND ARM_OPTIONS "-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}")
# Build
@ -166,9 +166,7 @@ if(BUILD_NATIVE_COMPONENTS)
DOWNLOAD_COMMAND ""
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}"
CMAKE_CACHE_ARGS ${ARM_OPTIONS}
INSTALL_COMMAND
"${CMAKE_COMMAND}" "-E"
"rm" "-rf" "<INSTALL_DIR>/${MCPI_INSTALL_DIR}"
INSTALL_COMMAND "${CMAKE_COMMAND}" "-E" "rm" "-rf" "<INSTALL_DIR>/install"
COMMAND
"${CMAKE_COMMAND}" "-E" "env"
"DESTDIR="
@ -180,5 +178,5 @@ if(BUILD_NATIVE_COMPONENTS)
)
# Install
ExternalProject_Get_Property(arm-components INSTALL_DIR)
install(DIRECTORY "${INSTALL_DIR}/${MCPI_INSTALL_DIR}/" DESTINATION "${MCPI_INSTALL_DIR}")
install(DIRECTORY "${INSTALL_DIR}/install/${MCPI_INSTALL_DIR}/" DESTINATION "${MCPI_INSTALL_DIR}")
endif()

View File

@ -19,3 +19,11 @@ function(mcpi_option name description type default)
list(APPEND MCPI_OPTIONS "-D${full_name}:${type}=${${full_name}}")
set(MCPI_OPTIONS "${MCPI_OPTIONS}" PARENT_SCOPE)
endfunction()
# Prebuilt ARMHF Toolchain
if(BUILD_ARM_COMPONENTS)
mcpi_option(USE_PREBUILT_ARMHF_TOOLCHAIN "Whether To Use A Prebuilt ARMHF Toolchain For Building ARM Components" BOOL FALSE)
if(MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN)
include("${CMAKE_CURRENT_LIST_DIR}/prebuilt-armhf-toolchain.cmake")
endif()
endif()

View File

@ -16,38 +16,25 @@ if(BUILD_NATIVE_COMPONENTS)
if(NOT IS_ARM_TARGETING)
set(MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN TRUE)
endif()
if(MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN)
include("${CMAKE_CURRENT_LIST_DIR}/prebuilt-armhf-toolchain.cmake")
endif()
endif()
# Media Layer
if(NOT MCPI_HEADLESS_MODE)
set(DEFAULT_USE_MEDIA_LAYER_TRAMPOLINE FALSE)
set(DEFAULT_USE_MEDIA_LAYER_PROXY FALSE)
if(BUILD_NATIVE_COMPONENTS AND NOT IS_ARM_TARGETING)
set(DEFAULT_USE_MEDIA_LAYER_TRAMPOLINE TRUE)
set(DEFAULT_USE_MEDIA_LAYER_PROXY TRUE)
endif()
mcpi_option(USE_MEDIA_LAYER_TRAMPOLINE "Whether To Enable The Media Layer Trampoline (Requires QEMU)" BOOL "${DEFAULT_USE_MEDIA_LAYER_TRAMPOLINE}")
mcpi_option(USE_MEDIA_LAYER_PROXY "Whether To Enable The Media Layer Proxy" BOOL "${DEFAULT_USE_MEDIA_LAYER_PROXY}")
mcpi_option(USE_GLES1_COMPATIBILITY_LAYER "Whether To Enable The GLESv1_CM Compatibility Layer" BOOL TRUE)
else()
set(MCPI_USE_MEDIA_LAYER_TRAMPOLINE FALSE)
set(MCPI_USE_MEDIA_LAYER_PROXY FALSE)
endif()
if(MCPI_USE_MEDIA_LAYER_TRAMPOLINE)
if(MCPI_USE_MEDIA_LAYER_PROXY)
set(BUILD_MEDIA_LAYER_CORE "${BUILD_NATIVE_COMPONENTS}")
else()
set(BUILD_MEDIA_LAYER_CORE "${BUILD_ARM_COMPONENTS}")
endif()
# QEMU
if(BUILD_NATIVE_COMPONENTS)
include(CheckSymbolExists)
check_symbol_exists("__ARM_ARCH" "" MCPI_IS_ARM32_OR_ARM64_TARGETING)
set(MCPI_USE_QEMU TRUE)
if(MCPI_IS_ARM32_OR_ARM64_TARGETING AND NOT MCPI_USE_MEDIA_LAYER_TRAMPOLINE)
set(MCPI_USE_QEMU FALSE)
endif()
endif()
# Specify Variant Name
set(MCPI_VARIANT_NAME "minecraft-pi-reborn")
if(MCPI_SERVER_MODE)
@ -63,7 +50,7 @@ if(MCPI_SERVER_MODE)
else()
string(APPEND DEFAULT_APP_ID "Client")
endif()
mcpi_option(APP_ID "App ID" STRING "${DEFAULT_APP_ID}")
set(MCPI_APP_ID "${DEFAULT_APP_ID}" CACHE STRING "App ID")
# App Title
mcpi_option(APP_BASE_TITLE "Base App Title" STRING "Minecraft: Pi Edition: Reborn")
@ -78,5 +65,12 @@ mcpi_option(APP_TITLE "App Title" STRING "${DEFAULT_APP_TITLE}")
# Skin Server
mcpi_option(SKIN_SERVER "Skin Server" STRING "https://raw.githubusercontent.com/MCPI-Revival/Skins/data")
# Discord Invite
mcpi_option(DISCORD_INVITE "Discord Invite URL" STRING "https://discord.gg/mcpi-revival-740287937727561779")
# QEMU
if(BUILD_NATIVE_COMPONENTS)
include(CheckSymbolExists)
check_symbol_exists("__ARM_ARCH" "" MCPI_IS_ARM32_OR_ARM64_TARGETING)
set(MCPI_USE_QEMU TRUE)
if(MCPI_IS_ARM32_OR_ARM64_TARGETING)
set(MCPI_USE_QEMU FALSE)
endif()
endif()

View File

@ -26,7 +26,7 @@ file(WRITE "${toolchain_dir}/toolchain.cmake"
"set(CMAKE_SYSTEM_PROCESSOR \"arm\")\n"
"set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)\n"
)
set(MCPI_CMAKE_TOOLCHAIN_FILE "${toolchain_dir}/toolchain.cmake" CACHE FILEPATH "" FORCE)
set(CMAKE_TOOLCHAIN_FILE "${toolchain_dir}/toolchain.cmake" CACHE FILEPATH "" FORCE)
# Build Sysroot
set(sysroot_dir "${CMAKE_CURRENT_BINARY_DIR}/bundled-armhf-sysroot")

View File

@ -6,9 +6,9 @@ project(qemu)
set(QEMU_VERSION "8.2.1")
# Flatpak Support
set(QEMU_FLATPAK_PATCH "")
set(QEMU_PATCH "")
if(MCPI_IS_FLATPAK_BUILD)
set(QEMU_FLATPAK_PATCH "sed" "-i" "s/libdrm/libdrm-dis/g" "<SOURCE_DIR>/meson.build")
set(QEMU_PATCH "sed" "-i" "s/libdrm/libdrm-dis/g" "<SOURCE_DIR>/meson.build")
endif()
# Build
@ -19,7 +19,6 @@ if(DEFINED ENV{PKG_CONFIG_LIBDIR})
endif()
ExternalProject_Add(qemu
URL "${CMAKE_CURRENT_SOURCE_DIR}/../../archives/qemu-${QEMU_VERSION}.tar.xz"
# Configure Build
CONFIGURE_COMMAND
"${CMAKE_COMMAND}" "-E" "env"
${PKGCONFIG_ENV}
@ -30,21 +29,16 @@ ExternalProject_Add(qemu
"--cross-prefix="
"--cc=${CMAKE_C_COMPILER}"
"--cxx=${CMAKE_CXX_COMPILER}"
"--extra-ldflags=-ldl -Wl,-rpath=$ORIGIN/../lib/native -Wl,--disable-new-dtags"
"--disable-debug-info"
"--target-list=arm-linux-user"
"--without-default-features"
USES_TERMINAL_CONFIGURE TRUE
# Build Command
BUILD_COMMAND "ninja" "qemu-arm"
BUILD_BYPRODUCTS "<BINARY_DIR>/qemu-arm"
BUILD_COMMAND ninja "qemu-arm"
USES_TERMINAL_BUILD TRUE
# Disable Install/Test Commands
INSTALL_COMMAND ""
TEST_COMMAND ""
# Patch Command
PATCH_COMMAND "patch" "-p1" "<" "${CMAKE_CURRENT_SOURCE_DIR}/trampoline.patch"
COMMAND ${QEMU_FLATPAK_PATCH}
PATCH_COMMAND ${QEMU_PATCH}
BUILD_BYPRODUCTS "<BINARY_DIR>/qemu-arm"
)
# Install

View File

@ -1,56 +0,0 @@
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -17,6 +17,7 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define _ATFILE_SOURCE
+#include <dlfcn.h>
#include "qemu/osdep.h"
#include "qemu/cutils.h"
#include "qemu/path.h"
@@ -9070,6 +9071,17 @@ _syscall5(int, sys_move_mount, int, __from_dfd, const char *, __from_pathname,
int, __to_dfd, const char *, __to_pathname, unsigned int, flag)
#endif
+// g2h For Trampoline
+static CPUState *_trampoline_g2h_cpu = NULL;
+static void *_trampoline_g2h(uint32_t guest_addr) {
+ if (guest_addr == 0) {
+ return NULL;
+ }
+ return g2h(_trampoline_g2h_cpu, guest_addr);
+}
+// Trampoline Function
+typedef void (*_trampoline_t)(typeof(_trampoline_g2h) *g2h, uint32_t id, uint32_t *args);
+
/* This is an internal helper for do_syscall so that it is easier
* to have a single return point, so that actions, such as logging
* of syscall results, can be performed.
@@ -9095,6 +9107,27 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
void *p;
switch(num) {
+ case 0x1337: {
+ // Load Trampoline
+ static _trampoline_t _trampoline = NULL;
+ if (_trampoline == NULL) {
+ // Open Library
+ void *_trampoline_handle = dlopen("libmedia-layer-trampoline.so", RTLD_NOW);
+ // Load Function
+ if (_trampoline_handle != NULL) {
+ _trampoline = dlsym(_trampoline_handle, "trampoline");
+ }
+ }
+ if (_trampoline == NULL) {
+ // Failed To Load
+ qemu_log_mask(LOG_UNIMP, "Unable To Load Media Layer Trampoline: %s\n", dlerror());
+ return -TARGET_ENOSYS;
+ }
+ // Call Trampoline
+ _trampoline_g2h_cpu = cpu;
+ _trampoline(_trampoline_g2h, arg1, g2h(cpu, arg2));
+ return 0;
+ }
case TARGET_NR_exit:
/* In old applications this may be used to implement _exit(2).
However in threaded applications it is used for thread termination,

@ -1 +1 @@
Subproject commit fbb9b6d6da1a9dfa9290d420d1b2c34f91026111
Subproject commit db3879f7a51c5413e1c17e17cd6949d711132468

View File

@ -3,7 +3,6 @@
**3.0.0**
* Modding API Revamped
* `*(unsigned char **)` Is Dead!
* Now C++ Only
* Add Peaceful Mode To Options Screen
* Proper Create New World Screen
* Proper Chat Screen
@ -15,15 +14,6 @@
* Add `Display Date In Select World Screen` Feature Flag (Enabled By Default)
* Add `Optimized Chunk Sorting` Feature Flag (Enabled By Default)
* Add `Add Cake` Feature Flag (Enabled By Default)
* Add `Add Reborn Info To Options` Feature Flag (Enabled By Default)
* Add `Track FPS` Feature Flag (Disabled By Default)
* Split Up `Remove Creative Mode Restrictions` Feature Flag
* `Remove Creative Mode Restrictions` (Disabled By Default)
* `Display Slot Count In Creative Mode` (Disabled By Default)
* `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`
* Add Milk Buckets
* Implement Crafting Remainders
* Improve Death Messages

View File

@ -15,7 +15,7 @@ HOOK(chat_handle_packet_send, void, (Minecraft *minecraft, ChatPacket *packet))
if (out.length() > 0 && out[out.length() - 1] == '\n') {
out[out.length() - 1] = '\0';
}
gui->addMessage(&out);
misc_add_message(gui, out.c_str());
} else {
// Call Original Method
ensure_chat_handle_packet_send();

View File

@ -2,19 +2,19 @@ project(launcher)
# Launcher
add_executable(launcher
src/bootstrap.cpp
src/bootstrap.c
src/patchelf.cpp
src/util.c
src/crash-report.c
src/sdk.cpp
src/mods.cpp
src/options/parser.cpp
src/main.cpp
src/sdk.c
src/mods.c
)
if(NOT MCPI_SERVER_MODE)
if(MCPI_SERVER_MODE)
target_sources(launcher PRIVATE src/server/launcher.c)
else()
embed_resource(launcher src/client/available-feature-flags)
target_sources(launcher PRIVATE
src/client/configuration.cpp
src/client/launcher.cpp
src/client/cache.cpp
src/client/available-feature-flags # Show In IDE
)
@ -22,7 +22,6 @@ endif()
target_link_libraries(launcher reborn-util LIB_LIEF)
# RPath
set_target_properties(launcher PROPERTIES INSTALL_RPATH "$ORIGIN/lib/native")
target_link_options(launcher PRIVATE "LINKER:--disable-new-dtags")
# Install
install(TARGETS launcher DESTINATION "${MCPI_INSTALL_DIR}")

361
launcher/src/bootstrap.c Normal file
View File

@ -0,0 +1,361 @@
#define _FILE_OFFSET_BITS 64
#include <libreborn/libreborn.h>
#include "util.h"
#include "bootstrap.h"
#include "patchelf.h"
#include "crash-report.h"
#define MCPI_BINARY "minecraft-pi"
#define QEMU_BINARY "qemu-arm"
#define REQUIRED_PAGE_SIZE 4096
#define _STR(x) #x
#define STR(x) _STR(x)
// Exit Handler
static void exit_handler(__attribute__((unused)) int signal_id) {
// Pass Signal To Child
murder_children();
while (wait(NULL) > 0) {}
_exit(EXIT_SUCCESS);
}
// Debug Information
static void run_debug_command(const char *const command[], const char *prefix) {
int status = 0;
char *output = run_command(command, &status, NULL);
if (output != NULL) {
// Remove Newline
size_t length = strlen(output);
if (length > 0 && output[length - 1] == '\n') {
output[length - 1] = '\0';
}
// Print
DEBUG("%s: %s", prefix, output);
free(output);
}
if (!is_exit_status_success(status)) {
ERR("Unable To Gather Debug Information");
}
}
static void print_debug_information() {
// System Information
const char *const command[] = {"uname", "-a", NULL};
run_debug_command(command, "System Information");
// Version
DEBUG("Reborn Version: v%s", MCPI_VERSION);
// Architecture
const char *arch = "Unknown";
#ifdef __x86_64__
arch = "AMD64";
#elif defined(__aarch64__)
arch = "ARM64";
#elif defined(__arm__)
arch = "ARM32";
#endif
DEBUG("Reborn Target Architecture: %s", arch);
}
// Pre-Bootstrap
void pre_bootstrap(int argc, char *argv[]) {
// Set Debug Tag
reborn_debug_tag = "(Launcher) ";
// Disable stdout Buffering
setvbuf(stdout, NULL, _IONBF, 0);
// Print Version
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-v") == 0) {
// Print
printf("Reborn v%s\n", MCPI_VERSION);
fflush(stdout);
exit(EXIT_SUCCESS);
}
}
// Setup Logging
setup_log_file();
// --debug
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--debug") == 0) {
set_and_print_env("MCPI_DEBUG", "1");
break;
}
}
// Set Default Native Component Environment
#define set_variable_default(name) set_and_print_env("MCPI_NATIVE_" name, getenv(name));
for_each_special_environmental_variable(set_variable_default);
// GTK Dark Mode
#ifndef MCPI_SERVER_MODE
set_and_print_env("GTK_THEME", "Adwaita:dark");
#endif
// Configure PATH
{
// Get Binary Directory
char *binary_directory = get_binary_directory();
// Add Library Directory
char *new_path = NULL;
safe_asprintf(&new_path, "%s/bin", binary_directory);
// Add Existing PATH
{
char *value = getenv("PATH");
if (value != NULL && strlen(value) > 0) {
string_append(&new_path, ":%s", value);
}
}
// Set And Free
set_and_print_env("PATH", new_path);
free(new_path);
// Free Binary Directory
free(binary_directory);
}
// --copy-sdk
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--copy-sdk") == 0) {
char *binary_directory = get_binary_directory();
copy_sdk(binary_directory, 0);
free(binary_directory);
fflush(stdout);
exit(EXIT_SUCCESS);
}
}
// Setup Crash Reports
setup_crash_report();
// AppImage
#ifdef MCPI_IS_APPIMAGE_BUILD
{
char *owd = getenv("OWD");
if (owd != NULL && chdir(owd) != 0) {
ERR("AppImage: Unable To Fix Current Directory: %s", strerror(errno));
}
}
#endif
// Install Signal Handlers
struct sigaction act_sigint;
memset((void *) &act_sigint, 0, sizeof (struct sigaction));
act_sigint.sa_flags = SA_RESTART;
act_sigint.sa_handler = &exit_handler;
sigaction(SIGINT, &act_sigint, NULL);
struct sigaction act_sigterm;
memset((void *) &act_sigterm, 0, sizeof (struct sigaction));
act_sigterm.sa_flags = SA_RESTART;
act_sigterm.sa_handler = &exit_handler;
sigaction(SIGTERM, &act_sigterm, NULL);
// Check Page Size (Not Needed When Using QEMU)
#ifndef MCPI_USE_QEMU
long page_size = sysconf(_SC_PAGESIZE);
if (page_size != REQUIRED_PAGE_SIZE) {
ERR("Invalid page size! A page size of %ld bytes is required, but the system size is %ld bytes.", (long) REQUIRED_PAGE_SIZE, page_size);
}
#endif
// Debug Information
print_debug_information();
}
// Bootstrap
void bootstrap(int argc, char *argv[]) {
INFO("Configuring Game...");
// Get Binary Directory
char *binary_directory = get_binary_directory();
DEBUG("Binary Directory: %s", binary_directory);
// Copy SDK
copy_sdk(binary_directory, 1);
// Set MCPI_REBORN_ASSETS_PATH
{
char *assets_path = realpath("/proc/self/exe", NULL);
ALLOC_CHECK(assets_path);
chop_last_component(&assets_path);
string_append(&assets_path, "/data");
set_and_print_env("MCPI_REBORN_ASSETS_PATH", assets_path);
free(assets_path);
}
// Resolve Binary Path & Set MCPI_DIRECTORY
char *resolved_path = NULL;
{
// Log
DEBUG("Resolving File Paths...");
// Resolve Full Binary Path
char *full_path = NULL;
safe_asprintf(&full_path, "%s/" MCPI_BINARY, binary_directory);
resolved_path = realpath(full_path, NULL);
ALLOC_CHECK(resolved_path);
free(full_path);
}
// Fix MCPI Dependencies
char new_mcpi_exe_path[] = MCPI_PATCHED_DIR "/XXXXXX";
{
// Log
DEBUG("Patching ELF Dependencies...");
// Find Linker
char *linker = NULL;
// Select Linker
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
// Use ARM Sysroot Linker
safe_asprintf(&linker, "%s/sysroot/lib/ld-linux-armhf.so.3", binary_directory);
#else
// Use Current Linker
linker = patch_get_interpreter();
#endif
// Patch
patch_mcpi_elf_dependencies(resolved_path, new_mcpi_exe_path, linker);
// Free Linker Path
if (linker != NULL) {
free(linker);
}
// Verify
if (!starts_with(new_mcpi_exe_path, MCPI_PATCHED_DIR)) {
IMPOSSIBLE();
}
}
// Set MCPI_VANILLA_ASSETS_PATH
{
char *assets_path = strdup(resolved_path);
ALLOC_CHECK(assets_path);
chop_last_component(&assets_path);
string_append(&assets_path, "/data");
set_and_print_env("MCPI_VANILLA_ASSETS_PATH", assets_path);
free(assets_path);
}
// Free Resolved Path
free(resolved_path);
// Configure Library Search Path
{
// Log
DEBUG("Setting Linker Search Paths...");
// Prepare
char *transitive_ld_path = NULL;
char *mcpi_ld_path = NULL;
// Library Search Path For Native Components
{
// Add Native Library Directory
safe_asprintf(&transitive_ld_path, "%s/lib/native", binary_directory);
// Add Host LD_LIBRARY_PATH
{
char *value = getenv("LD_LIBRARY_PATH");
if (value != NULL && strlen(value) > 0) {
string_append(&transitive_ld_path, ":%s", value);
}
}
// Set
set_and_print_env("MCPI_NATIVE_LD_LIBRARY_PATH", transitive_ld_path);
free(transitive_ld_path);
}
// Library Search Path For ARM Components
{
// Add ARM Library Directory
safe_asprintf(&mcpi_ld_path, "%s/lib/arm", binary_directory);
// Add ARM Sysroot Libraries (Ensure Priority) (Ignore On Actual ARM System)
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
string_append(&mcpi_ld_path, ":%s/sysroot/lib:%s/sysroot/lib/arm-linux-gnueabihf:%s/sysroot/usr/lib:%s/sysroot/usr/lib/arm-linux-gnueabihf", binary_directory, binary_directory, binary_directory, binary_directory);
#endif
// Add Host LD_LIBRARY_PATH
{
char *value = getenv("LD_LIBRARY_PATH");
if (value != NULL && strlen(value) > 0) {
string_append(&mcpi_ld_path, ":%s", value);
}
}
// Set
set_and_print_env("MCPI_ARM_LD_LIBRARY_PATH", mcpi_ld_path);
free(mcpi_ld_path);
}
}
// Configure Preloaded Objects
{
// Log
DEBUG("Locating Mods...");
// Native Components
char *host_ld_preload = getenv("LD_PRELOAD");
set_and_print_env("MCPI_NATIVE_LD_PRELOAD", host_ld_preload);
// ARM Components
bootstrap_mods(binary_directory);
}
// Free Binary Directory
free(binary_directory);
// Start Game
INFO("Starting Game...");
// Arguments
int argv_start = 1; // argv = &new_args[argv_start]
const char *new_args[argv_start /* 1 Potential Prefix Argument (QEMU) */ + argc + 1 /* NULL-Terminator */]; //
// Copy Existing Arguments
for (int i = 1; i < argc; i++) {
new_args[i + argv_start] = argv[i];
}
// NULL-Terminator
new_args[argv_start + argc] = NULL;
// Set Executable Argument
new_args[argv_start] = new_mcpi_exe_path;
// Non-ARM Systems Need QEMU
#ifdef MCPI_USE_QEMU
argv_start--;
new_args[argv_start] = QEMU_BINARY;
// Use 4k Page Size
set_and_print_env("QEMU_PAGESIZE", STR(REQUIRED_PAGE_SIZE));
#endif
// Setup Environment
setup_exec_environment(1);
// Pass LD_* Variables Through QEMU
#ifdef MCPI_USE_QEMU
char *qemu_set_env = NULL;
#define pass_variable_through_qemu(name) string_append(&qemu_set_env, "%s%s=%s", qemu_set_env == NULL ? "" : ",", name, getenv(name));
for_each_special_environmental_variable(pass_variable_through_qemu);
set_and_print_env("QEMU_SET_ENV", qemu_set_env);
free(qemu_set_env);
// Treat QEMU Itself As A Native Component
setup_exec_environment(0);
#endif
// Run
const char **new_argv = &new_args[argv_start];
safe_execvpe(new_argv, (const char *const *) environ);
}

View File

@ -1,226 +0,0 @@
#define _FILE_OFFSET_BITS 64
#include <string>
#include <vector>
#include <libreborn/libreborn.h>
#include "util.h"
#include "bootstrap.h"
#include "patchelf.h"
#define MCPI_BINARY "minecraft-pi"
#define QEMU_BINARY "qemu-arm"
#define REQUIRED_PAGE_SIZE 4096
#define _STR(x) #x
#define STR(x) _STR(x)
// Debug Information
static void run_debug_command(const char *const command[], const char *prefix) {
int status = 0;
char *output = run_command(command, &status, nullptr);
if (output != nullptr) {
// Remove Newline
size_t length = strlen(output);
if (length > 0 && output[length - 1] == '\n') {
output[length - 1] = '\0';
}
// Print
DEBUG("%s: %s", prefix, output);
free(output);
}
if (!is_exit_status_success(status)) {
ERR("Unable To Gather Debug Information");
}
}
static void print_debug_information() {
// System Information
const char *const command[] = {"uname", "-a", nullptr};
run_debug_command(command, "System Information");
// Version
DEBUG("Reborn Version: v%s", MCPI_VERSION);
// Architecture
const char *arch = "Unknown";
#ifdef __x86_64__
arch = "AMD64";
#elif defined(__aarch64__)
arch = "ARM64";
#elif defined(__arm__)
arch = "ARM32";
#endif
DEBUG("Reborn Target Architecture: %s", arch);
}
// Bootstrap
void bootstrap() {
// Debug Information
print_debug_information();
// Check Page Size (Not Needed When Using QEMU)
#ifndef MCPI_USE_QEMU
long page_size = sysconf(_SC_PAGESIZE);
if (page_size != REQUIRED_PAGE_SIZE) {
ERR("Invalid page size! A page size of %ld bytes is required, but the system size is %ld bytes.", (long) REQUIRED_PAGE_SIZE, page_size);
}
#else
set_and_print_env("QEMU_PAGESIZE", STR(REQUIRED_PAGE_SIZE));
#endif
// Get Binary Directory
char *binary_directory_raw = get_binary_directory();
const std::string binary_directory = binary_directory_raw;
free(binary_directory_raw);
DEBUG("Binary Directory: %s", binary_directory.c_str());
// Copy SDK
copy_sdk(binary_directory, true);
// Set MCPI_REBORN_ASSETS_PATH
{
char *assets_path = realpath("/proc/self/exe", nullptr);
ALLOC_CHECK(assets_path);
chop_last_component(&assets_path);
string_append(&assets_path, "/data");
set_and_print_env("MCPI_REBORN_ASSETS_PATH", assets_path);
free(assets_path);
}
// Resolve Binary Path & Set MCPI_DIRECTORY
char *resolved_path = nullptr;
{
// Log
DEBUG("Resolving File Paths...");
// Resolve Full Binary Path
const std::string full_path = binary_directory + ("/" MCPI_BINARY);
resolved_path = realpath(full_path.c_str(), nullptr);
ALLOC_CHECK(resolved_path);
}
// Fix MCPI Dependencies
char new_mcpi_exe_path[] = MCPI_PATCHED_DIR "/XXXXXX";
{
// Log
DEBUG("Patching ELF Dependencies...");
// Find Linker
char *linker = nullptr;
// Select Linker
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
// Use ARM Sysroot Linker
safe_asprintf(&linker, "%s/sysroot/lib/ld-linux-armhf.so.3", binary_directory.c_str());
#else
// Use Current Linker
linker = patch_get_interpreter();
#endif
// Patch
patch_mcpi_elf_dependencies(resolved_path, new_mcpi_exe_path, linker);
// Free Linker Path
if (linker != nullptr) {
free(linker);
}
// Verify
if (!starts_with(new_mcpi_exe_path, MCPI_PATCHED_DIR)) {
IMPOSSIBLE();
}
}
// Set MCPI_VANILLA_ASSETS_PATH
{
char *assets_path = strdup(resolved_path);
ALLOC_CHECK(assets_path);
chop_last_component(&assets_path);
string_append(&assets_path, "/data");
set_and_print_env("MCPI_VANILLA_ASSETS_PATH", assets_path);
free(assets_path);
}
// Free Resolved Path
free(resolved_path);
// Configure Library Search Path
{
// Log
DEBUG("Setting Linker Search Paths...");
// Prepare
std::string mcpi_ld_path = "";
// Library Search Path For ARM Components
{
// Add ARM Library Directory
mcpi_ld_path += binary_directory + "/lib/arm:";
// Add ARM Sysroot Libraries (Ensure Priority) (Ignore On Actual ARM System)
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
mcpi_ld_path += binary_directory + "/sysroot/lib:";
mcpi_ld_path += binary_directory + "/sysroot/lib/arm-linux-gnueabihf:";
mcpi_ld_path += binary_directory + "/sysroot/usr/lib:";
mcpi_ld_path += binary_directory + "/sysroot/usr/lib/arm-linux-gnueabihf:";
#endif
// Add Host LD_LIBRARY_PATH
{
char *value = getenv("LD_LIBRARY_PATH");
if (value != nullptr && strlen(value) > 0) {
mcpi_ld_path += value;
}
}
// Set
set_and_print_env(MCPI_LD_VARIABLE_PREFIX "LD_LIBRARY_PATH", mcpi_ld_path.c_str());
}
}
// Configure Preloaded Objects
{
// Log
DEBUG("Locating Mods...");
// ARM Components
bootstrap_mods(binary_directory);
}
// Start Game
INFO("Starting Game...");
// Arguments
std::vector<std::string> args;
// Non-ARM Systems Need QEMU
#ifdef MCPI_USE_QEMU
args.insert(args.begin(), QEMU_BINARY);
#endif
// Preserve Existing LD_* Variables
#define preserve_variable(name) set_and_print_env(MCPI_ORIGINAL_LD_VARIABLE_PREFIX name, getenv(name))
for_each_special_environmental_variable(preserve_variable);
set_and_print_env(MCPI_ORIGINAL_LD_VARIABLES_PRESERVED_ENV, "1");
// Setup Environment
setup_exec_environment(1);
// Pass LD_* Variables Through QEMU
#ifdef MCPI_USE_QEMU
#define pass_variable_through_qemu(name) args.push_back("-E"); args.push_back(std::string(name) + "=" + getenv(name))
for_each_special_environmental_variable(pass_variable_through_qemu);
// Treat QEMU Itself As A Native Component
setup_exec_environment(0);
#endif
// Specify MCPI Binary
args.push_back(new_mcpi_exe_path);
// Run
const char *new_argv[args.size() + 1];
for (std::vector<std::string>::size_type i = 0; i < args.size(); i++) {
new_argv[i] = args[i].c_str();
}
new_argv[args.size()] = nullptr;
safe_execvpe(new_argv, environ);
}

View File

@ -1,7 +1,14 @@
#pragma once
#include <string>
#ifdef __cplusplus
extern "C" {
#endif
void bootstrap();
void copy_sdk(const std::string &binary_directory, bool log_with_debug);
void bootstrap_mods(const std::string &binary_directory);
void pre_bootstrap(int argc, char *argv[]);
void bootstrap(int argc, char *argv[]);
void copy_sdk(char *binary_directory, int log_with_debug);
void bootstrap_mods(char *binary_directory);
#ifdef __cplusplus
}
#endif

View File

@ -8,10 +8,6 @@ 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
@ -64,6 +60,4 @@ 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 Track FPS
TRUE Disable Buggy Held Item Caching

View File

@ -8,13 +8,13 @@
#include <libreborn/libreborn.h>
#include "configuration.h"
#include "launcher.h"
#include "cache.h"
// Get Cache Path
static std::string get_cache_path() {
const char *home = getenv("HOME");
if (home == nullptr) {
if (home == NULL) {
IMPOSSIBLE();
}
return std::string(home) + HOME_SUBDIRECTORY_FOR_GAME_DATA "/.launcher-cache";

View File

@ -1,23 +0,0 @@
#pragma once
#include <string>
#include <functional>
#include "../options/parser.h"
// Defaults
#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);
// Handle Non-Launch Commands
void handle_non_launch_client_only_commands(const options_t &options);
// Check Environment
void check_environment_client();
// Configure Client Options
void configure_client(const options_t &options);

View File

@ -10,24 +10,25 @@
#include <libreborn/libreborn.h>
#include "../util.h"
#include "configuration.h"
#include "../bootstrap.h"
#include "launcher.h"
#include "cache.h"
// Strip Feature Flag Default
std::string strip_feature_flag_default(const std::string &flag, bool *default_ret) {
std::string strip_feature_flag_default(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) {
if (default_ret != NULL) {
*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) {
if (default_ret != NULL) {
*default_ret = false;
}
return flag.substr(false_str.length(), std::string::npos);
@ -40,7 +41,7 @@ std::string strip_feature_flag_default(const std::string &flag, bool *default_re
// Load Available Feature Flags
extern unsigned char available_feature_flags[];
extern size_t available_feature_flags_len;
void load_available_feature_flags(const std::function<void(std::string)> &callback) {
void load_available_feature_flags(std::function<void(std::string)> callback) {
// Get Path
char *binary_directory = get_binary_directory();
std::string path = std::string(binary_directory) + "/available-feature-flags";
@ -54,7 +55,7 @@ void load_available_feature_flags(const std::function<void(std::string)> &callba
{
std::string line;
while (std::getline(stream, line)) {
if (!line.empty()) {
if (line.length() > 0) {
// Verify Line
if (line.find('|') == std::string::npos) {
lines.push_back(line);
@ -66,15 +67,15 @@ void load_available_feature_flags(const std::function<void(std::string)> &callba
}
}
// Sort
std::sort(lines.begin(), lines.end(), [](const std::string &a, const std::string &b) {
std::sort(lines.begin(), lines.end(), [](std::string a, std::string b) {
// Strip Defaults
std::string stripped_a = strip_feature_flag_default(a, nullptr);
std::string stripped_b = strip_feature_flag_default(b, nullptr);
std::string stripped_a = strip_feature_flag_default(a, NULL);
std::string stripped_b = strip_feature_flag_default(b, NULL);
// Sort
return stripped_a < stripped_b;
});
// Run Callbacks
for (const std::string &line : lines) {
for (std::string &line : lines) {
callback(line);
}
}
@ -82,11 +83,11 @@ void load_available_feature_flags(const std::function<void(std::string)> &callba
// 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) {
if (getenv(env_name) == NULL) {
// Run
int return_code;
char *output = run_command(command, &return_code, nullptr);
if (output != nullptr) {
char *output = run_command(command, &return_code, NULL);
if (output != NULL) {
// Trim
int length = strlen(output);
if (output[length - 1] == '\n') {
@ -121,14 +122,14 @@ static void run_zenity_and_set_env(const char *env_name, std::vector<std::string
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;
full_command_array[full_command.size()] = NULL;
// 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) {
static void set_env_if_unset(const char *env_name, std::function<std::string()> callback) {
if (getenv(env_name) == NULL) {
char *value = strdup(callback().c_str());
ALLOC_CHECK(value);
set_and_print_env(env_name, value);
@ -136,50 +137,76 @@ static void set_env_if_unset(const char *env_name, const std::function<std::stri
}
}
// 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);
});
exit(EXIT_SUCCESS);
}
}
// Check Environment
void check_environment_client() {
// Launch
#define LIST_DIALOG_SIZE "400"
int main(int argc, char *argv[]) {
// Don't Run As Root
if (getenv("_MCPI_SKIP_ROOT_CHECK") == nullptr && (getuid() == 0 || geteuid() == 0)) {
if (getenv("_MCPI_SKIP_ROOT_CHECK") == NULL && (getuid() == 0 || geteuid() == 0)) {
ERR("Don't Run As Root");
}
// Ensure HOME
if (getenv("HOME") == NULL) {
ERR("$HOME Isn't Set");
}
// Check For Display
#ifndef MCPI_HEADLESS_MODE
if (getenv("DISPLAY") == nullptr && getenv("WAYLAND_DISPLAY") == nullptr) {
if (getenv("DISPLAY") == NULL && getenv("WAYLAND_DISPLAY") == NULL) {
ERR("No display attached! Make sure $DISPLAY or $WAYLAND_DISPLAY is set.");
}
#endif
}
// Configure Client Options
#define LIST_DIALOG_SIZE "400"
void configure_client(const options_t &options) {
// Wipe Cache If Needed
if (options.wipe_cache) {
wipe_cache();
// Print Features
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--print-available-feature-flags") == 0) {
// Print Available Feature Flags
load_available_feature_flags([](std::string line) {
printf("%s\n", line.c_str());
fflush(stdout);
});
return 0;
}
}
// Pre-Bootstrap
pre_bootstrap(argc, argv);
// Create ~/.minecraft-pi If Needed
{
char *minecraft_folder = NULL;
safe_asprintf(&minecraft_folder, "%s" HOME_SUBDIRECTORY_FOR_GAME_DATA, getenv("HOME"));
const char *const command[] = {"mkdir", "-p", minecraft_folder, NULL};
run_simple_command(command, "Unable To Create Data Directory");
free(minecraft_folder);
}
// --wipe-cache
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--wipe-cache") == 0) {
wipe_cache();
break;
}
}
// --no-cache
bool no_cache = false;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--no-cache") == 0) {
no_cache = true;
break;
}
}
// Load Cache
launcher_cache cache = options.no_cache ? empty_cache : load_cache();
launcher_cache cache = no_cache ? empty_cache : load_cache();
// --default
if (options.use_default) {
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--default") == 0) {
// Use Default Feature Flags
set_env_if_unset("MCPI_FEATURE_FLAGS", [&cache]() {
std::string feature_flags = "";
load_available_feature_flags([&feature_flags, &cache](const std::string &flag) {
load_available_feature_flags([&feature_flags, &cache](std::string flag) {
bool value;
// Strip Default Value
std::string stripped_flag = strip_feature_flag_default(flag, &value);
@ -193,7 +220,7 @@ void configure_client(const options_t &options) {
feature_flags += stripped_flag + '|';
}
});
if (!feature_flags.empty() && feature_flags[feature_flags.length() - 1] == '|') {
if (feature_flags.length() > 0 && feature_flags[feature_flags.length() - 1] == '|') {
feature_flags.pop_back();
}
return feature_flags;
@ -204,6 +231,8 @@ void configure_client(const options_t &options) {
set_env_if_unset("MCPI_USERNAME", [&cache]() {
return cache.username;
});
break;
}
}
// Setup MCPI_FEATURE_FLAGS
@ -219,7 +248,7 @@ void configure_client(const options_t &options) {
command.push_back("Enabled");
command.push_back("--column");
command.push_back("Feature");
load_available_feature_flags([&command, &cache](const std::string &flag) {
load_available_feature_flags([&command, &cache](std::string flag) {
bool value;
// Strip Default Value
std::string stripped_flag = strip_feature_flag_default(flag, &value);
@ -258,7 +287,7 @@ void configure_client(const options_t &options) {
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.compare(cache.render_distance) == 0 ? "TRUE" : "FALSE");
command.push_back(render_distance);
}
// Run
@ -277,7 +306,10 @@ void configure_client(const options_t &options) {
}
// Save Cache
if (!options.no_cache) {
if (!no_cache) {
save_cache();
}
// Bootstrap
bootstrap(argc, argv);
}

View File

@ -0,0 +1,12 @@
#pragma once
#include <string>
#include <functional>
// Defaults
#define DEFAULT_USERNAME "StevePi"
#define DEFAULT_RENDER_DISTANCE "Short"
// Feature Flags
std::string strip_feature_flag_default(std::string flag, bool *default_ret);
void load_available_feature_flags(std::function<void(std::string)> callback);

View File

@ -34,7 +34,7 @@ static void show_report(const char *log_filename) {
"--width", CRASH_REPORT_DIALOG_WIDTH,
"--height", CRASH_REPORT_DIALOG_HEIGHT,
"--text-info",
"--text", MCPI_APP_BASE_TITLE " has crashed!\n\nNeed help? Consider asking on the <a href=\"" MCPI_DISCORD_INVITE "\">Discord server</a>! <i>If you believe this is a problem with " MCPI_APP_BASE_TITLE " itself, please upload this crash report to the #bugs Discord channel.</i>",
"--text", MCPI_APP_BASE_TITLE " has crashed!\n\nNeed help? Consider asking on the <a href=\"https://discord.com/invite/aDqejQGMMy\">Discord server</a>! <i>If you believe this is a problem with " MCPI_APP_BASE_TITLE " itself, please upload this crash report to the #bugs Discord channel.</i>",
"--filename", log_filename,
"--no-wrap",
"--font", "Monospace",
@ -58,6 +58,7 @@ static void exit_handler(__attribute__((unused)) int signal) {
#define PIPE_WRITE 1
#define MCPI_LOGS_DIR "/tmp/.minecraft-pi-logs"
static char log_filename[] = MCPI_LOGS_DIR "/XXXXXX";
static int log_file_fd = -1;
void setup_log_file() {
// Ensure Temporary Directory
{
@ -73,12 +74,16 @@ void setup_log_file() {
}
// Create Temporary File
int log_file_fd = mkstemp(log_filename);
log_file_fd = mkstemp(log_filename);
if (log_file_fd == -1) {
ERR("Unable To Create Log File: %s", strerror(errno));
}
close(log_file_fd);
reborn_set_log(log_filename);
// Setup Environment
char *log_file_fd_env = NULL;
safe_asprintf(&log_file_fd_env, "%i", log_file_fd);
set_and_print_env("MCPI_LOG_FILE_FD", log_file_fd_env);
free(log_file_fd_env);
}
void setup_crash_report() {
// Store Output
@ -116,11 +121,13 @@ void setup_crash_report() {
track_child(ret);
// Install Signal Handlers
struct sigaction act_sigint = {0};
struct sigaction act_sigint;
memset((void *) &act_sigint, 0, sizeof (struct sigaction));
act_sigint.sa_flags = SA_RESTART;
act_sigint.sa_handler = &exit_handler;
sigaction(SIGINT, &act_sigint, NULL);
struct sigaction act_sigterm = {0};
struct sigaction act_sigterm;
memset((void *) &act_sigterm, 0, sizeof (struct sigaction));
act_sigterm.sa_flags = SA_RESTART;
act_sigterm.sa_handler = &exit_handler;
sigaction(SIGTERM, &act_sigterm, NULL);
@ -171,17 +178,17 @@ void setup_crash_report() {
bytes_available = 0;
}
// Read
ssize_t bytes_read = read(poll_fds[i].fd, buf, BUFFER_SIZE);
ssize_t bytes_read = read(poll_fds[i].fd, (void *) buf, BUFFER_SIZE);
if (bytes_read == -1) {
ERR("Unable To Read Input: %s", strerror(errno));
ERR("Unable To Read Log Data: %s", strerror(errno));
}
// Write To Child
if (write(input_pipe[PIPE_WRITE], buf, bytes_read) == -1) {
if (write(input_pipe[PIPE_WRITE], (void *) buf, bytes_read) == -1) {
ERR("Unable To Write Input To Child: %s", strerror(errno));
}
} else {
// Data Available From Child's stdout/stderr
ssize_t bytes_read = read(poll_fds[i].fd, buf, BUFFER_SIZE - 1 /* Account For NULL-Terminator */);
ssize_t bytes_read = read(poll_fds[i].fd, (void *) buf, BUFFER_SIZE - 1 /* Account For NULL-Terminator */);
if (bytes_read == -1) {
ERR("Unable To Read Log Data: %s", strerror(errno));
}
@ -191,11 +198,9 @@ void setup_crash_report() {
fprintf(poll_fds[i].fd == output_pipe[PIPE_READ] ? stdout : stderr, "%s", buf);
// Write To log
reborn_lock_log();
if (write(reborn_get_log_fd(), buf, bytes_read) == -1) {
if (write(log_file_fd, (void *) buf, bytes_read) == -1) {
ERR("Unable To Write Log Data: %s", strerror(errno));
}
reborn_unlock_log();
}
} else {
// File Descriptor No Longer Accessible
@ -229,18 +234,18 @@ void setup_crash_report() {
fprintf(stderr, "%s", exit_code_line);
// Write Exit Code Log Line
reborn_lock_log();
if (write(reborn_get_log_fd(), exit_code_line, strlen(exit_code_line)) == -1) {
if (write(log_file_fd, (void *) exit_code_line, strlen(exit_code_line)) == -1) {
ERR("Unable To Write Exit Code To Log: %s", strerror(errno));
}
reborn_unlock_log();
// Free Exit Code Log Line
free(exit_code_line);
}
// Close Log File
reborn_close_log();
// Close Log File FD
if (close(log_file_fd) == -1) {
ERR("Unable To Close Log File Descriptor: %s", strerror(errno));
}
// Show Crash Log
#ifndef MCPI_HEADLESS_MODE

View File

@ -1,155 +0,0 @@
#include <cstdlib>
#include <libreborn/libreborn.h>
#include <sys/stat.h>
#include "bootstrap.h"
#include "options/parser.h"
#include "crash-report.h"
#include "util.h"
#ifndef MCPI_SERVER_MODE
#include "client/configuration.h"
#endif
// Bind Options To Environmental Variable
static void bind_to_env(const char *env, const bool value) {
const bool force = env[0] == '_';
if (force || value) {
set_and_print_env(env, value ? "1" : nullptr);
}
}
static void setup_environment(const options_t &options) {
// Passthrough Options To Game
#ifndef MCPI_SERVER_MODE
bind_to_env("_MCPI_BENCHMARK", options.benchmark);
#else
bind_to_env("_MCPI_ONLY_GENERATE", options.only_generate);
#endif
// GTK Dark Mode
#ifndef MCPI_HEADLESS_MODE
set_and_print_env("GTK_THEME", "Adwaita:dark");
#endif
// Configure PATH
{
// Get Binary Directory
char *binary_directory = get_binary_directory();
std::string new_path = std::string(binary_directory) + "/bin";
free(binary_directory);
// Add Existing PATH
{
char *value = getenv("PATH");
if (value != nullptr && strlen(value) > 0) {
new_path += std::string(":") + value;
}
}
// Set And Free
set_and_print_env("PATH", new_path.c_str());
}
}
// Non-Launch Commands
static void handle_non_launch_commands(const options_t &options) {
if (options.copy_sdk) {
char *binary_directory = get_binary_directory();
copy_sdk(binary_directory, false);
free(binary_directory);
fflush(stdout);
exit(EXIT_SUCCESS);
}
}
// Exit Handler
static void exit_handler(__attribute__((unused)) int signal_id) {
// Pass Signal To Child
murder_children();
while (wait(nullptr) > 0) {}
_exit(EXIT_SUCCESS);
}
// Start The Game
static void start_game(const options_t &options) {
// Disable stdout Buffering
setvbuf(stdout, nullptr, _IONBF, 0);
// Environemntal Variable Options
setup_environment(options);
// Setup Crash Reporting
if (!options.disable_crash_report) {
setup_log_file();
setup_crash_report();
}
// Install Signal Handlers
struct sigaction act_sigint = {};
act_sigint.sa_flags = SA_RESTART;
act_sigint.sa_handler = &exit_handler;
sigaction(SIGINT, &act_sigint, nullptr);
struct sigaction act_sigterm = {};
act_sigterm.sa_flags = SA_RESTART;
act_sigterm.sa_handler = &exit_handler;
sigaction(SIGTERM, &act_sigterm, nullptr);
// Setup Home
#ifndef MCPI_SERVER_MODE
// Ensure $HOME
const char *home = getenv("HOME");
if (home == nullptr) {
ERR("$HOME Isn't Set");
}
// Create If Needed
{
std::string minecraft_folder = std::string(home) + HOME_SUBDIRECTORY_FOR_GAME_DATA;
struct stat tmp_stat = {};
bool exists = stat(minecraft_folder.c_str(), &tmp_stat) != 0 ? false : S_ISDIR(tmp_stat.st_mode);
if (!exists) {
// Doesn't Exist
if (mkdir(minecraft_folder.c_str(), S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
ERR("Unable To Create Data Directory: %s", strerror(errno));
}
}
}
#else
// Set Home To Current Directory, So World Data Is Stored There
char *launch_directory = getcwd(NULL, 0);
set_and_print_env("HOME", launch_directory);
free(launch_directory);
#endif
// Configure Client Options
#ifndef MCPI_SERVER_MODE
configure_client(options);
#endif
// Bootstrap
bootstrap();
}
// Main
int main(int argc, char *argv[]) {
// Parse Options
options_t options = parse_options(argc, argv);
// Set Debug Tag
reborn_debug_tag = "(Launcher) ";
// Debug Logging
unsetenv(MCPI_LOG_ENV);
bind_to_env(MCPI_DEBUG_ENV, options.debug);
// Handle Non-Launch Commands (Copy SDK, Print Feature Flags, Etc)
handle_non_launch_commands(options);
#ifndef MCPI_SERVER_MODE
handle_non_launch_client_only_commands(options);
#endif
// Check Environment
#ifndef MCPI_SERVER_MODE
// Code After This Can Safely Open A Window
check_environment_client();
#endif
// Start The Game
start_game(options);
}

110
launcher/src/mods.c Normal file
View File

@ -0,0 +1,110 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
#include <libreborn/libreborn.h>
#include "bootstrap.h"
// Get All Mods In Folder
static void load(char **ld_preload, const char *folder) {
int folder_name_length = strlen(folder);
// Open Folder
DIR *dp = opendir(folder);
if (dp != NULL) {
// Loop Through Folder
struct dirent *entry = NULL;
errno = 0;
while (1) {
errno = 0;
entry = readdir(dp);
if (entry != NULL) {
// Check If File Is Regular
if (entry->d_type == DT_REG) {
// Get Full Name
int name_length = strlen(entry->d_name);
int total_length = folder_name_length + name_length;
char name[total_length + 1];
// Concatenate Folder Name And File Name
for (int i = 0; i < folder_name_length; i++) {
name[i] = folder[i];
}
for (int i = 0; i < name_length; i++) {
name[folder_name_length + i] = entry->d_name[i];
}
// Add Terminator
name[total_length] = '\0';
// Check If File Is Accessible
int result = access(name, R_OK);
if (result == 0) {
// Add To LD_PRELOAD
string_append(ld_preload, "%s%s", *ld_preload == NULL ? "" : ":", name);
} else if (result == -1 && errno != 0) {
// Fail
WARN("Unable To Access: %s: %s", name, strerror(errno));
errno = 0;
}
}
} else if (errno != 0) {
// Error Reading Contents Of Folder
ERR("Error Reading Directory: %s: %s", folder, strerror(errno));
} else {
// Done!
break;
}
}
// Close Folder
closedir(dp);
} else if (errno == ENOENT) {
// Folder Doesn't Exist
} else {
// Unable To Open Folder
ERR("Error Opening Directory: %s: %s", folder, strerror(errno));
}
}
// Bootstrap Mods
void bootstrap_mods(char *binary_directory) {
// Prepare
char *preload = NULL;
// ~/.minecraft-pi/mods
{
// Get Mods Folder
char *mods_folder = NULL;
safe_asprintf(&mods_folder, "%s" HOME_SUBDIRECTORY_FOR_GAME_DATA "/mods/", getenv("HOME"));
// Load Mods From ./mods
load(&preload, mods_folder);
// Free Mods Folder
free(mods_folder);
}
// Built-In Mods
{
// Get Mods Folder
char *mods_folder = NULL;
safe_asprintf(&mods_folder, "%s/mods/", binary_directory);
// Load Mods From ./mods
load(&preload, mods_folder);
// Free Mods Folder
free(mods_folder);
}
// Add LD_PRELOAD
{
char *value = getenv("LD_PRELOAD");
if (value != NULL && strlen(value) > 0) {
string_append(&preload, ":%s", value);
}
}
// Set
set_and_print_env("MCPI_ARM_LD_PRELOAD", preload);
free(preload);
}

View File

@ -1,86 +0,0 @@
#include <dirent.h>
#include <cerrno>
#include <sys/stat.h>
#include <unistd.h>
#include <libreborn/libreborn.h>
#include "bootstrap.h"
// Get All Mods In Folder
static void load(std::string &ld_preload, const std::string &folder) {
// Open Folder
DIR *dp = opendir(folder.c_str());
if (dp != nullptr) {
// Loop Through Folder
while (true) {
errno = 0;
dirent *entry = readdir(dp);
if (entry != nullptr) {
// Check If File Is Regular
if (entry->d_type == DT_REG) {
// Get Full Name
std::string name = folder + entry->d_name;
// Check If File Is Accessible
int result = access(name.c_str(), R_OK);
if (result == 0) {
// Add To LD_PRELOAD
ld_preload += name + ":";
} else if (result == -1 && errno != 0) {
// Fail
WARN("Unable To Access: %s: %s", name.c_str(), strerror(errno));
errno = 0;
}
}
} else if (errno != 0) {
// Error Reading Contents Of Folder
ERR("Error Reading Directory: %s: %s", folder.c_str(), strerror(errno));
} else {
// Done!
break;
}
}
// Close Folder
closedir(dp);
} else if (errno == ENOENT) {
// Folder Doesn't Exist
} else {
// Unable To Open Folder
ERR("Error Opening Directory: %s: %s", folder.c_str(), strerror(errno));
}
}
// Bootstrap Mods
#define SUBDIRECTORY_FOR_MODS "/mods/"
void bootstrap_mods(const std::string &binary_directory) {
// Prepare
std::string preload = "";
// ~/.minecraft-pi/mods
{
// Get Mods Folder
std::string mods_folder = std::string(getenv("HOME")) + HOME_SUBDIRECTORY_FOR_GAME_DATA SUBDIRECTORY_FOR_MODS;
// Load Mods From ./mods
load(preload, mods_folder);
}
// Built-In Mods
{
// Get Mods Folder
std::string mods_folder = binary_directory + SUBDIRECTORY_FOR_MODS;
// Load Mods From ./mods
load(preload, mods_folder);
}
// Add LD_PRELOAD
{
const char *value = getenv("LD_PRELOAD");
if (value != nullptr && strlen(value) > 0) {
preload += value;
}
}
// Set
set_and_print_env(MCPI_LD_VARIABLE_PREFIX "LD_PRELOAD", preload.c_str());
}

View File

@ -1,12 +0,0 @@
OPTION(debug, "debug", 'd', "Enable Debug Logging (" MCPI_DEBUG_ENV ")")
OPTION(copy_sdk, "copy-sdk", -2, "Extract Modding SDK And Exit")
OPTION(disable_crash_report, "disable-crash-report", -1, "Disable Crash Report Dialog")
#ifndef MCPI_SERVER_MODE
OPTION(use_default, "default", -3, "Skip Configuration Dialogs")
OPTION(no_cache, "no-cache", -4, "Disable Configuration Cache")
OPTION(wipe_cache, "wipe-cache", -5, "Wipe Cached Configuration")
OPTION(print_available_feature_flags, "print-available-feature-flags", -6, "Print Available Feature Flags")
OPTION(benchmark, "benchmark", -7, "Run Benchmark")
#else
OPTION(only_generate, "only-generate", -8, "Generate World And Exit")
#endif

View File

@ -1,38 +0,0 @@
#include <argp.h>
#include "parser.h"
// Globals
const char *argp_program_version = "Reborn v" MCPI_VERSION;
const char *argp_program_bug_address = "<" MCPI_DISCORD_INVITE ">";
static char doc[] = "Minecraft: Pi Edition Modding Project";
// Options
#define OPTION(ignored, name, key, doc) {name, key, nullptr, 0, doc, 0},
static argp_option options_data[] = {
#include "option-list.h"
{nullptr, 0, nullptr, 0, nullptr, 0}
};
#undef OPTION
// Parse Options
#define OPTION(name, ignored, key, ...) \
case key: \
options->name = true; \
break;
static error_t parse_opt(int key, __attribute__((unused)) char *arg, argp_state *state) {
options_t *options = (options_t *) state->input;
switch (key) {
#include "option-list.h"
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
#undef OPTION
static argp argp = {options_data, parse_opt, nullptr, doc, nullptr, nullptr, nullptr};
options_t parse_options(int argc, char *argv[]) {
options_t options = {};
argp_parse(&argp, argc, argv, 0, nullptr, &options);
return options;
}

View File

@ -1,10 +0,0 @@
#pragma once
#include <libreborn/libreborn.h>
#define OPTION(name, ...) bool name;
struct options_t {
#include "option-list.h"
};
#undef OPTION
options_t parse_options(int argc, char *argv[]);

67
launcher/src/sdk.c Normal file
View File

@ -0,0 +1,67 @@
#include <libreborn/libreborn.h>
#include "bootstrap.h"
#include "util.h"
// Log
#define LOG(is_debug, ...) \
{ \
if (is_debug) { \
DEBUG(__VA_ARGS__); \
} else { \
INFO(__VA_ARGS__); \
} \
}
// Copy SDK Into ~/.minecraft-pi
#define HOME_SUBDIRECTORY_FOR_SDK HOME_SUBDIRECTORY_FOR_GAME_DATA "/sdk"
void copy_sdk(char *binary_directory, int log_with_debug) {
// Ensure SDK Directory
{
char *sdk_path = NULL;
safe_asprintf(&sdk_path, "%s" HOME_SUBDIRECTORY_FOR_SDK, getenv("HOME"));
const char *const command[] = {"mkdir", "-p", sdk_path, NULL};
run_simple_command(command, "Unable To Create SDK Directory");
}
// Lock File
char *lock_file_path = NULL;
safe_asprintf(&lock_file_path, "%s" HOME_SUBDIRECTORY_FOR_SDK "/.lock", getenv("HOME"));
int lock_file_fd = lock_file(lock_file_path);
// Output Directory
char *output = NULL;
safe_asprintf(&output, "%s" HOME_SUBDIRECTORY_FOR_SDK "/" MCPI_SDK_DIR, getenv("HOME"));
// Source Directory
char *source = NULL;
safe_asprintf(&source, "%s/sdk/.", binary_directory);
// Clean
{
const char *const command[] = {"rm", "-rf", output, NULL};
run_simple_command(command, "Unable To Clean SDK Output Directory");
}
// Make Directory
{
const char *const command[] = {"mkdir", "-p", output, NULL};
run_simple_command(command, "Unable To Create SDK Output Directory");
}
// Copy
{
const char *const command[] = {"cp", "-ar", source, output, NULL};
run_simple_command(command, "Unable To Copy SDK");
}
// Log
LOG(log_with_debug, "Copied SDK To: %s", output);
// Free
free(output);
free(source);
// Unlock File
unlock_file(lock_file_path, lock_file_fd);
free(lock_file_path);
}

View File

@ -1,59 +0,0 @@
#include <libreborn/libreborn.h>
#include "bootstrap.h"
#include "util.h"
// Log
#define LOG(is_debug, ...) \
{ \
if (is_debug) { \
DEBUG(__VA_ARGS__); \
} else { \
INFO(__VA_ARGS__); \
} \
}
// Copy SDK Into ~/.minecraft-pi
#define HOME_SUBDIRECTORY_FOR_SDK HOME_SUBDIRECTORY_FOR_GAME_DATA "/sdk"
void copy_sdk(const std::string &binary_directory, const bool log_with_debug) {
// Ensure SDK Directory
std::string sdk_path;
{
sdk_path = std::string(getenv("HOME")) + HOME_SUBDIRECTORY_FOR_SDK;
const char *const command[] = {"mkdir", "-p", sdk_path.c_str(), nullptr};
run_simple_command(command, "Unable To Create SDK Directory");
}
// Lock File
std::string lock_file_path = sdk_path + "/.lock";
int lock_file_fd = lock_file(lock_file_path.c_str());
// Output Directory
std::string output = sdk_path + "/" MCPI_SDK_DIR;
// Source Directory
std::string source = binary_directory + "/sdk/.";
// Clean
{
const char *const command[] = {"rm", "-rf", output.c_str(), nullptr};
run_simple_command(command, "Unable To Clean SDK Output Directory");
}
// Make Directory
{
const char *const command[] = {"mkdir", "-p", output.c_str(), nullptr};
run_simple_command(command, "Unable To Create SDK Output Directory");
}
// Copy
{
const char *const command[] = {"cp", "-ar", source.c_str(), output.c_str(), nullptr};
run_simple_command(command, "Unable To Copy SDK");
}
// Log
LOG(log_with_debug, "Copied SDK To: %s", output.c_str());
// Unlock File
unlock_file(lock_file_path.c_str(), lock_file_fd);
}

View File

@ -0,0 +1,19 @@
#include <stdlib.h>
#include <unistd.h>
#include <libreborn/libreborn.h>
#include "../bootstrap.h"
int main(int argc, char *argv[]) {
// Pre-Bootstrap
pre_bootstrap(argc, argv);
// Set Home To Current Directory, So World Data Is Stored There
char *launch_directory = getcwd(NULL, 0);
set_and_print_env("HOME", launch_directory);
free(launch_directory);
// Bootstrap
bootstrap(argc, argv);
}

View File

@ -3,7 +3,6 @@
#cmakedefine MCPI_SERVER_MODE
#cmakedefine MCPI_HEADLESS_MODE
#cmakedefine MCPI_IS_APPIMAGE_BUILD
#cmakedefine MCPI_IS_FLATPAK_BUILD
#cmakedefine MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
#cmakedefine MCPI_USE_GLES1_COMPATIBILITY_LAYER
#cmakedefine MCPI_APP_BASE_TITLE "@MCPI_APP_BASE_TITLE@"
@ -14,4 +13,3 @@
#cmakedefine MCPI_SDK_DIR "@MCPI_SDK_DIR@"
#cmakedefine MCPI_SKIN_SERVER "@MCPI_SKIN_SERVER@"
#cmakedefine MCPI_USE_QEMU
#cmakedefine MCPI_DISCORD_INVITE "@MCPI_DISCORD_INVITE@"

View File

@ -20,12 +20,9 @@ extern "C" {
void set_and_print_env(const char *name, const char *value);
// Safe execvpe()
#define MCPI_LD_VARIABLE_PREFIX "_MCPI_"
#define MCPI_ORIGINAL_LD_VARIABLE_PREFIX MCPI_LD_VARIABLE_PREFIX "ORIGINAL_"
#define MCPI_ORIGINAL_LD_VARIABLES_PRESERVED_ENV MCPI_ORIGINAL_LD_VARIABLE_PREFIX "PRESERVED"
#define for_each_special_environmental_variable(handle) \
handle("LD_LIBRARY_PATH"); \
handle("LD_PRELOAD")
handle("LD_PRELOAD");
void setup_exec_environment(int is_arm_component);
__attribute__((noreturn)) void safe_execvpe(const char *const argv[], const char *const envp[]);

View File

@ -7,24 +7,14 @@
extern "C" {
#endif
// Log File
#define MCPI_LOG_ENV "_MCPI_LOG"
int reborn_get_log_fd();
void reborn_lock_log();
void reborn_unlock_log();
void reborn_close_log();
void reborn_set_log(const char *file);
// Debug Logging
#define MCPI_DEBUG_ENV "MCPI_DEBUG"
// Debug
extern const char *reborn_debug_tag;
int reborn_get_debug_fd();
void reborn_lock_debug();
void reborn_unlock_debug();
// Logging
#define INFO(format, ...) { fprintf(stderr, "[INFO]: " format "\n", ##__VA_ARGS__); }
#define WARN(format, ...) { fprintf(stderr, "[WARN]: " format "\n", ##__VA_ARGS__); }
#define RAW_DEBUG(tag, format, ...) { reborn_lock_debug(); dprintf(reborn_get_debug_fd(), "[DEBUG]: %s" format "\n", tag, ##__VA_ARGS__); reborn_unlock_debug(); }
#define RAW_DEBUG(tag, format, ...) { int debug_fd = reborn_get_debug_fd(); if (debug_fd != -1) { dprintf(debug_fd, "[DEBUG]: %s" format "\n", tag, ##__VA_ARGS__); } }
#define DEBUG(format, ...) RAW_DEBUG(reborn_debug_tag, format, ##__VA_ARGS__)
#define ERR(format, ...) { fprintf(stderr, "[ERR]: (%s:%i): " format "\n", __FILE__, __LINE__, ##__VA_ARGS__); exit(EXIT_FAILURE); }
#define IMPOSSIBLE() ERR("This Should Never Be Called")

View File

@ -10,74 +10,55 @@ extern "C" {
void reborn_init_patch();
// Replace Call Located At start With A Call To target
void _overwrite_call(const char *file, int line, void *start, void *target);
#define overwrite_call(start, target) _overwrite_call(__FILE__, __LINE__, start, target)
#define _setup_fancy_overwrite(start, name, target) \
if (!_is_new_method_##name()) { \
ERR("Method Is Not \"New\""); \
} \
static name##_t _original_for_##target = start; \
static name##_t _helper_for_##target = __overwrite_helper_for_##name(target, _original_for_##target)
static name##_t _helper_for_##target = _overwrite_helper_for_##name(target, _original_for_##target)
// Replace All Calls To Method start With target
void *_overwrite_calls(const char *file, int line, void *start, void *target);
#define _update_references(from, to) \
{ \
void *old_reference = (void *) from; \
for (int i = 0; _all_method_symbols[i] != nullptr; i++) { \
if (_all_method_symbols[i] == old_reference) { \
_all_method_symbols[i] = (void *) to; \
} \
} \
}
void _overwrite_calls(const char *file, int line, void *start, void *target);
#define overwrite_calls_manual(start, target) _overwrite_calls(__FILE__, __LINE__, start, target)
#define overwrite_calls(start, target) \
{ \
_setup_fancy_overwrite(start, start, target); \
start = (start##_t) overwrite_calls_manual((void *) start, (void *) _helper_for_##target); \
overwrite_calls_manual((void *) start, (void *) _helper_for_##target); \
_update_references(start, _helper_for_##target); \
}
// Replace All Calls To Virtual Method start With target
#define _check_if_method_is_new(name) \
{ \
if (!__is_new_method_##name()) { \
ERR("Method Is Not \"New\""); \
} \
}
#define overwrite_virtual_calls(start, target) \
{ \
_check_if_method_is_new(start); \
_setup_fancy_overwrite(*start##_vtable_addr, start, target); \
overwrite_calls_manual((void *) *start##_vtable_addr, (void *) _helper_for_##target); \
}
// Replace All Calls To start With target Within [to, from)
void _overwrite_calls_within(const char *file, int line, void *from, void *to, void *start, void *target);
#define overwrite_calls_within_manual(from, to, start, target) _overwrite_calls_within(__FILE__, __LINE__, from, to, start, target)
#define overwrite_calls_within(from, to, start, target) \
{ \
start##_t type_check = target; \
overwrite_calls_within_manual(from, to, (void *) start, (void *) type_check); \
}
#define overwrite_calls_within(from, to, start, target) _overwrite_calls_within(__FILE__, __LINE__, from, to, start, target)
// Get Target Address From BL Instruction
void *extract_from_bl_instruction(unsigned char *from);
// Replace Method start With target
void _overwrite(const char *file, int line, void *start, void *target);
#define overwrite_manual(start, target) _overwrite(__FILE__, __LINE__, (void *) start, (void *) target)
#define overwrite(start, target) \
{ \
start##_t type_check = target; \
overwrite_manual((void *) start, (void *) type_check); \
}
#define overwrite(start, target) _overwrite(__FILE__, __LINE__, (void *) start, (void *) target)
// Patch Instruction
void _patch(const char *file, int line, void *start, unsigned char patch[4]);
#define patch(start, patch) _patch(__FILE__, __LINE__, start, patch)
// Patch 4 Bytes Of Data
void _patch_address(const char *file, int line, void *start, void *target);
#define patch_address(start, target) _patch_address(__FILE__, __LINE__, (void *) start, (void *) target)
// Patch VTable Entry
// This does not affect sub-classes.
#define patch_vtable(start, target) \
{ \
start##_t type_check = target; \
patch_address(start##_vtable_addr, (void *) type_check); \
}
#endif
#ifdef __cplusplus

View File

@ -77,7 +77,7 @@ static int _overwrite_calls_within_internal(const char *file, int line, void *fr
#define TEXT_END 0x1020c0
// Overwrite All B(L) Intrusctions That Target The Specified Address
#define NO_CALLSITE_ERROR "(%s:%i) Unable To Find Callsites For %p"
void *_overwrite_calls(const char *file, int line, void *start, void *target) {
void _overwrite_calls(const char *file, int line, void *start, void *target) {
// Add New Target To Code Block
void *code_block = update_code_block(target);
@ -93,9 +93,6 @@ void *_overwrite_calls(const char *file, int line, void *start, void *target) {
if (found < 1) {
ERR(NO_CALLSITE_ERROR, file, line, start);
}
// Return
return code_block;
}
void _overwrite_calls_within(const char *file, int line, void *from /* inclusive */, void *to /* exclusive */, void *target, void *replacement) {
// Add New Target To Code Block

View File

@ -21,14 +21,12 @@ void set_and_print_env(const char *name, const char *value) {
// Safe execvpe()
#define handle_environmental_variable(var) \
{ \
const char *full_var = is_arm_component ? MCPI_LD_VARIABLE_PREFIX var : MCPI_ORIGINAL_LD_VARIABLE_PREFIX var; \
const char *full_var = is_arm_component ? "MCPI_ARM_" var : "MCPI_NATIVE_" var; \
const char *var_value = getenv(full_var); \
set_and_print_env(var, var_value); \
}
void setup_exec_environment(int is_arm_component) {
if (is_arm_component || getenv(MCPI_ORIGINAL_LD_VARIABLES_PRESERVED_ENV) != NULL) {
for_each_special_environmental_variable(handle_environmental_variable);
}
}
__attribute__((noreturn)) void safe_execvpe(const char *const argv[], const char *const envp[]) {
// Log
@ -66,8 +64,13 @@ char *run_command(const char *const command[], int *exit_status, size_t *output_
close(output_pipe[1]);
// Setup stderr
reborn_lock_debug(); // Lock Released On Process Exit
dup2(reborn_get_debug_fd(), STDERR_FILENO);
if (getenv("MCPI_DEBUG") == NULL) {
const char *log_file_fd_env = getenv("MCPI_LOG_FILE_FD");
if (log_file_fd_env == NULL) {
IMPOSSIBLE();
}
dup2(atoi(log_file_fd_env), STDERR_FILENO);
}
// Setup Environment
setup_exec_environment(0);
@ -86,7 +89,7 @@ char *run_command(const char *const command[], int *exit_status, size_t *output_
char buf[BUFFER_SIZE];
size_t position = 0;
ssize_t bytes_read = 0;
while ((bytes_read = read(output_pipe[0], buf, BUFFER_SIZE)) > 0) {
while ((bytes_read = read(output_pipe[0], (void *) buf, BUFFER_SIZE)) > 0) {
// Grow Output If Needed
size_t needed_size = position + bytes_read;
if (needed_size > size) {

View File

@ -1,73 +1,23 @@
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/file.h>
#include <libreborn/log.h>
#include <libreborn/exec.h>
// Debug Tag
const char *reborn_debug_tag = "";
// Log File
static int log_fd = -1;
int reborn_get_log_fd() {
if (log_fd >= 0) {
return log_fd;
}
// Open Log File
const char *file = getenv(MCPI_LOG_ENV);
if (file == NULL) {
file = "/dev/null";
}
log_fd = open(file, O_WRONLY | O_APPEND | O_CLOEXEC);
// Check FD
if (log_fd < 0) {
ERR("Unable To Open Log: %s", strerror(errno));
}
// Return
return reborn_get_log_fd();
}
void reborn_lock_log() {
int ret = flock(reborn_get_log_fd(), LOCK_EX);
if (ret != 0) {
ERR("Unable To Lock Log: %s", strerror(errno));
}
}
void reborn_unlock_log() {
int ret = flock(reborn_get_log_fd(), LOCK_UN);
if (ret != 0) {
ERR("Unable To Unlock Log: %s", strerror(errno));
}
}
__attribute__((destructor)) void reborn_close_log() {
if (log_fd >= 0) {
close(log_fd);
log_fd = -1;
}
}
void reborn_set_log(const char *file) {
// Close Current Log
reborn_close_log();
// Set Variable
set_and_print_env(MCPI_LOG_ENV, file);
}
// Debug Logging
static int should_print_debug_to_stderr() {
return getenv(MCPI_DEBUG_ENV) != NULL;
}
// Debug FD
int reborn_get_debug_fd() {
return should_print_debug_to_stderr() ? STDERR_FILENO : reborn_get_log_fd();
}
void reborn_lock_debug() {
if (!should_print_debug_to_stderr()) {
reborn_lock_log();
}
}
void reborn_unlock_debug() {
if (!should_print_debug_to_stderr()) {
reborn_unlock_log();
if (getenv("MCPI_DEBUG") != NULL) {
return STDERR_FILENO;
} else {
static int debug_fd = -1;
if (debug_fd == -1) {
const char *log_file_fd_env = getenv("MCPI_LOG_FILE_FD");
if (log_file_fd_env == NULL) {
return -1;
}
debug_fd = atoi(log_file_fd_env);
}
return debug_fd;
}
}

View File

@ -33,7 +33,7 @@ if(BUILD_MEDIA_LAYER_CORE)
add_subdirectory(core)
endif()
# Add Trampoline
if(MCPI_USE_MEDIA_LAYER_TRAMPOLINE OR BUILD_ARM_COMPONENTS)
add_subdirectory(trampoline)
# Add Proxy
if(MCPI_USE_MEDIA_LAYER_PROXY OR BUILD_ARM_COMPONENTS)
add_subdirectory(proxy)
endif()

View File

@ -1,10 +1,10 @@
#include <cstdlib>
#include <vector>
#include <SDL/SDL.h>
#include <media-layer/internal.h>
#include <media-layer/core.h>
#include <libreborn/libreborn.h>
// SDL Is Replaced With GLFW

View File

@ -435,7 +435,6 @@ static void glfw_controller_look(float x, float y) {
verify_controller_axis_value(y, CONTROLLER_LOOK_AXIS_THRESHOLD);
// Send Event
if (is_interactable) {
SDL_Event event;
event.type = SDL_MOUSEMOTION;
event.motion.x = last_mouse_x;
@ -444,7 +443,6 @@ static void glfw_controller_look(float x, float y) {
event.motion.yrel = y * CONTROLLER_LOOK_AXIS_SENSITIVITY;
SDL_PushEvent(&event);
}
}
}
// Controller Place/Mine Triggers

View File

@ -4,8 +4,10 @@
extern "C" {
#endif
// Internal Methods
// Internal Methods (Not Handled By Media Layer Proxy)
__attribute__((visibility("internal"))) void _media_handle_SDL_PollEvent();
__attribute__((visibility("internal"))) void _media_handle_SDL_Quit();
#ifdef __cplusplus
}

View File

@ -0,0 +1,27 @@
project(media-layer-proxy)
# Configuration
set(MEDIA_LAYER_PROXY_SRC src/common/common.c src/media-layer-core.c) # Media Layer Proxy Source
if(NOT MCPI_HEADLESS_MODE)
list(APPEND MEDIA_LAYER_PROXY_SRC src/GLESv1_CM.c)
endif()
# Build
if(BUILD_NATIVE_COMPONENTS)
# Build Media Layer Proxy Client
add_executable(media-layer-proxy-client src/client/client.cpp ${MEDIA_LAYER_PROXY_SRC})
target_link_libraries(media-layer-proxy-client media-layer-headers reborn-util media-layer-core GLESv1_CM)
target_compile_definitions(media-layer-proxy-client PRIVATE -DMEDIA_LAYER_PROXY_CLIENT)
# Install
install(TARGETS media-layer-proxy-client DESTINATION "${MCPI_BIN_DIR}")
elseif(BUILD_ARM_COMPONENTS)
# Build Media Layer Proxy Server
add_library(media-layer-core SHARED src/server/server.cpp ${MEDIA_LAYER_PROXY_SRC} $<TARGET_OBJECTS:media-layer-extras>)
target_link_libraries(media-layer-core media-layer-headers reborn-util)
target_compile_definitions(media-layer-core PRIVATE -DMEDIA_LAYER_PROXY_SERVER)
# Install
if(MCPI_USE_MEDIA_LAYER_PROXY)
install(TARGETS media-layer-core DESTINATION "${MCPI_LIB_DIR}")
endif()
install(TARGETS media-layer-core EXPORT sdk DESTINATION "${MCPI_SDK_LIB_DIR}")
endif()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,107 @@
#include <vector>
#include <cerrno>
#include <unistd.h>
#include <cstring>
#include <sys/prctl.h>
#include <csignal>
#include <exception>
#include "../common/common.h"
// Store Handlers
#define MAX_HANDLERS 100
static proxy_handler_t handlers[MAX_HANDLERS];
void _add_handler(unsigned char unique_id, proxy_handler_t handler) {
if (unique_id >= MAX_HANDLERS) {
PROXY_ERR("ID Too Big: %i", (int) unique_id);
}
if (handlers[unique_id] != NULL) {
PROXY_ERR("Duplicate ID: %i", (int) unique_id);
}
handlers[unique_id] = handler;
}
// Store Parent PID
static int parent_is_alive = 1;
static void sigusr1_handler(__attribute__((unused)) int sig) {
// Mark Parent As Dead
parent_is_alive = 0;
}
// Check State Of Proxy And Exit If Invalid
void _check_proxy_state() {
// Check Server State
if (!parent_is_alive) {
void_write_cache(); // Parent Is Dead, No Reason To Send A Dead Process Data
PROXY_ERR("Server Terminated");
}
}
// Exit Handler
static volatile int exit_requested = 0;
static void exit_handler(__attribute__((unused)) int signal_id) {
// Request Exit
exit_requested = 1;
}
// Main
int main(int argc, char *argv[]) {
// Set Debug Tag
reborn_debug_tag = PROXY_LOG_TAG;
// Install Signal Handlers
signal(SIGINT, SIG_IGN);
struct sigaction act_sigterm;
memset((void *) &act_sigterm, 0, sizeof (struct sigaction));
act_sigterm.sa_handler = &exit_handler;
sigaction(SIGTERM, &act_sigterm, NULL);
// Send Signal On Parent Death To Interrupt Connection Read/Write And Exit
prctl(PR_SET_PDEATHSIG, SIGUSR1);
struct sigaction sa;
memset((void *) &sa, 0, sizeof (struct sigaction));
sa.sa_flags = SA_NOCLDSTOP;
sa.sa_handler = &sigusr1_handler;
if (sigaction(SIGUSR1, &sa, NULL) == -1) {
PROXY_ERR("Unable To Install Signal Handler: %s", strerror(errno));
}
// Get Connection
if (argc != 3) {
PROXY_ERR("Invalid Arguments");
}
char *read_str = argv[1];
char *write_str = argv[2];
set_connection(atoi(read_str), atoi(write_str));
PROXY_INFO("Connected");
// Send Connection Message
write_string((char *) CONNECTED_MSG);
flush_write_cache();
// Loop
int running = is_connection_open();
while (running && !exit_requested) {
unsigned char unique_id = read_byte();
if (handlers[unique_id] != NULL) {
// Run Method
handlers[unique_id]();
// Check If Connection Is Still Open
if (!is_connection_open()) {
// Exit
running = 0;
} else {
// Flush Write Cache
flush_write_cache();
}
} else {
PROXY_ERR("Invalid Method ID: %i", (int) unique_id);
}
}
if (is_connection_open()) {
close_connection();
}
// Exit
PROXY_INFO("Stopped");
return 0;
}

View File

@ -0,0 +1,13 @@
#pragma once
#define PROXY_LOG_TAG "(Media Layer Proxy Client) "
typedef void (*proxy_handler_t)();
__attribute__((visibility("internal"))) void _add_handler(unsigned char id, proxy_handler_t handler);
#define CALL(unique_id, name, return_type, args) \
static void _run_##name (); \
__attribute__((constructor)) static void _init_##name() { \
_add_handler(unique_id, _run_##name); \
} \
static void _run_##name ()

View File

@ -0,0 +1,245 @@
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include <sys/ioctl.h>
#include "common.h"
// Safely Send/Receive Data From The Connection
#define CHECK_CONNECTION() \
{ \
_check_proxy_state(); \
if (!is_connection_open()) { \
PROXY_ERR("Attempting To Access Closed Connection"); \
} \
}
// Buffer Reads
static void *_read_cache = NULL;
__attribute__((destructor)) static void _free_read_cache() {
if (_read_cache != NULL) {
free(_read_cache);
}
}
static size_t _read_cache_size = 0;
static size_t _read_cache_actual_size = 0;
static size_t _read_cache_position = 0;
#define max(a, b) (((a) > (b)) ? (a) : (b))
#define min(a, b) (((a) < (b)) ? (a) : (b))
void safe_read(void *buf, size_t len) {
// Check Data
if (buf == NULL) {
PROXY_ERR("Attempting To Read Into NULL Buffer");
}
// Setup
size_t to_read = len;
// Copy From Read Buffer
if (_read_cache != NULL && _read_cache_size > 0) {
char *read_cache = (void *) (((unsigned char *) _read_cache) + _read_cache_position);
size_t read_cache_size = _read_cache_size - _read_cache_position;
if (read_cache_size > 0) {
size_t to_copy = min(to_read, read_cache_size);
memcpy(buf, read_cache, to_copy);
to_read -= to_copy;
_read_cache_position += to_copy;
}
}
// Check If Done
if (to_read < 1) {
return;
}
if (_read_cache_position < _read_cache_size) {
IMPOSSIBLE();
}
// Flush Write Cache
flush_write_cache();
// Read Remaining Data
size_t to_read_to_cache = 0;
while (to_read_to_cache < 1) {
CHECK_CONNECTION();
int bytes_available;
if (ioctl(get_connection_read(), FIONREAD, &bytes_available) == -1) {
bytes_available = 0;
}
to_read_to_cache = max((size_t) bytes_available, to_read);
}
// Resize Buffer
_read_cache_position = 0;
_read_cache_size = to_read_to_cache;
if (_read_cache == NULL) {
_read_cache_actual_size = _read_cache_size;
_read_cache = malloc(_read_cache_actual_size);
} else if (_read_cache_size > _read_cache_actual_size) {
_read_cache_actual_size = _read_cache_size;
_read_cache = realloc(_read_cache, _read_cache_actual_size);
}
ALLOC_CHECK(_read_cache);
// Read Into Buffer
while (to_read_to_cache > 0) {
CHECK_CONNECTION();
ssize_t x = read(get_connection_read(), (void *) (((unsigned char *) _read_cache) + (_read_cache_size - to_read_to_cache)), to_read_to_cache);
if (x == -1 && errno != EINTR) {
PROXY_ERR("Failed Reading Data To Connection: %s", strerror(errno));
}
to_read_to_cache -= x;
}
// Copy Remaining Data
safe_read((void *) (((unsigned char *) buf) + (len - to_read)), to_read);
}
// Buffer Writes
static void *_write_cache = NULL;
__attribute__((destructor)) static void _free_write_cache() {
if (_write_cache != NULL) {
free(_write_cache);
}
}
static size_t _write_cache_size = 0;
static size_t _write_cache_position = 0;
void safe_write(void *buf, size_t len) {
// Check Data
if (buf == NULL) {
PROXY_ERR("Attempting To Send NULL Data");
}
// Expand Write Cache If Needed
size_t needed_size = _write_cache_position + len;
if (_write_cache == NULL) {
_write_cache_size = needed_size;
_write_cache = malloc(_write_cache_size);
} else if (needed_size > _write_cache_size) {
_write_cache_size = needed_size;
_write_cache = realloc(_write_cache, _write_cache_size);
}
ALLOC_CHECK(_write_cache);
// Copy Data
memcpy((void *) (((unsigned char *) _write_cache) + _write_cache_position), buf, len);
// Advance Position
_write_cache_position += len;
}
// Flush Write Cache
void flush_write_cache() {
// Check Cache
if (_write_cache == NULL || _write_cache_position < 1) {
// Nothing To Write
return;
}
// Check Connection
if (!is_connection_open()) {
// Connection Closed
return;
}
// Write & Reset
size_t to_write = _write_cache_position;
size_t old_write_cache_position = _write_cache_position;
_write_cache_position = 0;
while (to_write > 0) {
CHECK_CONNECTION();
ssize_t x = write(get_connection_write(), (void *) (((unsigned char *) _write_cache) + (old_write_cache_position - to_write)), to_write);
if (x == -1 && errno != EINTR) {
PROXY_ERR("Failed Writing Data To Connection: %s", strerror(errno));
}
to_write -= x;
}
}
void void_write_cache() {
_write_cache_position = 0;
}
// Read/Write 32-Bit Integers
uint32_t read_int() {
uint32_t ret = 0;
safe_read((void *) &ret, sizeof (ret));
return ret;
}
void write_int(uint32_t x) {
safe_write((void *) &x, sizeof (x));
}
// Read/Write Floats
float read_float() {
float ret = 0;
safe_read((void *) &ret, sizeof (ret));
return ret;
}
void write_float(float x) {
safe_write((void *) &x, sizeof (x));
}
// Read/Write Bytes
unsigned char read_byte() {
unsigned char ret = 0;
safe_read((void *) &ret, sizeof (ret));
return ret;
}
void write_byte(unsigned char x) {
safe_write((void *) &x, sizeof (x));
}
// Read/Write Strings
char *read_string() {
// Check NULL
unsigned char is_null = read_byte();
if (is_null) {
return NULL;
}
// Allocate String
unsigned char length = read_byte();
char *str = malloc((size_t) length + 1);
// Read String
safe_read((void *) str, length);
// Add Terminator
str[length] = '\0';
// Return String
return strdup(str);
}
#define MAX_STRING_SIZE 256
void write_string(const char *str) {
unsigned char is_null = str == NULL;
write_byte(is_null);
if (!is_null) {
int length = strlen(str);
if (length > MAX_STRING_SIZE) {
PROXY_ERR("Unable To Write String To Connection: Larger Than %i Bytes", MAX_STRING_SIZE);
}
write_byte((unsigned char) length);
safe_write((void *) str, length);
}
}
// Close Connection
void close_connection() {
// Flush Write Cache
flush_write_cache();
// Close
int state_changed = 0;
if (get_connection_read() != -1) {
close(get_connection_read());
state_changed = 1;
}
if (get_connection_write() != -1) {
close(get_connection_write());
state_changed = 1;
}
set_connection(-1, -1);
if (state_changed) {
PROXY_INFO("Connection Closed");
}
}
// Check If Connection Is Open
int is_connection_open() {
return get_connection_read() != -1 && get_connection_write() != -1;
}
// Pipe
static int _read = -1;
static int _write = -1;
// Set Pipe
void set_connection(int read, int write) {
_read = read;
_write = write;
}
// Get Pipe
int get_connection_read() {
return _read;
}
int get_connection_write() {
return _write;
}

View File

@ -0,0 +1,62 @@
#pragma once
#include <stdint.h>
#include <libreborn/libreborn.h>
#ifdef __cplusplus
extern "C" {
#endif
#if __BYTE_ORDER != __LITTLE_ENDIAN
#error "Only Little Endian Is Supported"
#endif
#if defined(MEDIA_LAYER_PROXY_SERVER)
#include "../server/server.h"
#elif defined(MEDIA_LAYER_PROXY_CLIENT)
#include "../client/client.h"
#else
#error "Invalid Configuration"
#endif
#define CONNECTED_MSG "Connected"
#define PROXY_INFO(format, ...) RAW_DEBUG(PROXY_LOG_TAG, format, ##__VA_ARGS__);
#define PROXY_ERR(format, ...) { close_connection(); ERR(PROXY_LOG_TAG format, ##__VA_ARGS__); }
// Safely Send/Receive Data From The Connection
__attribute__((visibility("internal"))) void safe_read(void *buf, size_t len);
__attribute__((visibility("internal"))) void safe_write(void *buf, size_t len);
__attribute__((visibility("internal"))) void flush_write_cache();
__attribute__((visibility("internal"))) void void_write_cache();
// Read/Write 32-Bit Integers
__attribute__((visibility("internal"))) uint32_t read_int();
__attribute__((visibility("internal"))) void write_int(uint32_t x);
// Read/Write Bytes
__attribute__((visibility("internal"))) unsigned char read_byte();
__attribute__((visibility("internal"))) void write_byte(unsigned char x);
// Read/Write Floats
__attribute__((visibility("internal"))) float read_float();
__attribute__((visibility("internal"))) void write_float(float x);
// Read/Write Strings
__attribute__((visibility("internal"))) char *read_string(); // Remember To free()
__attribute__((visibility("internal"))) void write_string(const char *str);
// Manipulate Connection
__attribute__((visibility("internal"))) void set_connection(int read, int write);
__attribute__((visibility("internal"))) int get_connection_read();
__attribute__((visibility("internal"))) int get_connection_write();
__attribute__((visibility("internal"))) void close_connection();
__attribute__((visibility("internal"))) int is_connection_open();
// Check State Of Proxy And Exit If Invalid
__attribute__((visibility("internal"))) void _check_proxy_state();
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,354 @@
#include <stdint.h>
#include <SDL/SDL.h>
#include <libreborn/libreborn.h>
#include <media-layer/core.h>
#include <media-layer/audio.h>
#include <media-layer/internal.h>
#include "common/common.h"
// SDL Functions
CALL(0, SDL_Init, int, (uint32_t flags)) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// Arguments
write_int(flags);
// Get Return Value
int32_t ret = (int32_t) read_int();
// Release Proxy
end_proxy_call();
// Return
return ret;
#else
uint32_t flags = read_int();
// Run
int ret = SDL_Init(flags);
// Return Values
write_int((uint32_t) ret);
#endif
}
CALL(1, SDL_PollEvent, int, (SDL_Event *event)) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// No Arguments
// Get Return Value
int32_t ret = (int32_t) read_int();
if (ret) {
safe_read((void *) event, sizeof (SDL_Event));
}
// Release Proxy
end_proxy_call();
// Return Value
return ret;
#else
SDL_Event event;
// Run
int ret = (int32_t) SDL_PollEvent(&event);
// Return Values
write_int(ret);
if (ret) {
safe_write((void *) &event, sizeof (SDL_Event));
}
#endif
}
CALL(2, SDL_PushEvent, int, (SDL_Event *event)) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// Arguments
safe_write((void *) event, sizeof (SDL_Event));
// Get Return Value
int32_t ret = (int32_t) read_int();
// Release Proxy
end_proxy_call();
// Return Value
return ret;
#else
SDL_Event event;
safe_read((void *) &event, sizeof (SDL_Event));
// Run
int ret = SDL_PushEvent(&event);
// Return Value
write_int((uint32_t) ret);
#endif
}
CALL(3, SDL_WM_SetCaption, void, (const char *title, const char *icon)) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// Arguments
write_string((char *) title);
write_string((char *) icon);
// Release Proxy
end_proxy_call();
#else
char *title = read_string();
char *icon = read_string();
// Run
SDL_WM_SetCaption(title, icon);
// Free
free(title);
free(icon);
#endif
}
CALL(4, media_toggle_fullscreen, void, ()) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// Release Proxy
end_proxy_call();
#else
// Run
media_toggle_fullscreen();
#endif
}
CALL(5, SDL_WM_GrabInput, SDL_GrabMode, (SDL_GrabMode mode)) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// Arguments
write_int((uint32_t) mode);
// Get Return Value
SDL_GrabMode ret = (SDL_GrabMode) read_int();
// Release Proxy
end_proxy_call();
// Return Value
return ret;
#else
SDL_GrabMode mode = (SDL_GrabMode) read_int();
// Run
SDL_GrabMode ret = SDL_WM_GrabInput(mode);
// Return Value
write_int((uint32_t) ret);
#endif
}
CALL(6, SDL_ShowCursor, int, (int32_t toggle)) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// Arguments
write_int((uint32_t) toggle);
// Get Return Value
int32_t ret = (int32_t) read_int();
// Release Proxy
end_proxy_call();
// Return Value
return ret;
#else
int mode = (int) read_int();
// Run
int ret = SDL_ShowCursor(mode);
// Return Value
write_int((uint32_t) ret);
#endif
}
CALL(8, media_swap_buffers, void, ()) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// Release Proxy
end_proxy_call();
flush_write_cache();
#else
// Run
media_swap_buffers();
#endif
}
// This Method May Be Called In A Situation Where The Proxy Is Disconnected
CALL(9, media_cleanup, void, ()) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Check Connection
if (is_connection_open()) {
// Lock Proxy
start_proxy_call();
// Block Until Cleanup Is Complete
flush_write_cache();
read_byte();
// Close The Connection
close_connection();
// Release Proxy
end_proxy_call();
}
#else
// Run
media_cleanup();
// Confirm Cleanup
write_byte(0);
// Close The Connection
close_connection();
#endif
}
CALL(10, media_get_framebuffer_size, void, (int *width, int *height)) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// Get Return Values
*width = (int) read_int();
*height = (int) read_int();
// Release Proxy
end_proxy_call();
#else
int width;
int height;
// Run
media_get_framebuffer_size(&width, &height);
// Return Values
write_int((uint32_t) width);
write_int((uint32_t) height);
#endif
}
CALL(59, media_audio_update, void, (float volume, float x, float y, float z, float yaw)) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// Arguments
write_float(volume);
write_float(x);
write_float(y);
write_float(z);
write_float(yaw);
// Release Proxy
end_proxy_call();
#else
float volume = read_float();
float x = read_float();
float y = read_float();
float z = read_float();
float yaw = read_float();
// Run
media_audio_update(volume, x, y, z, yaw);
#endif
}
CALL(60, media_audio_play, void, (const char *source, const char *name, float x, float y, float z, float pitch, float volume, int is_ui)) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// Arguments
write_string(source);
write_string(name);
write_float(x);
write_float(y);
write_float(z);
write_float(pitch);
write_float(volume);
write_int(is_ui);
// Release Proxy
end_proxy_call();
#else
char *source = read_string();
char *name = read_string();
float x = read_float();
float y = read_float();
float z = read_float();
float pitch = read_float();
float volume = read_float();
int is_ui = read_int();
// Run
media_audio_play(source, name, x, y, z, pitch, volume, is_ui);
// Free
free(source);
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
}
CALL(64, media_set_raw_mouse_motion_enabled, void, (int enabled)) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// Arguments
write_int(enabled);
// Release Proxy
end_proxy_call();
#else
int enabled = read_int();
// Run
media_set_raw_mouse_motion_enabled(enabled);
#endif
}
CALL(66, media_force_egl, void, ()) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// Release Proxy
end_proxy_call();
#else
// Run
media_force_egl();
#endif
}

View File

@ -0,0 +1,170 @@
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include <csignal>
#include <sys/wait.h>
#include <fcntl.h>
#include <string>
#include <unordered_map>
#include <fstream>
#include <media-layer/core.h>
#include "../common/common.h"
// Track Client State
static int _client_is_alive = 0;
static int _client_status = 0;
static void update_client_state(int is_alive, int status) {
_client_is_alive = is_alive;
_client_status = status;
}
// Check State Of Proxy And Exit If Invalid
void _check_proxy_state() {
// Check Client State
if (!_client_is_alive) {
void_write_cache(); // Child Is Dead, No Reason To Send A Dead Process Data
char *exit_status = NULL;
get_exit_status_string(_client_status, &exit_status);
PROXY_ERR("Client Terminated%s", exit_status);
}
}
// Start Proxy Client
static pid_t _client_pid;
static void sigchld_handler(__attribute__((unused)) int sig) {
// Track
int status;
// Reap
int saved_errno = errno;
// Only waitpid() Proxy Client, Other Sub-Processes Are Handled By pclose()
if (waitpid(_client_pid, &status, WNOHANG) == _client_pid) {
// Handle Client Death
untrack_child(_client_pid);
update_client_state(0, status);
}
errno = saved_errno;
}
static void start_media_layer_proxy_client(int read, int write) {
// Reap Children
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_NOCLDSTOP;
sa.sa_handler = &sigchld_handler;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
PROXY_ERR("Unable To Install Signal Handler: %s", strerror(errno));
}
// Fork And Start
pid_t ret = fork();
if (ret == -1) {
PROXY_ERR("Unable To Launch Client: %s", strerror(errno));
} else if (ret == 0) {
// Child Process
// Set Debug Tag
reborn_debug_tag = CHILD_PROCESS_TAG;
// Prepare Arguments
char *read_str = NULL;
safe_asprintf(&read_str, "%i", read);
char *write_str = NULL;
safe_asprintf(&write_str, "%i", write);
const char *argv[] = {"media-layer-proxy-client", read_str, write_str, NULL};
// Setup Environment
setup_exec_environment(0);
// Run
safe_execvpe(argv, (const char *const *) environ);
} else {
// Parent Process
_client_pid = ret;
track_child(_client_pid);
}
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");
}
// Close
max_size_file.close();
// 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
static int loaded = 0;
__attribute__((constructor)) void media_ensure_loaded() {
if (!loaded) {
loaded = 1;
// Log
PROXY_INFO("Starting...");
// 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]);
// 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("Connected");
} else {
PROXY_ERR("Unable To Connect");
}
// Free
free(str);
}
}
// Assign Unique ID To Function
static std::unordered_map<std::string, unsigned char> &get_unique_ids() {
static std::unordered_map<std::string, unsigned char> unique_ids;
return unique_ids;
}
void _assign_unique_id(const char *name, unsigned char id) {
get_unique_ids()[name] = id;
}
unsigned char _get_unique_id(const char *name) {
return get_unique_ids()[name]; // Assume ID Exists
}
// Proxy Call Functions
void _start_proxy_call(unsigned char call_id) {
// Start Call
write_byte(call_id);
}
void end_proxy_call() {
}

View File

@ -0,0 +1,27 @@
#pragma once
#define PROXY_LOG_TAG "(Media Layer Proxy Server) "
// Assign Unique ID To Function
__attribute__((visibility("internal"))) void _assign_unique_id(const char *name, unsigned char id);
__attribute__((visibility("internal"))) unsigned char _get_unique_id(const char *name);
// Must Call After Every Call
__attribute__((visibility("internal"))) void _start_proxy_call(unsigned char call_id);
#define start_proxy_call() \
{ \
static int _loaded_id = 0; \
static unsigned char _call_id; \
if (!_loaded_id) { \
_loaded_id = 1; \
_call_id = _get_unique_id(__func__); \
} \
_start_proxy_call(_call_id); \
}
__attribute__((visibility("internal"))) void end_proxy_call();
#define CALL(unique_id, name, return_type, args) \
__attribute__((constructor)) static void _init_##name() { \
_assign_unique_id(#name, unique_id); \
} \
return_type name args

View File

@ -1,30 +0,0 @@
project(media-layer-trampoline)
# Configuration
set(MEDIA_LAYER_TRAMPOLINE_SRC src/media-layer-core.c) # Media Layer Trampoline Source
if(NOT MCPI_HEADLESS_MODE)
list(APPEND MEDIA_LAYER_TRAMPOLINE_SRC src/GLESv1_CM.c)
endif()
# Build
if(BUILD_NATIVE_COMPONENTS)
# Host Component
add_library(media-layer-trampoline src/host/host.c ${MEDIA_LAYER_TRAMPOLINE_SRC})
target_link_libraries(media-layer-trampoline reborn-util media-layer-core)
if(NOT MCPI_HEADLESS_MODE)
target_link_libraries(media-layer-trampoline GLESv1_CM)
endif()
target_compile_definitions(media-layer-trampoline PRIVATE -DMEDIA_LAYER_TRAMPOLINE_HOST)
# Install
install(TARGETS media-layer-trampoline DESTINATION "${MCPI_LIB_DIR}")
elseif(BUILD_ARM_COMPONENTS)
# Guest Component
add_library(media-layer-core SHARED src/guest/guest.c ${MEDIA_LAYER_TRAMPOLINE_SRC} $<TARGET_OBJECTS:media-layer-extras>)
target_link_libraries(media-layer-core media-layer-headers reborn-util)
target_compile_definitions(media-layer-core PRIVATE -DMEDIA_LAYER_TRAMPOLINE_GUEST)
# Install
if(MCPI_USE_MEDIA_LAYER_TRAMPOLINE)
install(TARGETS media-layer-core DESTINATION "${MCPI_LIB_DIR}")
endif()
install(TARGETS media-layer-core EXPORT sdk DESTINATION "${MCPI_SDK_LIB_DIR}")
endif()

View File

@ -1,581 +0,0 @@
#include <stdint.h>
#include <GLES/gl.h>
#include <libreborn/libreborn.h>
#include "common/common.h"
CALL(11, glFogfv, void, (GLenum pname, const GLfloat *params))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(pname, (uint32_t) params);
#else
GLenum pname = next_int();
GLfloat *params = next_ptr();
// Run
func(pname, params);
#endif
}
// 'pointer' Is Only Supported As An Integer, Not As An Actual Pointer
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
#define CALL_GL_POINTER(unique_id, name) \
CALL(unique_id, name, void, (GLint size, GLenum type, GLsizei stride, const void *pointer)) \
trampoline(size, type, stride, (uint32_t) pointer); \
}
#else
#define CALL_GL_POINTER(unique_id, name) \
CALL(unique_id, name, unused, unused) \
GLint size = next_int(); \
GLenum type = next_int(); \
GLsizei stride = next_int(); \
const void *pointer = (const void *) (uint64_t) next_int(); \
/* Run */ \
func(size, type, stride, pointer); \
}
#endif
CALL_GL_POINTER(12, glVertexPointer)
CALL(13, glLineWidth, void, (GLfloat width))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(pun_to(uint32_t, width));
#else
GLfloat width = next_float();
// Run
func(width);
#endif
}
CALL(14, glBlendFunc, void, (GLenum sfactor, GLenum dfactor))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(sfactor, dfactor);
#else
GLenum sfactor = next_int();
GLenum dfactor = next_int();
// Run
func(sfactor, dfactor);
#endif
}
CALL(15, glDrawArrays, void, (GLenum mode, GLint first, GLsizei count))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(mode, first, count);
#else
GLenum mode = next_int();
GLint first = next_int();
GLsizei count = next_int();
// Run
func(mode, first, count);
#endif
}
CALL(16, glColor4f, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(pun_to(uint32_t, red), pun_to(uint32_t, green), pun_to(uint32_t, blue), pun_to(uint32_t, alpha));
#else
GLfloat red = next_float();
GLfloat green = next_float();
GLfloat blue = next_float();
GLfloat alpha = next_float();
// Run
func(red, green, blue, alpha);
#endif
}
CALL(17, glClear, void, (GLbitfield mask))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(mask);
#else
GLbitfield mask = next_int();
// Run
func(mask);
#endif
}
CALL(18, glBufferData, void, (GLenum target, GLsizeiptr size, const void *data, GLenum usage))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(target, size, (uint32_t) data, usage);
#else
GLenum target = next_int();
GLsizeiptr size = next_int();
const void *data = next_ptr();
GLenum usage = next_int();
// Run
func(target, size, data, usage);
#endif
}
CALL(19, glFogx, void, (GLenum pname, GLfixed param))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(pname, param);
#else
GLenum pname = next_int();
GLfixed param = next_int();
// Run
func(pname, param);
#endif
}
CALL(20, glFogf, void, (GLenum pname, GLfloat param))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(pname, pun_to(uint32_t, param));
#else
GLenum pname = next_int();
GLfloat param = next_float();
// Run
func(pname, param);
#endif
}
CALL(21, glMatrixMode, void, (GLenum mode))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(mode);
#else
GLenum mode = next_int();
// Run
func(mode);
#endif
}
CALL_GL_POINTER(22, glColorPointer)
CALL(23, glScissor, void, (GLint x, GLint y, GLsizei width, GLsizei height))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(x, y, width, height);
#else
GLint x = next_int();
GLint y = next_int();
GLsizei width = next_int();
GLsizei height = next_int();
// Run
func(x, y, width, height);
#endif
}
CALL(24, glTexParameteri, void, (GLenum target, GLenum pname, GLint param))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(target, pname, param);
#else
GLenum target = next_int();
GLenum pname = next_int();
GLint param = next_int();
// Run
func(target, pname, param);
#endif
}
CALL(25, glTexImage2D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(target, level, internalformat, width, height, border, format, type, (uint32_t) pixels);
#else
GLenum target = next_int();
GLint level = next_int();
GLint internalformat = next_int();
GLsizei width = next_int();
GLsizei height = next_int();
GLint border = next_int();
GLenum format = next_int();
GLenum type = next_int();
const void *pixels = next_ptr();
// Run
func(target, level, internalformat, width, height, border, format, type, pixels);
#endif
}
CALL(26, glEnable, void, (GLenum cap))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(cap);
#else
GLenum cap = next_int();
// Run
func(cap);
#endif
}
CALL(27, glEnableClientState, void, (GLenum array))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(array);
#else
GLenum array = next_int();
// Run
func(array);
#endif
}
CALL(28, glPolygonOffset, void, (GLfloat factor, GLfloat units))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(pun_to(uint32_t, factor), pun_to(uint32_t, units));
#else
GLfloat factor = next_float();
GLfloat units = next_float();
// Run
func(factor, units);
#endif
}
CALL_GL_POINTER(41, glTexCoordPointer)
CALL(29, glDisableClientState, void, (GLenum array))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(array);
#else
GLenum array = next_int();
// Run
func(array);
#endif
}
CALL(30, glDepthRangef, void, (GLclampf near, GLclampf far))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(pun_to(uint32_t, near), pun_to(uint32_t, far));
#else
GLclampf near = next_float();
GLclampf far = next_float();
// Run
func(near, far);
#endif
}
CALL(31, glDepthFunc, void, (GLenum func))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(func);
#else
GLenum func2 = next_int();
// Run
func(func2);
#endif
}
CALL(32, glBindBuffer, void, (GLenum target, GLuint buffer))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(target, buffer);
#else
GLenum target = next_int();
GLenum buffer = next_int();
// Run
func(target, buffer);
#endif
}
CALL(33, glClearColor, void, (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(pun_to(uint32_t, red), pun_to(uint32_t, green), pun_to(uint32_t, blue), pun_to(uint32_t, alpha));
#else
GLclampf red = next_float();
GLclampf green = next_float();
GLclampf blue = next_float();
GLclampf alpha = next_float();
// Run
func(red, green, blue, alpha);
#endif
}
CALL(34, glPopMatrix, void, ())
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline();
#else
// Run
func();
#endif
}
CALL(35, glLoadIdentity, void, ())
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline();
#else
// Run
func();
#endif
}
CALL(36, glScalef, void, (GLfloat x, GLfloat y, GLfloat z))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(pun_to(uint32_t, x), pun_to(uint32_t, y), pun_to(uint32_t, z));
#else
GLfloat x = next_float();
GLfloat y = next_float();
GLfloat z = next_float();
// Run
func(x, y, z);
#endif
}
CALL(37, glPushMatrix, void, ())
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline();
#else
// Run
func();
#endif
}
CALL(38, glDepthMask, void, (GLboolean flag))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(flag);
#else
GLboolean flag = next_int();
// Run
func(flag);
#endif
}
CALL(39, glHint, void, (GLenum target, GLenum mode))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(target, mode);
#else
GLenum target = next_int();
GLenum mode = next_int();
// Run
func(target, mode);
#endif
}
CALL(40, glMultMatrixf, void, (const GLfloat *m))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline((uint32_t) m);
#else
GLfloat *m = next_ptr();
// Run
func(m);
#endif
}
CALL(42, glDeleteBuffers, void, (GLsizei n, const GLuint *buffers))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(n, (uint32_t) buffers);
#else
GLsizei n = next_int();
GLuint *buffers = next_ptr();
// Run
func(n, buffers);
#endif
}
CALL(43, glColorMask, void, (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(red, green, blue, alpha);
#else
GLboolean red = next_int();
GLboolean green = next_int();
GLboolean blue = next_int();
GLboolean alpha = next_int();
// Run
func(red, green, blue, alpha);
#endif
}
CALL(44, glTexSubImage2D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(target, level, xoffset, yoffset, width, height, format, type, (uint32_t) pixels);
#else
GLenum target = next_int();
GLint level = next_int();
GLint xoffset = next_int();
GLint yoffset = next_int();
GLsizei width = next_int();
GLsizei height = next_int();
GLenum format = next_int();
GLenum type = next_int();
const void *pixels = next_ptr();
// Run
func(target, level, xoffset, yoffset, width, height, format, type, pixels);
#endif
}
CALL(45, glGenTextures, void, (GLsizei n, GLuint *textures))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(n, (uint32_t) textures);
#else
GLsizei n = next_int();
GLuint *textures = next_ptr();
// Run
func(n, textures);
#endif
}
CALL(46, glDeleteTextures, void, (GLsizei n, const GLuint *textures))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(n, (uint32_t) textures);
#else
GLsizei n = next_int();
GLuint *textures = next_ptr();
// Run
func(n, textures);
#endif
}
CALL(47, glAlphaFunc, void, (GLenum func, GLclampf ref))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(func, pun_to(uint32_t, ref));
#else
GLenum func2 = next_int();
GLclampf ref = next_float();
// Run
func(func2, ref);
#endif
}
CALL(48, glGetFloatv, void, (GLenum pname, GLfloat *params))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(pname, (uint32_t) params);
#else
GLenum pname = next_int();
GLfloat *params = next_ptr();
// Run
func(pname, params);
#endif
}
CALL(49, glBindTexture, void, (GLenum target, GLuint texture))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(target, texture);
#else
GLenum target = next_int();
GLuint texture = next_int();
// Run
func(target, texture);
#endif
}
CALL(50, glTranslatef, void, (GLfloat x, GLfloat y, GLfloat z))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(pun_to(uint32_t, x), pun_to(uint32_t, y), pun_to(uint32_t, z));
#else
GLfloat x = next_float();
GLfloat y = next_float();
GLfloat z = next_float();
// Run
func(x, y, z);
#endif
}
CALL(51, glShadeModel, void, (GLenum mode))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(mode);
#else
GLenum mode = next_int();
// Run
func(mode);
#endif
}
CALL(52, glOrthof, void, (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat near, GLfloat far))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(pun_to(uint32_t, left), pun_to(uint32_t, right), pun_to(uint32_t, bottom), pun_to(uint32_t, top), pun_to(uint32_t, near), pun_to(uint32_t, far));
#else
GLfloat left = next_float();
GLfloat right = next_float();
GLfloat bottom = next_float();
GLfloat top = next_float();
GLfloat near = next_float();
GLfloat far = next_float();
// Run
func(left, right, bottom, top, near, far);
#endif
}
CALL(53, glDisable, void, (GLenum cap))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(cap);
#else
GLenum cap = next_int();
// Run
func(cap);
#endif
}
CALL(54, glCullFace, void, (GLenum mode))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(mode);
#else
GLenum mode = next_int();
// Run
func(mode);
#endif
}
CALL(55, glRotatef, void, (GLfloat angle, GLfloat x, GLfloat y, GLfloat z))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(pun_to(uint32_t, angle), pun_to(uint32_t, x), pun_to(uint32_t, y), pun_to(uint32_t, z));
#else
GLfloat angle = next_float();
GLfloat x = next_float();
GLfloat y = next_float();
GLfloat z = next_float();
// Run
func(angle, x, y, z);
#endif
}
CALL(56, glViewport, void, (GLint x, GLint y, GLsizei width, GLsizei height))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(x, y, width, height);
#else
GLint x = next_int();
GLint y = next_int();
GLsizei width = next_int();
GLsizei height = next_int();
// Run
func(x, y, width, height);
#endif
}
CALL(57, glNormal3f, void, (GLfloat nx, GLfloat ny, GLfloat nz))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(pun_to(uint32_t, nx), pun_to(uint32_t, ny), pun_to(uint32_t, nz));
#else
GLfloat nx = next_float();
GLfloat ny = next_float();
GLfloat nz = next_float();
// Run
func(nx, ny, nz);
#endif
}
CALL(58, glIsEnabled, GLboolean, (GLenum cap))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
return trampoline(cap);
#else
GLenum cap = next_int();
// Run
ret(func(cap));
#endif
}
CALL(61, glGetIntegerv, void, (GLenum pname, GLint *params))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(pname, (uint32_t) params);
#else
GLenum pname = next_int();
GLint *params = next_ptr();
// Run
func(pname, params);
#endif
}
CALL(65, glReadPixels, void, (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *data))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(x, y, width, height, format, type, (uint32_t) data);
#else
GLint x = next_int();
GLint y = next_int();
GLsizei width = next_int();
GLsizei height = next_int();
GLenum format = next_int();
GLenum type = next_int();
void *data = next_ptr();
// Run
func(x, y, width, height, format, type, data);
#endif
}
CALL(67, glGenBuffers, void, (GLsizei n, GLuint *buffers))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(n, (uint32_t) buffers);
#else
GLsizei n = next_int();
GLuint *buffers = next_ptr();
// Run
func(n, buffers);
#endif
}

View File

@ -1,21 +0,0 @@
#pragma once
#if __BYTE_ORDER != __LITTLE_ENDIAN
#error "Only Little Endian Is Supported"
#endif
#if defined(MEDIA_LAYER_TRAMPOLINE_HOST)
#include "../host/host.h"
#elif defined(MEDIA_LAYER_TRAMPOLINE_GUEST)
#include "../guest/guest.h"
#else
#error "Invalid Configuration"
#endif
//#define pun_to(type, x) (*(type *) &(x))
#define pun_to(type, x) \
({ \
union { typeof(x) a; type b; } _pun; \
_pun.a = x; \
_pun.b; \
})

View File

@ -1,15 +0,0 @@
#include <unistd.h>
#include <libreborn/libreborn.h>
#include "guest.h"
uint32_t _trampoline(uint32_t id, uint32_t *args) {
// Make Syscall
long ret = syscall(0x1337 /* See trampoline.patch */, id, args);
if (ret == -1) {
// Error
ERR("Trampoline Error: %s", strerror(errno));
}
// Return
return args[0];
}

View File

@ -1,12 +0,0 @@
#pragma once
#include <stdint.h>
// Trampoline Function
uint32_t _trampoline(uint32_t id, uint32_t *args);
#define trampoline(...) _trampoline(_id, (uint32_t[]){__VA_ARGS__})
// Macro
#define CALL(unique_id, name, return_type, args) \
return_type name args { \
static unsigned char _id = unique_id;

View File

@ -1,17 +0,0 @@
#include <libreborn/libreborn.h>
#include "host.h"
// Registration
static handler_t *handlers[256];
void _add_handler(unsigned char id, handler_t *handler) {
if (handlers[id]) {
ERR("Conflicting Trampolines For ID: %i", (int) id);
}
handlers[id] = handler;
}
// Trampoline
void trampoline(g2h_t g2h, uint32_t id, uint32_t *args) {
handlers[id](g2h, args);
}

View File

@ -1,27 +0,0 @@
#pragma once
#include <stdint.h>
// Trampoline Function
typedef void *(*g2h_t)(uint32_t guest_addr);
void trampoline(g2h_t g2h, uint32_t id, uint32_t *args); // See trampoline.patch
// Macro
typedef void handler_t(g2h_t g2h, uint32_t *args);
__attribute__((visibility("internal"))) void _add_handler(unsigned char id, handler_t *handler);
#define CALL(unique_id, name, ignored1, ignored2) \
static handler_t _run_##name; \
__attribute__((constructor)) static void _init_##name() { \
_add_handler(unique_id, _run_##name); \
} \
static void _run_##name(__attribute__((unused)) g2h_t g2h, __attribute__((unused)) uint32_t *args) { \
__attribute__((unused)) int _current_arg = 0; \
static typeof(name) *func = name;
// Helper Macros
#define next_int() args[_current_arg++]
#define next_ptr() g2h(next_int())
#define next_float() pun_to(float, next_int())
#define ret(x) \
args[0] = x; \
return;

View File

@ -1,188 +0,0 @@
#include <stdint.h>
#include <SDL/SDL.h>
#include <libreborn/libreborn.h>
#include <media-layer/core.h>
#include <media-layer/audio.h>
#include "common/common.h"
// SDL Functions
CALL(0, SDL_Init, int, (uint32_t flags))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
return trampoline(flags);
#else
uint32_t flags = next_int();
// Run
ret(func(flags));
#endif
}
CALL(1, SDL_PollEvent, int, (SDL_Event *event))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
return trampoline((uint32_t) event);
#else
SDL_Event *event = next_ptr();
// Run
ret(func(event));
#endif
}
CALL(2, SDL_PushEvent, int, (SDL_Event *event))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
return trampoline((uint32_t) event);
#else
SDL_Event *event = next_ptr();
// Run
ret(func(event));
#endif
}
CALL(3, SDL_WM_SetCaption, void, (const char *title, const char *icon))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline((uint32_t) title, (uint32_t) icon);
#else
char *title = next_ptr();
char *icon = next_ptr();
// Run
func(title, icon);
#endif
}
CALL(4, media_toggle_fullscreen, void, ())
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline();
#else
// Run
func();
#endif
}
CALL(5, SDL_WM_GrabInput, SDL_GrabMode, (SDL_GrabMode mode))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
return trampoline(mode);
#else
SDL_GrabMode mode = next_int();
// Run
ret(func(mode));
#endif
}
CALL(6, SDL_ShowCursor, int, (int32_t toggle))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
return trampoline(toggle);
#else
int mode = next_int();
// Run
ret(func(mode));
#endif
}
CALL(8, media_swap_buffers, void, ())
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline();
#else
// Run
func();
#endif
}
CALL(9, media_cleanup, void, ())
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline();
#else
// Run
func();
#endif
}
CALL(10, media_get_framebuffer_size, void, (int *width, int *height))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline((uint32_t) width, (uint32_t) height);
#else
int *width = next_ptr();
int *height = next_ptr();
// Run
func(width, height);
#endif
}
CALL(59, media_audio_update, void, (float volume, float x, float y, float z, float yaw))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(pun_to(uint32_t, volume), pun_to(uint32_t, x), pun_to(uint32_t, y), pun_to(uint32_t, z), pun_to(uint32_t, yaw));
#else
float volume = next_float();
float x = next_float();
float y = next_float();
float z = next_float();
float yaw = next_float();
// Run
func(volume, x, y, z, yaw);
#endif
}
CALL(60, media_audio_play, void, (const char *source, const char *name, float x, float y, float z, float pitch, float volume, int is_ui))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline((uint32_t) source, (uint32_t) name, pun_to(uint32_t, x), pun_to(uint32_t, y), pun_to(uint32_t, z), pun_to(uint32_t, pitch), pun_to(uint32_t, volume), is_ui);
#else
char *source = next_ptr();
char *name = next_ptr();
float x = next_float();
float y = next_float();
float z = next_float();
float pitch = next_float();
float volume = next_float();
int is_ui = next_int();
// Run
func(source, name, x, y, z, pitch, volume, is_ui);
#endif
}
CALL(62, media_set_interactable, void, (int is_interactable))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(is_interactable);
#else
int is_interactable = next_int();
// Run
func(is_interactable);
#endif
}
CALL(63, media_disable_vsync, void, ())
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline();
#else
// Run
func();
#endif
}
CALL(64, media_set_raw_mouse_motion_enabled, void, (int enabled))
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline(enabled);
#else
int enabled = next_int();
// Run
func(enabled);
#endif
}
CALL(66, media_force_egl, void, ())
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline();
#else
// Run
func();
#endif
}
CALL(68, media_ensure_loaded, void, ())
#ifdef MEDIA_LAYER_TRAMPOLINE_GUEST
trampoline();
#else
// Run
func();
#endif
}

View File

@ -31,19 +31,12 @@ set(SRC
src/misc/api.cpp
# options
src/options/options.cpp
src/options/ui.cpp
src/options/info.cpp
# bucket
src/bucket/bucket.cpp
# cake
src/cake/cake.cpp
# home
src/home/home.cpp
# touch
src/touch/touch.cpp
# text-input-box
src/text-input-box/TextInputBox.cpp
src/text-input-box/TextInputScreen.cpp
# test
src/test/test.cpp
# init
@ -89,6 +82,8 @@ else()
src/input/crafting.cpp
# sign
src/sign/sign.cpp
# touch
src/touch/touch.cpp
# atlas
src/atlas/atlas.cpp
# title-screen
@ -102,6 +97,9 @@ else()
# textures
src/textures/textures.cpp
src/textures/lava.cpp
# text-input-box
src/text-input-box/TextInputBox.cpp
src/text-input-box/TextInputScreen.cpp
# fps
src/fps/fps.cpp
)

View File

@ -1,5 +1,7 @@
#pragma once
#include <libreborn/libreborn.h>
extern "C" {
void run_tests();
void init_version();
@ -8,19 +10,19 @@ void init_compat();
void init_server();
#else
void init_multiplayer();
void init_benchmark();
void init_benchmark(int argc, char *argv[]);
#endif
#ifndef MCPI_HEADLESS_MODE
void init_sound();
void init_input();
void init_sign();
void init_camera();
void init_touch();
void init_atlas();
void init_title_screen();
void init_skin();
void init_fps();
#endif
void init_touch();
void init_textures();
void init_creative();
void init_game_mode();

View File

@ -6,7 +6,6 @@
extern "C" {
int32_t misc_get_real_selected_slot(Player *player);
void misc_render_background(int color, Minecraft *minecraft, int x, int y, int width, int height);
typedef void (*misc_update_function_Minecraft_t)(Minecraft *obj);
void misc_run_on_update(misc_update_function_Minecraft_t function); // obj == Minecraft *

View File

@ -8,17 +8,17 @@
// Fix Grass And Leaves Inventory Rendering When The gui_blocks Atlas Is Disabled
static void ItemRenderer_renderGuiItemCorrect_injection(ItemRenderer_renderGuiItemCorrect_t original, Font *font, Textures *textures, ItemInstance *item_instance, int32_t param_1, int32_t param_2) {
int32_t leaves_id = Tile::leaves->id;
int32_t grass_id = Tile::grass->id;
int32_t leaves_id = Tile_leaves->id;
int32_t grass_id = Tile_grass->id;
// Replace Rendered Item With Carried Variant
ItemInstance carried_item_instance;
bool use_carried = false;
if (item_instance != nullptr) {
if (item_instance->id == leaves_id) {
carried_item_instance.constructor_tile_extra(Tile::leaves_carried, item_instance->count, item_instance->auxiliary);
ItemInstance_constructor_tile_extra(&carried_item_instance, Tile_leaves_carried, item_instance->count, item_instance->auxiliary);
use_carried = true;
} else if (item_instance->id == grass_id) {
carried_item_instance.constructor_tile_extra(Tile::grass_carried, item_instance->count, item_instance->auxiliary);
ItemInstance_constructor_tile_extra(&carried_item_instance, Tile_grass_carried, item_instance->count, item_instance->auxiliary);
use_carried = true;
}
}
@ -64,12 +64,12 @@ static void Tesselator_begin_injection(Tesselator_begin_t original, Tesselator *
// Fix Furnace UI
if (item_color_fix_mode != 0) {
// Implict Translucent
tesselator->color(0xff, 0xff, 0xff, 0xff);
Tesselator_color(tesselator, 0xff, 0xff, 0xff, 0xff);
}
}
static void InventoryPane_renderBatch_Tesselator_color_injection(Tesselator *tesselator, int32_t r, int32_t g, int32_t b) {
// Call Original Method
tesselator->color(r, g, b, 0xff);
Tesselator_color(tesselator, r, g, b, 0xff);
// Enable Item Color Fix
item_color_fix_mode = 2;
@ -86,7 +86,7 @@ static void FurnaceScreen_render_ItemRenderer_renderGuiItem_one_injection(Font *
item_color_fix_mode = 1;
// Call Original Method
ItemRenderer::renderGuiItem_one(font, textures, item_instance, param_1, param_2, param_3);
ItemRenderer_renderGuiItem_one(font, textures, item_instance, param_1, param_2, param_3);
}
// Init

View File

@ -32,18 +32,18 @@ static void start_world(Minecraft *minecraft) {
settings.seed = BENCHMARK_SEED;
// Delete World If It Already Exists
LevelStorageSource *level_source = minecraft->getLevelSource();
LevelStorageSource *level_source = Minecraft_getLevelSource(minecraft);
std::string name = BENCHMARK_WORLD_NAME;
level_source->deleteLevel(&name);
level_source->vtable->deleteLevel(level_source, &name);
// Select Level
minecraft->selectLevel(&name, &name, &settings);
minecraft->vtable->selectLevel(minecraft, &name, &name, &settings);
// Open ProgressScreen
ProgressScreen *screen = new ProgressScreen;
ProgressScreen *screen = alloc_ProgressScreen();
ALLOC_CHECK(screen);
screen = screen->constructor();
minecraft->setScreen((Screen *) screen);
screen = ProgressScreen_constructor(screen);
Minecraft_setScreen(minecraft, (Screen *) screen);
}
// Track Frames
@ -93,7 +93,7 @@ static void Minecraft_update_injection(Minecraft *minecraft) {
}
// Detect World Loaded
if (!world_loaded && minecraft->isLevelGenerated()) {
if (!world_loaded && Minecraft_isLevelGenerated(minecraft)) {
world_loaded = 1;
world_loaded_time = get_time();
#ifndef MCPI_HEADLESS_MODE
@ -158,9 +158,17 @@ static void Minecraft_update_injection(Minecraft *minecraft) {
}
// Init Benchmark
void init_benchmark() {
void init_benchmark(int argc, char *argv[]) {
// --benchmark: Activate Benchmark
bool active = getenv("_MCPI_BENCHMARK") != nullptr;
bool active = false;
for (int i = 1; i < argc; i++) {
// Check Argument
if (strcmp(argv[i], "--benchmark") == 0) {
// Enabled
active = true;
break;
}
}
if (active) {
misc_run_on_update(Minecraft_update_injection);
// Track Ticks

View File

@ -11,9 +11,9 @@ static FoodItem *bucket = nullptr;
// Description And Texture
static std::string BucketItem_getDescriptionId(__attribute__((unused)) FoodItem *item, ItemInstance *item_instance) {
if (item_instance->auxiliary == Tile::water->id) {
if (item_instance->auxiliary == Tile_water->id) {
return "item.bucketWater";
} else if (item_instance->auxiliary == Tile::lava->id) {
} else if (item_instance->auxiliary == Tile_lava->id) {
return "item.bucketLava";
} else if (item_instance->auxiliary == 1) {
return "item.bucketMilk";
@ -22,9 +22,9 @@ static std::string BucketItem_getDescriptionId(__attribute__((unused)) FoodItem
}
}
static int32_t BucketItem_getIcon(__attribute__((unused)) FoodItem *item, int32_t auxiliary) {
if (auxiliary == Tile::water->id) {
if (auxiliary == Tile_water->id) {
return 75;
} else if (auxiliary == Tile::lava->id) {
} else if (auxiliary == Tile_lava->id) {
return 76;
} else if (auxiliary == 1) {
return 77;
@ -45,7 +45,7 @@ static bool fill_bucket(ItemInstance *item_instance, Player *player, int new_aux
new_item.count = 1;
new_item.auxiliary = new_auxiliary;
Inventory *inventory = player->inventory;
if (inventory->add(&new_item)) {
if (inventory->vtable->add(inventory, &new_item)) {
// Added To Inventory
success = true;
item_instance->count -= 1;
@ -62,16 +62,16 @@ static int32_t BucketItem_useOn(__attribute__((unused)) FoodItem *item, ItemInst
} else if (item_instance->auxiliary == 0) {
// Empty Bucket
int32_t new_auxiliary = 0;
int32_t tile = level->getTile(x, y, z);
if (tile == Tile::calmWater->id) {
new_auxiliary = Tile::water->id;
} else if (tile == Tile::calmLava->id) {
new_auxiliary = Tile::lava->id;
int32_t tile = level->vtable->getTile(level, x, y, z);
if (tile == Tile_calmWater->id) {
new_auxiliary = Tile_water->id;
} else if (tile == Tile_calmLava->id) {
new_auxiliary = Tile_lava->id;
}
if (new_auxiliary != 0) {
// Valid
if (fill_bucket(item_instance, player, new_auxiliary)) {
level->setTileAndData(x, y, z, 0, 0);
Level_setTileAndData(level, x, y, z, 0, 0);
return 1;
} else {
return 0;
@ -110,15 +110,15 @@ static int32_t BucketItem_useOn(__attribute__((unused)) FoodItem *item, ItemInst
}
// Get Current Tile
bool valid = false;
Material *material = level->getMaterial(x, y, z);
Material *material = level->vtable->getMaterial(level, x, y, z);
if (material != nullptr) {
valid = !material->isSolid();
valid = !material->vtable->isSolid(material);
}
if (item_instance->auxiliary != Tile::water->id && item_instance->auxiliary != Tile::lava->id) {
if (item_instance->auxiliary != Tile_water->id && item_instance->auxiliary != Tile_lava->id) {
valid = false;
}
if (valid) {
level->setTileAndData(x, y, z, item_instance->auxiliary, 0);
Level_setTileAndData(level, x, y, z, item_instance->auxiliary, 0);
item_instance->auxiliary = 0;
return 1;
} else {
@ -163,7 +163,7 @@ static ItemInstance *BucketItem_getCraftingRemainingItem(FoodItem *item, ItemIns
if (item_instance->auxiliary == 0) {
return nullptr;
}
ItemInstance *ret = new ItemInstance;
ItemInstance *ret = alloc_ItemInstance();
ret->id = item->id;
ret->count = item_instance->count;
ret->auxiliary = 0;
@ -186,16 +186,16 @@ CUSTOM_VTABLE(bucket, FoodItem) {
// Create Items
static FoodItem *create_bucket(int32_t id, int32_t texture_x, int32_t texture_y, std::string name) {
// Construct
FoodItem *item = new FoodItem;
FoodItem *item = alloc_FoodItem();
ALLOC_CHECK(item);
Item_constructor((Item *) item, id); // FoodItem's Constructor Was Inlined
Item_constructor((Item *) item, id);
// Set VTable
item->vtable = get_bucket_vtable();
// Setup
item->setIcon(texture_x, texture_y);
item->setDescriptionId(&name);
item->vtable->setIcon(item, texture_x, texture_y);
item->vtable->setDescriptionId(item, &name);
item->is_stacked_by_data = 1;
item->category = 2;
item->max_damage = 0;
@ -224,7 +224,7 @@ static int32_t ItemInstance_getMaxStackSize_injection(ItemInstance_getMaxStackSi
// Milking
bool Cow_interact_injection(Cow_interact_t original, Cow *self, Player *player) {
ItemInstance *item = player->inventory->getSelected();
ItemInstance *item = Inventory_getSelected(player->inventory);
if (item && item->id == bucket->id && item->auxiliary == 0) {
// Fill with milk
fill_bucket(item, player, 1);
@ -237,13 +237,13 @@ bool Cow_interact_injection(Cow_interact_t original, Cow *self, Player *player)
static void inventory_add_item(FillingContainer *inventory, FoodItem *item, int32_t auxiliary) {
ItemInstance *item_instance = new ItemInstance;
ALLOC_CHECK(item_instance);
item_instance = item_instance->constructor_item_extra((Item *) item, 1, auxiliary);
inventory->addItem(item_instance);
item_instance = ItemInstance_constructor_item_extra(item_instance, (Item *) item, 1, auxiliary);
FillingContainer_addItem(inventory, item_instance);
}
static void Inventory_setupDefault_FillingContainer_addItem_call_injection(FillingContainer *filling_container) {
inventory_add_item(filling_container, bucket, 0);
inventory_add_item(filling_container, bucket, Tile::water->id);
inventory_add_item(filling_container, bucket, Tile::lava->id);
inventory_add_item(filling_container, bucket, Tile_water->id);
inventory_add_item(filling_container, bucket, Tile_lava->id);
inventory_add_item(filling_container, bucket, 1);
}
@ -251,7 +251,7 @@ static void Inventory_setupDefault_FillingContainer_addItem_call_injection(Filli
static bool is_holding_bucket = false;
static HitResult Mob_pick_Level_clip_injection(Level *level, unsigned char *param_1, unsigned char *param_2, __attribute__((unused)) bool clip_liquids, bool param_3) {
// Call Original Method
return level->clip(param_1, param_2, is_holding_bucket, param_3);
return Level_clip(level, param_1, param_2, is_holding_bucket, param_3);
}
static void handle_tick(Minecraft *minecraft) {
LocalPlayer *player = minecraft->player;
@ -261,7 +261,7 @@ static void handle_tick(Minecraft *minecraft) {
Inventory *inventory = player->inventory;
// Get Item
ItemInstance *inventory_item = inventory->getItem(selected_slot);
ItemInstance *inventory_item = inventory->vtable->getItem(inventory, selected_slot);
// Check
is_holding_bucket = inventory_item != nullptr && inventory_item->id == bucket->id && inventory_item->auxiliary == 0;
}
@ -269,9 +269,9 @@ static void handle_tick(Minecraft *minecraft) {
// Prevent Breaking Liquid
static bool is_calm_liquid(int32_t id) {
if (id == Tile::calmWater->id) {
if (id == Tile_calmWater->id) {
return true;
} else if (id == Tile::calmLava->id) {
} else if (id == Tile_calmLava->id) {
return true;
} else {
return false;
@ -284,7 +284,7 @@ static void Minecraft_handleMouseDown_injection(Minecraft_handleMouseDown_t orig
int32_t x = minecraft->hit_result.x;
int32_t y = minecraft->hit_result.y;
int32_t z = minecraft->hit_result.z;
int32_t tile = level->getTile(x, y, z);
int32_t tile = level->vtable->getTile(level, x, y, z);
if (is_calm_liquid(tile)) {
can_destroy = false;
}
@ -315,12 +315,12 @@ static void Recipes_injection(Recipes *recipes) {
std::string line1 = "# #";
std::string line2 = " # ";
std::vector<Recipes_Type> types = {type1};
recipes->addShapedRecipe_2(&result, &line1, &line2, &types);
Recipes_addShapedRecipe_2(recipes, &result, &line1, &line2, &types);
}
// Custom Furnace Fuel
static int32_t FurnaceTileEntity_getBurnDuration_injection(FurnaceTileEntity_getBurnDuration_t original, ItemInstance *item_instance) {
if (item_instance->count > 0 && item_instance->id == bucket->id && item_instance->auxiliary == Tile::lava->id) {
if (item_instance->count > 0 && item_instance->id == bucket->id && item_instance->auxiliary == Tile_lava->id) {
return 20000;
} else {
// Call Original Method
@ -341,7 +341,7 @@ static void FurnaceTileEntity_tick_ItemInstance_setNull_injection(ItemInstance *
// Add the bucket name to the language file
static void Language_injection(__attribute__((unused)) void *null) {
I18n::_strings.insert(std::make_pair("item.bucketMilk.name", "Milk Bucket"));
I18n__strings.insert(std::make_pair("item.bucketMilk.name", "Milk Bucket"));
}
// Init

View File

@ -31,7 +31,7 @@ static int Cake_getTexture2(__attribute__((unused)) Tile *tile, int face, __attr
static int Cake_getTexture3(__attribute__((unused)) Tile *tile, LevelSource *level, int x, int y, int z, int face) {
// Eaten face
if (face == 3) {
int data = level->getData(x, y, z);
int data = level->vtable->getData(level, x, y, z);
if (data != 0 && data < 6) {
// Sliced texture
return 123;
@ -59,7 +59,8 @@ static bool Cake_isCubeShaped(__attribute__((unused)) Tile *tile) {
// Size
static void Cake_updateDefaultShape(Tile *tile) {
// Set the default shape
tile->setShape(
tile->vtable->setShape(
tile,
CAKE_LEN, 0.0, CAKE_LEN,
1.0 - CAKE_LEN, 0.5, 1.0 - CAKE_LEN
);
@ -67,7 +68,7 @@ static void Cake_updateDefaultShape(Tile *tile) {
static AABB *Cake_getAABB(Tile *tile, Level *level, int x, int y, int z) {
// Get the size of the slices
int data = level->getData(x, y, z);
int data = level->vtable->getData(level, x, y, z);
if (data >= 6) data = 0;
float slice_size = (1.0 / 7.0) * (float) data;
@ -87,11 +88,12 @@ static AABB *Cake_getAABB(Tile *tile, Level *level, int x, int y, int z) {
static void Cake_updateShape(Tile *tile, LevelSource *level, int x, int y, int z) {
// Set cake
int data = level->getData(x, y, z);
int data = level->vtable->getData(level, x, y, z);
if (data >= 6) data = 0;
// Get slice amount
float slice_size = (1.0 / 7.0) * (float) data;
tile->setShape(
tile->vtable->setShape(
tile,
CAKE_LEN, 0.0, CAKE_LEN,
1.0 - CAKE_LEN, 0.5, (1.0 - CAKE_LEN) - slice_size
);
@ -100,15 +102,15 @@ static void Cake_updateShape(Tile *tile, LevelSource *level, int x, int y, int z
// Eating
static int Cake_use(__attribute__((unused)) Tile *tile, Level *level, int x, int y, int z, Player *player) {
// Eat
player->foodData.eat(3);
SimpleFoodData_eat(&player->foodData, 3);
// Set the new tile
int data = level->getData(x, y, z);
int data = level->vtable->getData(level, x, y, z);
if (data >= 5) {
// Remove the cake, it has been completely gobbled up
level->setTileAndData(x, y, z, 0, 0);
Level_setTileAndData(level, x, y, z, 0, 0);
} else {
// Remove a slice
level->setTileAndData(x, y, z, 92, data + 1);
Level_setTileAndData(level, x, y, z, 92, data + 1);
}
return 1;
}
@ -116,10 +118,10 @@ static int Cake_use(__attribute__((unused)) Tile *tile, Level *level, int x, int
// Makes the cakes
static void make_cake() {
// Construct
cake = new Tile;
cake = alloc_Tile();
ALLOC_CHECK(cake);
int texture = 122;
cake->constructor(92, texture, Material::dirt);
Tile_constructor(cake, 92, texture, Material_dirt);
cake->texture = texture;
// Set VTable
@ -127,7 +129,8 @@ static void make_cake() {
ALLOC_CHECK(cake->vtable);
// Set shape
cake->setShape(
cake->vtable->setShape(
cake,
CAKE_LEN, 0.0, CAKE_LEN,
1.0 - CAKE_LEN, 0.5, 1.0 - CAKE_LEN
);
@ -145,12 +148,12 @@ static void make_cake() {
cake->vtable->use = Cake_use;
// Init
cake->init();
cake->setDestroyTime(1.0f);
cake->setExplodeable(20.0f);
Tile_init(cake);
cake->vtable->setDestroyTime(cake, 1.0f);
cake->vtable->setExplodeable(cake, 20.0f);
cake->category = 4;
std::string name = "Cake";
cake->setDescriptionId(&name);
cake->vtable->setDescriptionId(cake, &name);
}
static void Tile_initTiles_injection(__attribute__((unused)) void *null) {
@ -164,7 +167,7 @@ static void Inventory_setupDefault_FillingContainer_addItem_call_injection(Filli
cake_instance->count = 255;
cake_instance->auxiliary = 0;
cake_instance->id = 92;
filling_container->addItem(cake_instance);
FillingContainer_addItem(filling_container, cake_instance);
}
// Recipe (only when buckets are enabled)
@ -224,7 +227,7 @@ static void Recipes_injection(Recipes *recipes) {
std::string line2 = "ses";
std::string line3 = "www";
std::vector<Recipes_Type> ingredients = {milk, sugar, wheat, eggs};
recipes->addShapedRecipe_3(&cake_item, &line1, &line2, &line3, &ingredients);
Recipes_addShapedRecipe_3(recipes, &cake_item, &line1, &line2, &line3, &ingredients);
}
void init_cake() {

View File

@ -19,10 +19,10 @@ static EntityRenderDispatcher *EntityRenderDispatcher_injection(EntityRenderDisp
original(dispatcher);
// Register TripodCameraRenderer
TripodCameraRenderer *renderer = new TripodCameraRenderer;
TripodCameraRenderer *renderer = alloc_TripodCameraRenderer();
ALLOC_CHECK(renderer);
renderer->constructor();
dispatcher->assign((unsigned char) 0x5, (EntityRenderer *) renderer);
TripodCameraRenderer_constructor(renderer);
EntityRenderDispatcher_assign(dispatcher, (unsigned char) 0x5, (EntityRenderer *) renderer);
return dispatcher;
}
@ -30,7 +30,7 @@ static EntityRenderDispatcher *EntityRenderDispatcher_injection(EntityRenderDisp
// Display Smoke From TripodCamera Higher
static void TripodCamera_tick_Level_addParticle_call_injection(Level *level, std::string *particle, float x, float y, float z, float deltaX, float deltaY, float deltaZ, int count) {
// Call Original Method
level->addParticle(particle, x, y + 0.5, z, deltaX, deltaY, deltaZ, count);
Level_addParticle(level, particle, x, y + 0.5, z, deltaX, deltaY, deltaZ, count);
}
// Init

View File

@ -2,7 +2,7 @@
#include <string>
#include <symbols/minecraft.h>
#include <libreborn/libreborn.h>
// Message Limitations
#define MAX_CHAT_MESSAGE_LENGTH 256
@ -11,7 +11,11 @@
__attribute__((visibility("internal"))) std::string _chat_get_prefix(char *username);
// Queue Message For Sending
__attribute__((visibility("internal"))) void _chat_send_message(Minecraft *minecraft, const char *message);
#ifndef MCPI_SERVER_MODE
__attribute__((visibility("internal"))) void _chat_queue_message(const char *message);
#endif
// Init Chat UI
#ifndef MCPI_HEADLESS_MODE
__attribute__((visibility("internal"))) void _init_chat_ui();
#endif

View File

@ -1,13 +1,21 @@
// Config Needs To Load First
#include <libreborn/libreborn.h>
#include <string>
#include <cstring>
#include <cstdio>
#include <vector>
#include <libreborn/libreborn.h>
#include <symbols/minecraft.h>
#ifndef MCPI_HEADLESS_MODE
#include <media-layer/core.h>
#endif
#include <mods/init/init.h>
#include <mods/feature/feature.h>
#ifndef MCPI_HEADLESS_MODE
#include <mods/input/input.h>
#endif
#include "chat-internal.h"
#include <mods/chat/chat.h>
@ -19,12 +27,13 @@ std::string chat_send_api_command(Minecraft *minecraft, std::string str) {
client.time = 0;
CommandServer *command_server = minecraft->command_server;
if (command_server != nullptr) {
return command_server->parse(&client, &str);
return CommandServer_parse(command_server, &client, &str);
} else {
return "";
}
}
#ifndef MCPI_HEADLESS_MODE
// Send API Chat Command
static void send_api_chat_command(Minecraft *minecraft, char *str) {
char *command = nullptr;
@ -32,6 +41,7 @@ static void send_api_chat_command(Minecraft *minecraft, char *str) {
chat_send_api_command(minecraft, command);
free(command);
}
#endif
// Send Message To Players
std::string _chat_get_prefix(char *username) {
@ -43,19 +53,19 @@ void chat_send_message(ServerSideNetworkHandler *server_side_network_handler, ch
sanitize_string(&full_message, MAX_CHAT_MESSAGE_LENGTH, 0);
std::string cpp_string = full_message;
free(full_message);
server_side_network_handler->displayGameMessage(&cpp_string);
ServerSideNetworkHandler_displayGameMessage(server_side_network_handler, &cpp_string);
}
// Handle Chat packet Send
void chat_handle_packet_send(Minecraft *minecraft, ChatPacket *packet) {
RakNetInstance *rak_net_instance = minecraft->rak_net_instance;
if (rak_net_instance->isServer()) {
if (rak_net_instance->vtable->isServer(rak_net_instance)) {
// Hosting Multiplayer
const char *message = packet->message.c_str();
ServerSideNetworkHandler *server_side_network_handler = (ServerSideNetworkHandler *) minecraft->network_handler;
chat_send_message(server_side_network_handler, Strings::default_username, (char *) message);
chat_send_message(server_side_network_handler, Strings_default_username, (char *) message);
} else {
// Client
rak_net_instance->send((Packet *) packet);
rak_net_instance->vtable->send(rak_net_instance, (Packet *) packet);
}
}
@ -69,7 +79,7 @@ static void CommandServer_parse_CommandServer_dispatchPacket_injection(CommandSe
// Handle ChatPacket Server-Side
static void ServerSideNetworkHandler_handle_ChatPacket_injection(ServerSideNetworkHandler *server_side_network_handler, RakNet_RakNetGUID *rak_net_guid, ChatPacket *chat_packet) {
Player *player = server_side_network_handler->getPlayer(rak_net_guid);
Player *player = ServerSideNetworkHandler_getPlayer(server_side_network_handler, rak_net_guid);
if (player != nullptr) {
const char *username = player->username.c_str();
const char *message = chat_packet->message.c_str();
@ -77,10 +87,25 @@ static void ServerSideNetworkHandler_handle_ChatPacket_injection(ServerSideNetwo
}
}
// Send Message
void _chat_send_message(Minecraft *minecraft, const char *message) {
send_api_chat_command(minecraft, (char *) message);
#ifndef MCPI_HEADLESS_MODE
// Message Queue
static std::vector<std::string> queue;
// Add To Queue
void _chat_queue_message(const char *message) {
// Add
std::string str = message;
queue.push_back(str);
}
// Empty Queue
unsigned int old_chat_counter = 0;
static void send_queued_messages(Minecraft *minecraft) {
// Loop
for (unsigned int i = 0; i < queue.size(); i++) {
send_api_chat_command(minecraft, (char *) queue[i].c_str());
}
queue.clear();
}
#endif
// Init
void init_chat() {
@ -91,9 +116,13 @@ void init_chat() {
// Manually Send (And Loopback) ChatPacket
overwrite_call((void *) 0x6b518, (void *) CommandServer_parse_CommandServer_dispatchPacket_injection);
// Re-Broadcast ChatPacket
patch_vtable(ServerSideNetworkHandler_handle_ChatPacket, ServerSideNetworkHandler_handle_ChatPacket_injection);
patch_address(ServerSideNetworkHandler_handle_ChatPacket_vtable_addr, ServerSideNetworkHandler_handle_ChatPacket_injection);
#ifndef MCPI_HEADLESS_MODE
// Send Messages On Input Tick
input_run_on_tick(send_queued_messages);
// Init UI
_init_chat_ui();
#endif
// Disable Built-In Chat Message Limiting
unsigned char message_limit_patch[4] = {0x03, 0x00, 0x53, 0xe1}; // "cmp r4, r4"
patch((void *) 0x6b4c0, message_limit_patch);

View File

@ -1,5 +1,10 @@
#include "chat-internal.h"
// Config Needs To Load First
#include <libreborn/libreborn.h>
// Chat UI Code Is Useless In Headless Mode
#ifndef MCPI_HEADLESS_MODE
#include "chat-internal.h"
#include <mods/chat/chat.h>
#include <mods/text-input-box/TextInputScreen.h>
#include <mods/misc/misc.h>
@ -34,7 +39,7 @@ CUSTOM_VTABLE(chat_screen, Screen) {
local_history = get_history();
local_history.push_back("");
// Determine Max Length
std::string prefix = _chat_get_prefix(Strings::default_username);
std::string prefix = _chat_get_prefix(Strings_default_username);
int max_length = MAX_CHAT_MESSAGE_LENGTH - prefix.length();
self->chat->setMaxLength(max_length);
// Send Button
@ -51,15 +56,15 @@ CUSTOM_VTABLE(chat_screen, Screen) {
is_in_chat = false;
ChatScreen *self = (ChatScreen *) super;
delete self->chat;
self->send->destructor_deleting();
self->send->vtable->destructor_deleting(self->send);
};
// Rendering
static Screen_render_t original_render = vtable->render;
vtable->render = [](Screen *super, int x, int y, float param_1) {
// Background
super->renderBackground();
super->vtable->renderBackground(super);
// Render Chat
super->minecraft->gui.renderChatMessages(super->height, 20, true, super->font);
Gui_renderChatMessages(&super->minecraft->gui, super->height, 20, true, super->font);
// Call Original Method
original_render(super, x, y, param_1);
};
@ -89,9 +94,9 @@ CUSTOM_VTABLE(chat_screen, Screen) {
if (get_history().size() == 0 || text != get_history().back()) {
get_history().push_back(text);
}
_chat_send_message(super->minecraft, text.c_str());
_chat_queue_message(text.c_str());
}
super->minecraft->setScreen(nullptr);
Minecraft_setScreen(super->minecraft, nullptr);
} else if (key == 0x26) {
// Up
local_history.at(self->history_pos) = self->chat->getText();
@ -120,7 +125,7 @@ CUSTOM_VTABLE(chat_screen, Screen) {
if (button == self->send) {
// Send
self->chat->setFocused(true);
super->keyPressed(0x0d);
super->vtable->keyPressed(super, 0x0d);
} else {
// Call Original Method
original_buttonClicked(super, button);
@ -131,7 +136,7 @@ static Screen *create_chat_screen() {
// Construct
ChatScreen *screen = new ChatScreen;
ALLOC_CHECK(screen);
screen->super.super.constructor();
Screen_constructor(&screen->super.super);
// Set VTable
screen->super.super.vtable = get_chat_screen_vtable();
@ -144,8 +149,8 @@ static Screen *create_chat_screen() {
void _init_chat_ui() {
misc_run_on_game_key_press([](Minecraft *minecraft, int key) {
if (key == 0x54) {
if (minecraft->isLevelGenerated() && minecraft->screen == nullptr) {
minecraft->setScreen(create_chat_screen());
if (Minecraft_isLevelGenerated(minecraft) && minecraft->screen == nullptr) {
Minecraft_setScreen(minecraft, create_chat_screen());
}
return true;
} else {
@ -153,3 +158,5 @@ void _init_chat_ui() {
}
});
}
#endif

View File

@ -11,23 +11,23 @@
static void inventory_add_item(FillingContainer *inventory, Item *item) {
ItemInstance *item_instance = new ItemInstance;
ALLOC_CHECK(item_instance);
item_instance = item_instance->constructor_item(item);
inventory->addItem(item_instance);
item_instance = ItemInstance_constructor_item(item_instance, item);
FillingContainer_addItem(inventory, item_instance);
}
static void inventory_add_item(FillingContainer *inventory, Tile *item) {
ItemInstance *item_instance = new ItemInstance;
ALLOC_CHECK(item_instance);
item_instance = item_instance->constructor_tile(item);
inventory->addItem(item_instance);
item_instance = ItemInstance_constructor_tile(item_instance, item);
FillingContainer_addItem(inventory, item_instance);
}
// Expand Creative Inventory
static void Inventory_setupDefault_FillingContainer_addItem_call_injection(FillingContainer *filling_container) {
// Add Items
inventory_add_item(filling_container, Item::flintAndSteel);
inventory_add_item(filling_container, Item::snowball);
inventory_add_item(filling_container, Item::egg);
inventory_add_item(filling_container, Item::shears);
inventory_add_item(filling_container, Item_flintAndSteel);
inventory_add_item(filling_container, Item_snowball);
inventory_add_item(filling_container, Item_egg);
inventory_add_item(filling_container, Item_shears);
// Dyes
for (int i = 0; i < 16; i++) {
if (i == 15) {
@ -36,23 +36,23 @@ static void Inventory_setupDefault_FillingContainer_addItem_call_injection(Filli
}
ItemInstance *new_item_instance = new ItemInstance;
ALLOC_CHECK(new_item_instance);
new_item_instance = new_item_instance->constructor_item_extra(Item::dye_powder, 1, i);
filling_container->addItem(new_item_instance);
new_item_instance = ItemInstance_constructor_item_extra(new_item_instance, Item_dye_powder, 1, i);
FillingContainer_addItem(filling_container, new_item_instance);
}
inventory_add_item(filling_container, Item::camera);
inventory_add_item(filling_container, Item_camera);
// Add Tiles
inventory_add_item(filling_container, Tile::water);
inventory_add_item(filling_container, Tile::lava);
inventory_add_item(filling_container, Tile::calmWater);
inventory_add_item(filling_container, Tile::calmLava);
inventory_add_item(filling_container, Tile::glowingObsidian);
inventory_add_item(filling_container, Tile::web);
inventory_add_item(filling_container, Tile::topSnow);
inventory_add_item(filling_container, Tile::ice);
inventory_add_item(filling_container, Tile::invisible_bedrock);
inventory_add_item(filling_container, Tile::bedrock);
inventory_add_item(filling_container, Tile::info_updateGame1);
inventory_add_item(filling_container, Tile::info_updateGame2);
inventory_add_item(filling_container, Tile_water);
inventory_add_item(filling_container, Tile_lava);
inventory_add_item(filling_container, Tile_calmWater);
inventory_add_item(filling_container, Tile_calmLava);
inventory_add_item(filling_container, Tile_glowingObsidian);
inventory_add_item(filling_container, Tile_web);
inventory_add_item(filling_container, Tile_topSnow);
inventory_add_item(filling_container, Tile_ice);
inventory_add_item(filling_container, Tile_invisible_bedrock);
inventory_add_item(filling_container, Tile_bedrock);
inventory_add_item(filling_container, Tile_info_updateGame1);
inventory_add_item(filling_container, Tile_info_updateGame2);
// Nether Reactor
for (int i = 0; i < 3; i++) {
if (i == 0) {
@ -61,8 +61,8 @@ static void Inventory_setupDefault_FillingContainer_addItem_call_injection(Filli
}
ItemInstance *new_item_instance = new ItemInstance;
ALLOC_CHECK(new_item_instance);
new_item_instance = new_item_instance->constructor_tile_extra(Tile::netherReactor, 1, i);
filling_container->addItem(new_item_instance);
new_item_instance = ItemInstance_constructor_tile_extra(new_item_instance, Tile_netherReactor, 1, i);
FillingContainer_addItem(filling_container, new_item_instance);
}
// Tall Grass
for (int i = 0; i < 4; i++) {
@ -72,15 +72,15 @@ static void Inventory_setupDefault_FillingContainer_addItem_call_injection(Filli
}
ItemInstance *new_item_instance = new ItemInstance;
ALLOC_CHECK(new_item_instance);
new_item_instance = new_item_instance->constructor_tile_extra(Tile::tallgrass, 1, i);
filling_container->addItem(new_item_instance);
new_item_instance = ItemInstance_constructor_tile_extra(new_item_instance, Tile_tallgrass, 1, i);
FillingContainer_addItem(filling_container, new_item_instance);
}
// Smooth Stone Slab
{
ItemInstance *new_item_instance = new ItemInstance;
ALLOC_CHECK(new_item_instance);
new_item_instance = new_item_instance->constructor_tile_extra(Tile::stoneSlab, 1, 6);
filling_container->addItem(new_item_instance);
new_item_instance = ItemInstance_constructor_tile_extra(new_item_instance, Tile_stoneSlab, 1, 6);
FillingContainer_addItem(filling_container, new_item_instance);
}
}
#endif
@ -88,14 +88,14 @@ static void Inventory_setupDefault_FillingContainer_addItem_call_injection(Filli
// Hook Specific TileItem Constructor
static TileItem *Tile_initTiles_TileItem_injection(TileItem *tile_item, int32_t id) {
// Call Original Method
tile_item->constructor(id);
TileItem_constructor(tile_item, id);
// Switch VTable
tile_item->vtable = (TileItem_vtable *) AuxDataTileItem_vtable_base;
// Configure Item
tile_item->is_stacked_by_data = true;
tile_item->max_damage = 0;
((AuxDataTileItem *) tile_item)->icon_tile = Tile::tiles[id + 0x100];
((AuxDataTileItem *) tile_item)->icon_tile = Tile_tiles[id + 0x100];
// Return
return tile_item;
@ -119,7 +119,7 @@ void init_creative() {
// Inventory can have arbitrary auxiliary values.
{
// Fix Size
unsigned char size_patch[4] = {sizeof(AuxDataTileItem), 0x00, 0xa0, 0xe3}; // "mov r0, #AUX_DATA_TILE_ITEM_SIZE"
unsigned char size_patch[4] = {AUX_DATA_TILE_ITEM_SIZE, 0x00, 0xa0, 0xe3}; // "mov r0, #AUX_DATA_TILE_ITEM_SIZE"
patch((void *) 0xc6f64, size_patch);
// Hook Constructor
overwrite_call((void *) 0xc6f74, (void *) Tile_initTiles_TileItem_injection);
@ -127,49 +127,38 @@ void init_creative() {
}
// Remove Creative Mode Restrictions (Opening Chests, Crafting, Etc)
unsigned char nop_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop"
if (feature_has("Remove Creative Mode Restrictions", server_enabled)) {
unsigned char nop_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop"
// Remove Restrictions
patch((void *) 0x43ee8, nop_patch);
patch((void *) 0x43f3c, nop_patch);
patch((void *) 0x43f8c, nop_patch);
patch((void *) 0x43fd8, nop_patch);
patch((void *) 0x99010, nop_patch);
// Fix UI
patch((void *) 0x341c0, nop_patch);
patch((void *) 0x3adb4, nop_patch);
patch((void *) 0x3b374, nop_patch);
// Fix Inventory
patch((void *) 0x8d080, nop_patch);
patch((void *) 0x8d090, nop_patch);
patch((void *) 0x91d48, nop_patch);
patch((void *) 0x92098, nop_patch);
unsigned char inv_creative_check_r3_patch[4] = {0x03, 0x00, 0x53, 0xe1}; // "cmp r3, r3"
patch((void *) 0x923c0, inv_creative_check_r3_patch);
patch((void *) 0x92828, nop_patch);
patch((void *) 0x92830, nop_patch);
// Display Slot Count
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 *) 0x33954, 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);
// Allow Nether Reactor
patch((void *) 0xc0290, nop_patch);
// Disable Other Restrictions
is_restricted = 0;
}
// Inventory Behavior
if (feature_has("Force Survival Mode Inventory Behavior", server_enabled)) {
patch((void *) 0x8d080, nop_patch); // Inventory::add
patch((void *) 0x92828, nop_patch); // FillingContainer::add
patch((void *) 0x91d48, nop_patch); // FillingContainer::hasResource
patch((void *) 0x92098, nop_patch); // FillingContainer::removeResource(int)
unsigned char inv_creative_check_r3_patch[4] = {0x03, 0x00, 0x53, 0xe1}; // "cmp r3, r3"
patch((void *) 0x923c0, inv_creative_check_r3_patch); // FillingContainer::removeResource(ItemInstance const&, bool)
}
// "Craft" And "Armor" Buttons
if (feature_has("Force Survival Mode Inventory UI", server_enabled)) {
patch((void *) 0x341c0, nop_patch); // Add "Armor" Button To Classic Inventory
unsigned char inv_creative_check_r5_patch[4] = {0x05, 0x00, 0x55, 0xe1}; // "cmp r5, r5"
patch((void *) 0x3adb0, inv_creative_check_r5_patch); // Reposition "Select blocks" In Touch Inventory
patch((void *) 0x3b374, nop_patch); // Add "Armor" And "Craft" Buttons To Touch Inventory
}
// Display Slot Count
if (feature_has("Display Slot Count In Creative Mode", server_enabled)) {
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 *) 0x33954, nop_patch);
}
// Maximize Creative Inventory Stack Size
if (feature_has("Maximize Creative Mode Inventory Stack Size", server_enabled)) {
unsigned char maximize_stack_patch[4] = {0xff, 0xc0, 0xa0, 0xe3}; // "mov r12, 0xff"
patch((void *) 0x8e104, maximize_stack_patch);
}
}

View File

@ -13,10 +13,10 @@ std::string get_death_message(Player *player, Entity *cause, bool was_shot = fal
std::string message = player->username;
if (cause) {
// Entity cause
int type_id = cause->getEntityTypeId();
int aux = cause->getAuxData();
bool is_player = cause->isPlayer();
if (cause->getCreatureBaseType() != 0 || is_player) {
int type_id = cause->vtable->getEntityTypeId(cause);
int aux = cause->vtable->getAuxData(cause);
bool is_player = cause->vtable->isPlayer(cause);
if (cause->vtable->getCreatureBaseType(cause) != 0 || is_player) {
// Killed by a creature
if (was_shot) {
message += " was shot by ";
@ -38,12 +38,12 @@ std::string get_death_message(Player *player, Entity *cause, bool was_shot = fal
} else if (aux) {
// Killed by a throwable with owner
Level *level = player->level;
Entity *shooter = level->getEntity(aux);
Entity *shooter = Level_getEntity(level, aux);
return get_death_message(player, shooter, true);
} else if (type_id == 65) {
// Blown up by TNT
return message + " was blown apart";
} else if (cause->isHangingEntity()) {
} else if (cause->vtable->isHangingEntity(cause)) {
// Painting?
return message + " admired too much art";
}
@ -73,21 +73,22 @@ static bool Mob_hurt_injection(Mob_hurt_t original, Mob *mob, Entity *source, in
}
// Death Message Logic
#define Player_die_injections(type, original_method_self) \
static void type##_die_injection(original_method_self##_die_t original, type *player, Entity *cause) { \
#define Player_die_injections(type) \
static type##_die_t original_##type##_die; \
static void type##_die_injection(type *player, Entity *cause) { \
/* Call Original Method */ \
original((original_method_self *) player, cause); \
original_##type##_die(player, cause); \
\
/* Get Variable */ \
RakNetInstance *rak_net_instance = player->minecraft->rak_net_instance; \
/* Only Run On Server-Side */ \
if (rak_net_instance->isServer()) { \
if (rak_net_instance->vtable->isServer(rak_net_instance)) { \
/* Get Death Message */ \
std::string message = get_death_message((Player *) player, cause); \
\
/* Post Death Message */ \
ServerSideNetworkHandler *server_side_network_handler = (ServerSideNetworkHandler *) player->minecraft->network_handler; \
server_side_network_handler->displayGameMessage(&message); \
ServerSideNetworkHandler_displayGameMessage(server_side_network_handler, &message); \
} \
}
#define Player_actuallyHurt_injections(type) \
@ -105,7 +106,7 @@ static bool Mob_hurt_injection(Mob_hurt_t original, Mob *mob, Entity *source, in
/* Get Variables */ \
RakNetInstance *rak_net_instance = player->minecraft->rak_net_instance; \
/* Only Run On Server-Side */ \
if (rak_net_instance->isServer()) { \
if (rak_net_instance->vtable->isServer(rak_net_instance)) { \
/* Check Health */ \
if (new_health < 1 && old_health >= 1) { \
/* Get Death Message */ \
@ -113,13 +114,13 @@ static bool Mob_hurt_injection(Mob_hurt_t original, Mob *mob, Entity *source, in
\
/* Post Death Message */ \
ServerSideNetworkHandler *server_side_network_handler = (ServerSideNetworkHandler *) player->minecraft->network_handler; \
server_side_network_handler->displayGameMessage(&message); \
ServerSideNetworkHandler_displayGameMessage(server_side_network_handler, &message); \
} \
} \
}
Player_die_injections(LocalPlayer, LocalPlayer)
Player_die_injections(ServerPlayer, Player)
Player_die_injections(LocalPlayer)
Player_die_injections(ServerPlayer)
Player_actuallyHurt_injections(LocalPlayer)
Player_actuallyHurt_injections(ServerPlayer)
@ -128,12 +129,10 @@ Player_actuallyHurt_injections(ServerPlayer)
void init_death() {
// Death Messages
if (feature_has("Implement Death Messages", server_auto)) {
patch_vtable(ServerPlayer_die, [](ServerPlayer *player, Entity *cause) {
ServerPlayer_die_injection(*Player_die_vtable_addr, player, cause);
});
overwrite_virtual_calls(LocalPlayer_die, LocalPlayer_die_injection);
patch_vtable(LocalPlayer_actuallyHurt, LocalPlayer_actuallyHurt_injection);
patch_vtable(ServerPlayer_actuallyHurt, ServerPlayer_actuallyHurt_injection);
patch_address(ServerPlayer_die_vtable_addr, ServerPlayer_die_injection);
patch_address(LocalPlayer_die_vtable_addr, LocalPlayer_die_injection);
patch_address(LocalPlayer_actuallyHurt_vtable_addr, LocalPlayer_actuallyHurt_injection);
patch_address(ServerPlayer_actuallyHurt_vtable_addr, ServerPlayer_actuallyHurt_injection);
overwrite_virtual_calls(Mob_hurt, Mob_hurt_injection);
}

View File

@ -1,3 +1,11 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
__attribute__((visibility("internal"))) void _init_game_mode_ui();
#ifdef __cplusplus
}
#endif

View File

@ -17,7 +17,7 @@ static void set_is_survival(bool new_is_survival) {
patch((void *) 0x16efc, inventory_patch);
// Use Correct Size For GameMode Object
unsigned char size_patch[4] = {(unsigned char) (new_is_survival ? sizeof(SurvivalMode) : sizeof(CreatorMode)), 0x00, 0xa0, 0xe3}; // "mov r0, #SURVIVAL_MODE_SIZE" or "mov r0, #CREATOR_MODE_SIZE"
unsigned char size_patch[4] = {(unsigned char) (new_is_survival ? SURVIVAL_MODE_SIZE : CREATOR_MODE_SIZE), 0x00, 0xa0, 0xe3}; // "mov r0, #SURVIVAL_MODE_SIZE" or "mov r0, #CREATOR_MODE_SIZE"
patch((void *) 0x16ee4, size_patch);
// Replace Default CreatorMode Constructor With CreatorMode Or SurvivalMode Constructor
@ -57,7 +57,7 @@ void init_game_mode() {
overwrite_call((void *) 0x16f84, (void *) ServerLevel_constructor);
// Allocate Correct Size For ServerLevel
uint32_t level_size = sizeof(ServerLevel);
uint32_t level_size = SERVER_LEVEL_SIZE;
patch_address((void *) 0x17004, (void *) level_size);
// Disable CreatorMode-Specific API Features (Polling Block Hits) In SurvivalMode, This Is Preferable To Crashing

View File

@ -1,13 +1,17 @@
// Config Needs To Load First
#include <libreborn/libreborn.h>
#include "game-mode-internal.h"
// Game Mode UI Code Is Useless In Headless Mode
#ifndef MCPI_HEADLESS_MODE
#include <string>
#include <set>
#include <symbols/minecraft.h>
#include <libreborn/libreborn.h>
#include <mods/text-input-box/TextInputScreen.h>
#include <mods/touch/touch.h>
#include <mods/misc/misc.h>
#include "game-mode-internal.h"
// Strings
#define GAME_MODE_STR(mode) ("Game Mode: " mode)
@ -32,9 +36,6 @@ CUSTOM_VTABLE(create_world_screen, Screen) {
static int inner_padding = 4;
static int description_padding = 4;
static int title_padding = 8;
static int button_height = 24;
static int content_y_offset_top = (title_padding * 2) + line_height;
static int content_y_offset_bottom = button_height + (bottom_padding * 2);
// Init
static Screen_init_t original_init = vtable->init;
vtable->init = [](Screen *super) {
@ -70,26 +71,25 @@ CUSTOM_VTABLE(create_world_screen, Screen) {
CreateWorldScreen *self = (CreateWorldScreen *) super;
delete self->name;
delete self->seed;
self->game_mode->destructor_deleting();
self->back->destructor_deleting();
self->create->destructor_deleting();
self->game_mode->vtable->destructor_deleting(self->game_mode);
self->back->vtable->destructor_deleting(self->back);
self->create->vtable->destructor_deleting(self->create);
};
// Rendering
static Screen_render_t original_render = vtable->render;
vtable->render = [](Screen *super, int x, int y, float param_1) {
// Background
misc_render_background(80, super->minecraft, 0, 0, super->width, super->height);
misc_render_background(32, super->minecraft, 0, content_y_offset_top, super->width, super->height - content_y_offset_top - content_y_offset_bottom);
super->vtable->renderBackground(super);
// Call Original Method
original_render(super, x, y, param_1);
// Title
std::string title = "Create world";
super->drawCenteredString(super->font, &title, super->width / 2, title_padding, 0xffffffff);
Screen_drawCenteredString(super, super->font, &title, super->width / 2, title_padding, 0xffffffff);
// Game Mode Description
CreateWorldScreen *self = (CreateWorldScreen *) super;
bool is_creative = self->game_mode->text == CREATIVE_STR;
std::string description = is_creative ? Strings::creative_mode_description : Strings::survival_mode_description;
super->drawString(super->font, &description, self->game_mode->x, self->game_mode->y + self->game_mode->height + description_padding, 0xa0a0a0);
std::string description = is_creative ? Strings_creative_mode_description : Strings_survival_mode_description;
Screen_drawString(super, super->font, &description, self->game_mode->x, self->game_mode->y + self->game_mode->height + description_padding, 0xa0a0a0);
};
// Positioning
static Screen_setupPositions_t original_setupPositions = vtable->setupPositions;
@ -98,15 +98,15 @@ CUSTOM_VTABLE(create_world_screen, Screen) {
CreateWorldScreen *self = (CreateWorldScreen *) super;
// Height/Width
int width = 120;
int height = button_height;
int height = 24;
self->create->width = self->back->width = self->game_mode->width = width;
int seed_width = self->game_mode->width;
int name_width = width * 1.5f;
self->create->height = self->back->height = self->game_mode->height = height;
int text_box_height = self->game_mode->height;
// Find Center Y
int top = content_y_offset_top;
int bottom = super->height - content_y_offset_bottom;
int top = (title_padding * 2) + line_height;
int bottom = super->height - self->create->height - (bottom_padding * 2);
int center_y = ((bottom - top) / 2) + top;
center_y -= (description_padding + line_height) / 2;
// X/Y
@ -125,7 +125,7 @@ CUSTOM_VTABLE(create_world_screen, Screen) {
// ESC
vtable->handleBackEvent = [](Screen *super, bool do_nothing) {
if (!do_nothing) {
super->minecraft->screen_chooser.setScreen(5);
ScreenChooser_setScreen(&super->minecraft->screen_chooser, 5);
}
return true;
};
@ -138,7 +138,7 @@ CUSTOM_VTABLE(create_world_screen, Screen) {
self->game_mode->text = is_creative ? SURVIVAL_STR : CREATIVE_STR;
} else if (button == self->back) {
// Back
super->handleBackEvent(false);
super->vtable->handleBackEvent(super, false);
} else if (button == self->create) {
// Create
create_world(super->minecraft, self->name->getText(), is_creative, self->seed->getText());
@ -149,7 +149,7 @@ static Screen *create_create_world_screen() {
// Construct
CreateWorldScreen *screen = new CreateWorldScreen;
ALLOC_CHECK(screen);
screen->super.super.constructor();
Screen_constructor(&screen->super.super);
// Set VTable
screen->super.super.vtable = get_create_world_screen_vtable();
@ -162,7 +162,7 @@ static Screen *create_create_world_screen() {
static std::string getUniqueLevelName(LevelStorageSource *source, const std::string &in) {
std::set<std::string> maps;
std::vector<LevelSummary> vls;
source->getLevelList(&vls);
source->vtable->getLevelList(source, &vls);
for (int i = 0; i < int(vls.size()); i++) {
const LevelSummary &ls = vls[i];
maps.insert(ls.folder);
@ -178,20 +178,20 @@ static std::string getUniqueLevelName(LevelStorageSource *source, const std::str
static void create_world(Minecraft *minecraft, std::string name, bool is_creative, std::string seed_str) {
// Get Seed
int seed;
seed_str = Util::stringTrim(&seed_str);
seed_str = Util_stringTrim(&seed_str);
if (!seed_str.empty()) {
int num;
if (sscanf(seed_str.c_str(), "%d", &num) > 0) {
seed = num;
} else {
seed = Util::hashCode(&seed_str);
seed = Util_hashCode(&seed_str);
}
} else {
seed = Common::getEpochTimeS();
seed = Common_getEpochTimeS();
}
// Get Folder Name
name = Util::stringTrim(&name);
name = Util_stringTrim(&name);
std::string folder = "";
for (char c : name) {
if (
@ -213,7 +213,7 @@ static void create_world(Minecraft *minecraft, std::string name, bool is_creativ
if (folder.empty()) {
folder = "World";
}
folder = getUniqueLevelName(minecraft->getLevelSource(), folder);
folder = getUniqueLevelName(Minecraft_getLevelSource(minecraft), folder);
// Settings
LevelSettings settings;
@ -221,16 +221,16 @@ static void create_world(Minecraft *minecraft, std::string name, bool is_creativ
settings.seed = seed;
// Create World
minecraft->selectLevel(&folder, &name, &settings);
minecraft->vtable->selectLevel(minecraft, &folder, &name, &settings);
// Multiplayer
minecraft->hostMultiplayer(19132);
Minecraft_hostMultiplayer(minecraft, 19132);
// Open ProgressScreen
ProgressScreen *screen = new ProgressScreen;
ProgressScreen *screen = alloc_ProgressScreen();
ALLOC_CHECK(screen);
screen = screen->constructor();
minecraft->setScreen((Screen *) screen);
screen = ProgressScreen_constructor(screen);
Minecraft_setScreen(minecraft, (Screen *) screen);
}
// Redirect Create World Button
@ -238,7 +238,7 @@ static void create_world(Minecraft *minecraft, std::string name, bool is_creativ
static void prefix##SelectWorldScreen_tick_injection(prefix##SelectWorldScreen_tick_t original, prefix##SelectWorldScreen *screen) { \
if (screen->should_create_world) { \
/* Open Screen */ \
screen->minecraft->setScreen(create_create_world_screen()); \
Minecraft_setScreen(screen->minecraft, create_create_world_screen()); \
/* Finish */ \
screen->should_create_world = false; \
} else { \
@ -255,3 +255,8 @@ void _init_game_mode_ui() {
overwrite_virtual_calls(SelectWorldScreen_tick, SelectWorldScreen_tick_injection);
overwrite_virtual_calls(Touch_SelectWorldScreen_tick, Touch_SelectWorldScreen_tick_injection);
}
#else
void _init_game_mode_ui() {
}
#endif

View File

@ -24,7 +24,7 @@ __attribute__((destructor)) static void _free_home() {
// Init
void init_home() {
// Store Data In ~/.minecraft-pi Instead Of ~/.minecraft
patch_address((void *) &Strings::default_path, (void *) HOME_SUBDIRECTORY_FOR_GAME_DATA);
patch_address((void *) Strings_default_path_pointer, (void *) HOME_SUBDIRECTORY_FOR_GAME_DATA);
// The override code resolves assets manually,
// making changing directory redundant.

View File

@ -1,11 +1,13 @@
#include <libreborn/libreborn.h>
#include <mods/init/init.h>
#include <media-layer/core.h>
#include <symbols/minecraft.h>
__attribute__((constructor)) static void init() {
__attribute__((constructor)) static void init(int argc, char *argv[]) {
media_ensure_loaded();
reborn_init_patch();
run_tests();
init_symbols();
init_version();
init_compat();
#ifdef MCPI_SERVER_MODE
@ -18,12 +20,12 @@ __attribute__((constructor)) static void init() {
init_input();
init_sign();
init_camera();
init_touch();
init_atlas();
init_title_screen();
init_skin();
init_fps();
#endif
init_touch();
init_textures();
init_creative();
init_game_mode();
@ -35,6 +37,9 @@ __attribute__((constructor)) static void init() {
init_cake();
init_home();
#ifndef MCPI_SERVER_MODE
init_benchmark();
init_benchmark(argc, argv);
#else
(void) argc;
(void) argv;
#endif
}

View File

@ -39,7 +39,7 @@ static int32_t MouseBuildInput_tickBuild_injection(MouseBuildInput_tickBuild_t o
static bool last_player_attack_successful = false;
static bool Player_attack_Entity_hurt_injection(Entity *entity, Entity *attacker, int32_t damage) {
// Call Original Method
last_player_attack_successful = entity->hurt(attacker, damage);
last_player_attack_successful = entity->vtable->hurt(entity, attacker, damage);
return last_player_attack_successful;
}
static ItemInstance *Player_attack_Inventory_getSelected_injection(Inventory *inventory) {
@ -49,7 +49,7 @@ static ItemInstance *Player_attack_Inventory_getSelected_injection(Inventory *in
}
// Call Original Method
return inventory->getSelected();
return Inventory_getSelected(inventory);
}
// Init

View File

@ -19,8 +19,8 @@ static void _handle_bow(Minecraft *minecraft) {
if (fix_bow && !is_right_click) {
GameMode *game_mode = minecraft->game_mode;
LocalPlayer *player = minecraft->player;
if (player != nullptr && game_mode != nullptr && player->isUsingItem()) {
game_mode->releaseUsingItem((Player *) player);
if (player != nullptr && game_mode != nullptr && LocalPlayer_isUsingItem(player)) {
game_mode->vtable->releaseUsingItem(game_mode, (Player *) player);
}
}
}

View File

@ -16,10 +16,10 @@ static void _handle_open_crafting(Minecraft *minecraft) {
// Set Screen
if (!creative_is_restricted() || !Minecraft_isCreativeMode(minecraft)) {
WorkbenchScreen *screen = new WorkbenchScreen;
WorkbenchScreen *screen = alloc_WorkbenchScreen();
ALLOC_CHECK(screen);
screen = screen->constructor(0);
minecraft->setScreen((Screen *) screen);
screen = WorkbenchScreen_constructor(screen, 0);
Minecraft_setScreen(minecraft, (Screen *) screen);
}
}
}

View File

@ -34,7 +34,7 @@ static void _handle_drop(Minecraft *minecraft) {
Inventory *inventory = player->inventory;
// Get Item
ItemInstance *inventory_item = inventory->getItem(selected_slot);
ItemInstance *inventory_item = inventory->vtable->getItem(inventory, selected_slot);
// Check
if (inventory_item != nullptr && inventory_item->count > 0) {
// Copy
@ -59,12 +59,12 @@ static void _handle_drop(Minecraft *minecraft) {
// Empty Slot If Needed
if (inventory_item->count < 1) {
inventory->release(selected_slot);
inventory->compressLinkedSlotList(selected_slot);
Inventory_release(inventory, selected_slot);
Inventory_compressLinkedSlotList(inventory, selected_slot);
}
// Drop
player->drop(dropped_item, false);
player->vtable->drop(player, dropped_item, false);
}
}
}

View File

@ -1,8 +1,16 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
__attribute__((visibility("internal"))) void _init_attack();
__attribute__((visibility("internal"))) void _init_bow();
__attribute__((visibility("internal"))) void _init_misc();
__attribute__((visibility("internal"))) void _init_toggle();
__attribute__((visibility("internal"))) void _init_drop();
__attribute__((visibility("internal"))) void _init_crafting();
#ifdef __cplusplus
}
#endif

View File

@ -29,7 +29,7 @@ static void _handle_back(Minecraft *minecraft) {
}
// Send Event
for (int i = 0; i < back_button_presses; i++) {
minecraft->handleBack(0);
minecraft->vtable->handleBack(minecraft, 0);
}
back_button_presses = 0;
}
@ -38,7 +38,7 @@ static void _handle_back(Minecraft *minecraft) {
static bool OptionsScreen_handleBackEvent_injection(OptionsScreen *screen, bool do_nothing) {
if (!do_nothing) {
Minecraft *minecraft = screen->minecraft;
minecraft->setScreen(nullptr);
Minecraft_setScreen(minecraft, nullptr);
}
return true;
}
@ -48,11 +48,11 @@ static bool InBedScreen_handleBackEvent_injection(InBedScreen *screen, bool do_n
if (!do_nothing) {
// Close Screen
Minecraft *minecraft = screen->minecraft;
minecraft->setScreen(nullptr);
Minecraft_setScreen(minecraft, nullptr);
// Stop Sleeping
LocalPlayer *player = minecraft->player;
if (player != nullptr) {
player->stopSleepInBed(1, 1, 1);
player->vtable->stopSleepInBed(player, 1, 1, 1);
}
}
return true;
@ -68,10 +68,10 @@ void input_set_mouse_grab_state(int state) {
static void _handle_mouse_grab(Minecraft *minecraft) {
if (mouse_grab_state == -1) {
// Grab
minecraft->grabMouse();
Minecraft_grabMouse(minecraft);
} else if (mouse_grab_state == 1) {
// Un-Grab
minecraft->releaseMouse();
Minecraft_releaseMouse(minecraft);
}
mouse_grab_state = 0;
}
@ -83,7 +83,7 @@ static bool Gui_tickItemDrop_Minecraft_isCreativeMode_call_injection(Minecraft *
bool is_in_game = minecraft->screen == nullptr || minecraft->screen->vtable == (Screen_vtable *) Touch_IngameBlockSelectionScreen_vtable_base;
if (!enable_misc || (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_OFF && is_in_game)) {
// Call Original Method
return creative_is_restricted() && minecraft->isCreativeMode();
return creative_is_restricted() && Minecraft_isCreativeMode(minecraft);
} else {
// Disable Item Drop Ticking
return 1;
@ -103,9 +103,9 @@ void _init_misc() {
enable_misc = feature_has("Miscellaneous Input Fixes", server_disabled);
if (enable_misc) {
// Fix OptionsScreen Ignoring The Back Button
patch_vtable(OptionsScreen_handleBackEvent, OptionsScreen_handleBackEvent_injection);
patch_address(OptionsScreen_handleBackEvent_vtable_addr, OptionsScreen_handleBackEvent_injection);
// Fix "Sleeping Beauty" Bug
patch_vtable(InBedScreen_handleBackEvent, InBedScreen_handleBackEvent_injection);
patch_address(InBedScreen_handleBackEvent_vtable_addr, InBedScreen_handleBackEvent_injection);
// Disable Opening Inventory Using The Cursor When Cursor Is Hidden
overwrite_calls(Gui_handleClick, Gui_handleClick_injection);
}

View File

@ -3,9 +3,6 @@
#include <libreborn/libreborn.h>
#include <symbols/minecraft.h>
#ifndef MCPI_HEADLESS_MODE
#include <GLES/gl.h>
#endif
#include <mods/misc/misc.h>
#include "misc-internal.h"
@ -82,7 +79,7 @@ SETUP_CALLBACK(creative_inventory_setup, FillingContainer);
// Handle Custom Creative Inventory Setup Behavior
static void Inventory_setupDefault_FillingContainer_addItem_call_injection(FillingContainer *filling_container, ItemInstance *item_instance) {
// Call Original Method
filling_container->addItem(item_instance);
FillingContainer_addItem(filling_container, item_instance);
// Run Functions
handle_misc_creative_inventory_setup(filling_container);
@ -142,28 +139,6 @@ static void Gui_handleKeyPressed_injection(Gui_handleKeyPressed_t original, Gui
original(self, key);
}
// Render Fancy Background
void misc_render_background(int color, Minecraft *minecraft, int x, int y, int width, int height) {
// https://github.com/ReMinecraftPE/mcpe/blob/f0d65eaecec1b3fe9c2f2b251e114a890c54ab77/source/client/gui/components/RolledSelectionList.cpp#L169-L179
#ifndef MCPI_HEADLESS_MODE
glColor4f(1, 1, 1, 1);
#endif
std::string texture = "gui/background.png";
minecraft->textures->loadAndBindTexture(&texture);
Tesselator *t = &Tesselator::instance;
t->begin(7);
t->color(color, color, color, 255);
float x1 = x;
float x2 = x + width;
float y1 = y;
float y2 = y + height;
t->vertexUV(x1, y2, 0.0f, x1 / 32.0f, y2 / 32.0f);
t->vertexUV(x2, y2, 0.0f, x2 / 32.0f, y2 / 32.0f);
t->vertexUV(x2, y1, 0.0f, x2 / 32.0f, y1 / 32.0f);
t->vertexUV(x1, y1, 0.0f, x1 / 32.0f, y1 / 32.0f);
t->draw();
}
// Init
void _init_misc_api() {
// Handle Custom Update Behavior

View File

@ -43,9 +43,9 @@ static void Gui_addMessage_injection(Gui_addMessage_t original, Gui *gui, std::s
static int last_progress = -1;
static const char *last_message = nullptr;
static void print_progress(Minecraft *minecraft) {
const char *message = minecraft->getProgressMessage();
const char *message = Minecraft_getProgressMessage(minecraft);
int32_t progress = minecraft->progress;
if (minecraft->isLevelGenerated()) {
if (Minecraft_isLevelGenerated(minecraft)) {
message = "Ready";
progress = -1;
}

View File

@ -1,4 +1,12 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
__attribute__((visibility("internal"))) void _init_misc_logging();
__attribute__((visibility("internal"))) void _init_misc_api();
#ifdef __cplusplus
}
#endif

View File

@ -24,61 +24,17 @@
#include "misc-internal.h"
#include <mods/misc/misc.h>
// Classic HUD
#define DEFAULT_HUD_PADDING 2
#define NEW_HUD_PADDING 1
#define HUD_ELEMENT_WIDTH 82
#define HUD_ELEMENT_HEIGHT 9
#define TOOLBAR_HEIGHT 22
#define SLOT_WIDTH 20
#define DEFAULT_BUBBLES_PADDING 1
#define NUMBER_OF_SLOTS 9
static int use_classic_hud = 0;
static void Gui_renderHearts_GuiComponent_blit_hearts_injection(GuiComponent *component, int32_t x_dest, int32_t y_dest, int32_t x_src, int32_t y_src, int32_t width_dest, int32_t height_dest, int32_t width_src, int32_t height_src) {
Minecraft *minecraft = ((Gui *) component)->minecraft;
x_dest -= DEFAULT_HUD_PADDING;
float width = ((float) minecraft->screen_width) * Gui::InvGuiScale;
float height = ((float) minecraft->screen_height) * Gui::InvGuiScale;
x_dest += (width - (NUMBER_OF_SLOTS * SLOT_WIDTH)) / 2;
y_dest -= DEFAULT_HUD_PADDING;
y_dest += height - HUD_ELEMENT_HEIGHT - TOOLBAR_HEIGHT - NEW_HUD_PADDING;
// Call Original Method
component->blit(x_dest, y_dest, x_src, y_src, width_dest, height_dest, width_src, height_src);
}
static void Gui_renderHearts_GuiComponent_blit_armor_injection(Gui *component, int32_t x_dest, int32_t y_dest, int32_t x_src, int32_t y_src, int32_t width_dest, int32_t height_dest, int32_t width_src, int32_t height_src) {
Minecraft *minecraft = component->minecraft;
x_dest -= DEFAULT_HUD_PADDING + HUD_ELEMENT_WIDTH;
float width = ((float) minecraft->screen_width) * Gui::InvGuiScale;
float height = ((float) minecraft->screen_height) * Gui::InvGuiScale;
x_dest += width - ((width - (NUMBER_OF_SLOTS * SLOT_WIDTH)) / 2) - HUD_ELEMENT_WIDTH;
y_dest -= DEFAULT_HUD_PADDING;
y_dest += height - HUD_ELEMENT_HEIGHT - TOOLBAR_HEIGHT - NEW_HUD_PADDING;
// Call Original Method
component->blit(x_dest, y_dest, x_src, y_src, width_dest, height_dest, width_src, height_src);
}
static void Gui_renderBubbles_GuiComponent_blit_injection(Gui *component, int32_t x_dest, int32_t y_dest, int32_t x_src, int32_t y_src, int32_t width_dest, int32_t height_dest, int32_t width_src, int32_t height_src) {
Minecraft *minecraft = component->minecraft;
x_dest -= DEFAULT_HUD_PADDING;
float width = ((float) minecraft->screen_width) * Gui::InvGuiScale;
float height = ((float) minecraft->screen_height) * Gui::InvGuiScale;
x_dest += (width - (NUMBER_OF_SLOTS * SLOT_WIDTH)) / 2;
y_dest -= DEFAULT_HUD_PADDING + DEFAULT_BUBBLES_PADDING + HUD_ELEMENT_HEIGHT;
y_dest += height - HUD_ELEMENT_HEIGHT - TOOLBAR_HEIGHT - HUD_ELEMENT_HEIGHT - NEW_HUD_PADDING;
// Call Original Method
component->blit(x_dest, y_dest, x_src, y_src, width_dest, height_dest, width_src, height_src);
}
// Heart Food Overlay
// Heart food overlay
static int heal_amount = 0, heal_amount_drawing = 0;
static void Gui_renderHearts_injection(Gui_renderHearts_t original, Gui *gui) {
// Get heal_amount
heal_amount = heal_amount_drawing = 0;
Inventory *inventory = gui->minecraft->player->inventory;
ItemInstance *held_ii = inventory->getSelected();
ItemInstance *held_ii = Inventory_getSelected(inventory);
if (held_ii) {
Item *held = Item::items[held_ii->id];
if (held->isFood() && held_ii->id) {
Item *held = Item_items[held_ii->id];
if (held->vtable->isFood(held) && held_ii->id) {
int nutrition = ((FoodItem *) held)->nutrition;
int cur_health = gui->minecraft->player->health;
int heal_num = fmin(cur_health + nutrition, 20) - cur_health;
@ -89,42 +45,86 @@ static void Gui_renderHearts_injection(Gui_renderHearts_t original, Gui *gui) {
// Call original
original(gui);
}
static GuiComponent_blit_t get_blit_with_classic_hud_offset() {
return use_classic_hud ? Gui_renderHearts_GuiComponent_blit_hearts_injection : GuiComponent_blit;
}
#define PINK_HEART_FULL 70
#define PINK_HEART_HALF 79
static Gui_blit_t Gui_blit_renderHearts_original = nullptr;
static void Gui_renderHearts_GuiComponent_blit_overlay_empty_injection(Gui *gui, int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t w1, int32_t h1, int32_t w2, int32_t h2) {
// Call Original Method
get_blit_with_classic_hud_offset()((GuiComponent *) gui, x1, y1, x2, y2, w1, h1, w2, h2);
// Render The Overlay
// Call original
Gui_blit_renderHearts_original(gui, x1, y1, x2, y2, w1, h1, w2, h2);
// Render the overlay
if (heal_amount_drawing == 1) {
// Half Heart
get_blit_with_classic_hud_offset()((GuiComponent *) gui, x1, y1, PINK_HEART_HALF, 0, w1, h1, w2, h2);
// Half heart
Gui_blit_renderHearts_original(gui, x1, y1, PINK_HEART_HALF, 0, w1, h1, w2, h2);
heal_amount_drawing = 0;
} else if (heal_amount_drawing > 0) {
// Full Heart
get_blit_with_classic_hud_offset()((GuiComponent *) gui, x1, y1, PINK_HEART_FULL, 0, w1, h1, w2, h2);
// Full heart
Gui_blit_renderHearts_original(gui, x1, y1, PINK_HEART_FULL, 0, w1, h1, w2, h2);
heal_amount_drawing -= 2;
}
}
static void Gui_renderHearts_GuiComponent_blit_overlay_hearts_injection(Gui *gui, int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t w1, int32_t h1, int32_t w2, int32_t h2) {
// Offset the overlay
if (x2 == 52) {
heal_amount_drawing += 2;
} else if (x2 == 61 && heal_amount) {
// Half heart, flipped
get_blit_with_classic_hud_offset()((GuiComponent *) gui, x1, y1, PINK_HEART_FULL, 0, w1, h1, w2, h2);
Gui_blit_renderHearts_original(gui, x1, y1, PINK_HEART_FULL, 0, w1, h1, w2, h2);
heal_amount_drawing += 1;
}
// Call Original Method
get_blit_with_classic_hud_offset()((GuiComponent *) gui, x1, y1, x2, y2, w1, h1, w2, h2);
};
// Call original
Gui_blit_renderHearts_original(gui, x1, y1, x2, y2, w1, h1, w2, h2);
heal_amount_drawing = fmin(heal_amount_drawing, heal_amount);
}
// Classic HUD
#define DEFAULT_HUD_PADDING 2
#define NEW_HUD_PADDING 1
#define HUD_ELEMENT_WIDTH 82
#define HUD_ELEMENT_HEIGHT 9
#define TOOLBAR_HEIGHT 22
#define SLOT_WIDTH 20
#define DEFAULT_BUBBLES_PADDING 1
#define NUMBER_OF_SLOTS 9
static int use_classic_hud = 0;
static void Gui_renderHearts_GuiComponent_blit_hearts_injection(Gui *component, int32_t x_dest, int32_t y_dest, int32_t x_src, int32_t y_src, int32_t width_dest, int32_t height_dest, int32_t width_src, int32_t height_src) {
Minecraft *minecraft = component->minecraft;
x_dest -= DEFAULT_HUD_PADDING;
float width = ((float) minecraft->screen_width) * Gui_InvGuiScale;
float height = ((float) minecraft->screen_height) * Gui_InvGuiScale;
x_dest += (width - (NUMBER_OF_SLOTS * SLOT_WIDTH)) / 2;
y_dest -= DEFAULT_HUD_PADDING;
y_dest += height - HUD_ELEMENT_HEIGHT - TOOLBAR_HEIGHT - NEW_HUD_PADDING;
// Call Original Method
Gui_blit(component, x_dest, y_dest, x_src, y_src, width_dest, height_dest, width_src, height_src);
}
static void Gui_renderHearts_GuiComponent_blit_armor_injection(Gui *component, int32_t x_dest, int32_t y_dest, int32_t x_src, int32_t y_src, int32_t width_dest, int32_t height_dest, int32_t width_src, int32_t height_src) {
Minecraft *minecraft = component->minecraft;
x_dest -= DEFAULT_HUD_PADDING + HUD_ELEMENT_WIDTH;
float width = ((float) minecraft->screen_width) * Gui_InvGuiScale;
float height = ((float) minecraft->screen_height) * Gui_InvGuiScale;
x_dest += width - ((width - (NUMBER_OF_SLOTS * SLOT_WIDTH)) / 2) - HUD_ELEMENT_WIDTH;
y_dest -= DEFAULT_HUD_PADDING;
y_dest += height - HUD_ELEMENT_HEIGHT - TOOLBAR_HEIGHT - NEW_HUD_PADDING;
// Call Original Method
Gui_blit(component, x_dest, y_dest, x_src, y_src, width_dest, height_dest, width_src, height_src);
}
static void Gui_renderBubbles_GuiComponent_blit_injection(Gui *component, int32_t x_dest, int32_t y_dest, int32_t x_src, int32_t y_src, int32_t width_dest, int32_t height_dest, int32_t width_src, int32_t height_src) {
Minecraft *minecraft = component->minecraft;
x_dest -= DEFAULT_HUD_PADDING;
float width = ((float) minecraft->screen_width) * Gui_InvGuiScale;
float height = ((float) minecraft->screen_height) * Gui_InvGuiScale;
x_dest += (width - (NUMBER_OF_SLOTS * SLOT_WIDTH)) / 2;
y_dest -= DEFAULT_HUD_PADDING + DEFAULT_BUBBLES_PADDING + HUD_ELEMENT_HEIGHT;
y_dest += height - HUD_ELEMENT_HEIGHT - TOOLBAR_HEIGHT - HUD_ELEMENT_HEIGHT - NEW_HUD_PADDING;
// Call Original Method
Gui_blit(component, x_dest, y_dest, x_src, y_src, width_dest, height_dest, width_src, height_src);
}
// Additional GUI Rendering
static int hide_chat_messages = 0;
bool is_in_chat = false;
bool is_in_chat = 0;
static int render_selected_item_text = 0;
static void Gui_renderChatMessages_injection(Gui_renderChatMessages_t original, Gui *gui, int32_t y_offset, uint32_t max_messages, bool disable_fading, Font *font) {
// Handle Classic HUD
@ -149,9 +149,9 @@ static void Gui_renderChatMessages_injection(Gui_renderChatMessages_t original,
// Calculate Selected Item Text Scale
Minecraft *minecraft = gui->minecraft;
int32_t screen_width = minecraft->screen_width;
float scale = ((float) screen_width) * Gui::InvGuiScale;
float scale = ((float) screen_width) * Gui_InvGuiScale;
// Render Selected Item Text
gui->renderOnSelectItemNameText((int32_t) scale, font, y_offset - 0x13);
Gui_renderOnSelectItemNameText(gui, (int32_t) scale, font, y_offset - 0x13);
}
}
// Reset Selected Item Text Timer On Slot Select
@ -235,7 +235,7 @@ static void LoginPacket_read_injection(LoginPacket_read_t original, LoginPacket
ALLOC_CHECK(new_username);
sanitize_string(&new_username, MAX_USERNAME_LENGTH, 0);
// Set New Username
rak_string->Assign(new_username);
RakNet_RakString_Assign(rak_string, new_username);
// Free
free(new_username);
}
@ -272,7 +272,7 @@ static const char *RAKNET_ERROR_NAMES[] = {
#endif
static RakNet_StartupResult RakNetInstance_host_RakNet_RakPeer_Startup_injection(RakNet_RakPeer *rak_peer, unsigned short maxConnections, unsigned char *socketDescriptors, uint32_t socketDescriptorCount, int32_t threadPriority) {
// Call Original Method
RakNet_StartupResult result = rak_peer->Startup(maxConnections, socketDescriptors, socketDescriptorCount, threadPriority);
RakNet_StartupResult result = rak_peer->vtable->Startup(rak_peer, maxConnections, socketDescriptors, socketDescriptorCount, threadPriority);
// Print Error
if (result != RAKNET_STARTED) {
@ -297,7 +297,7 @@ static RakNetInstance *RakNetInstance_injection(RakNetInstance_constructor_t ori
static void LocalPlayer_die_injection(LocalPlayer_die_t original, LocalPlayer *entity, Entity *cause) {
// Close Screen
Minecraft *minecraft = entity->minecraft;
minecraft->setScreen(nullptr);
Minecraft_setScreen(minecraft, nullptr);
// Call Original Method
original(entity, cause);
@ -307,7 +307,7 @@ static void LocalPlayer_die_injection(LocalPlayer_die_t original, LocalPlayer *e
static int32_t FurnaceScreen_handleAddItem_injection(FurnaceScreen_handleAddItem_t original, FurnaceScreen *furnace_screen, int32_t slot, ItemInstance *item) {
// Get Existing Item
FurnaceTileEntity *tile_entity = furnace_screen->tile_entity;
ItemInstance *existing_item = tile_entity->getItem(slot);
ItemInstance *existing_item = tile_entity->vtable->getItem(tile_entity, slot);
// Check Item
int valid;
@ -349,11 +349,11 @@ static void GameRenderer_render_injection(GameRenderer_render_t original, GameRe
// Fix GL Mode
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Get X And Y
float x = Mouse::getX() * Gui::InvGuiScale;
float y = Mouse::getY() * Gui::InvGuiScale;
float x = Mouse_getX() * Gui_InvGuiScale;
float y = Mouse_getY() * Gui_InvGuiScale;
// Render Cursor
Minecraft *minecraft = game_renderer->minecraft;
Common::renderCursor(x, y, minecraft);
Common_renderCursor(x, y, minecraft);
}
}
#endif
@ -410,14 +410,14 @@ HOOK(bind, int, (int sockfd, const struct sockaddr *addr, socklen_t addrlen)) {
// Change Grass Color
static int32_t get_color(LevelSource *level_source, int32_t x, int32_t z) {
Biome *biome = level_source->getBiome(x, z);
Biome *biome = level_source->vtable->getBiome(level_source, x, z);
if (biome == nullptr) {
return 0;
}
return biome->color;
}
#define BIOME_BLEND_SIZE 7
static int32_t GrassTile_getColor_injection(__attribute__((unused)) GrassTile *tile, LevelSource *level_source, int32_t x, __attribute__((unused)) int32_t y, int32_t z) {
static int32_t GrassTile_getColor_injection(__attribute__((unused)) Tile *tile, LevelSource *level_source, int32_t x, __attribute__((unused)) int32_t y, int32_t z) {
int r_sum = 0;
int g_sum = 0;
int b_sum = 0;
@ -441,7 +441,7 @@ static int32_t GrassTile_getColor_injection(__attribute__((unused)) GrassTile *t
static int32_t TallGrass_getColor_injection(TallGrass_getColor_t original, TallGrass *tile, LevelSource *level_source, int32_t x, int32_t y, int32_t z) {
int32_t original_color = original(tile, level_source, x, y, z);
if (original_color == 0x339933) {
return GrassTile_getColor_injection(nullptr, level_source, x, y, z);
return GrassTile_getColor_injection((Tile *) tile, level_source, x, y, z);
} else {
return original_color;
}
@ -459,27 +459,26 @@ static void RandomLevelSource_buildSurface_injection(RandomLevelSource_buildSurf
LargeCaveFeature *cave_feature = &random_level_source->cave_feature;
// Generate
cave_feature->apply((ChunkSource *) random_level_source, level, chunk_x, chunk_y, chunk_data, 0);
cave_feature->vtable->apply(cave_feature, (ChunkSource *) random_level_source, level, chunk_x, chunk_y, chunk_data, 0);
}
// No Block Tinting
template <typename T, typename S>
static int32_t Tile_getColor_injection(__attribute__((unused)) T original, __attribute__((unused)) S *self, __attribute__((unused)) LevelSource *level_source, __attribute__((unused)) int x, __attribute__((unused)) int y, __attribute__((unused)) int z) {
static int32_t Tile_getColor_injection() {
return 0xffffff;
}
// Disable Hostile AI In Creative Mode
static Entity *PathfinderMob_findAttackTarget_injection(PathfinderMob *mob) {
// Call Original Method
Entity *target = mob->findAttackTarget();
Entity *target = mob->vtable->findAttackTarget(mob);
// Only modify the AI of monsters
if (mob->getCreatureBaseType() != 1) {
if (mob->vtable->getCreatureBaseType(mob) != 1) {
return target;
}
// Check If Creative Mode
if (target != nullptr && target->isPlayer()) {
if (target != nullptr && target->vtable->isPlayer(target)) {
Player *player = (Player *) target;
Inventory *inventory = player->inventory;
bool is_creative = inventory->is_creative;
@ -494,12 +493,12 @@ static Entity *PathfinderMob_findAttackTarget_injection(PathfinderMob *mob) {
// 3D Chests
static int32_t Tile_getRenderShape_injection(Tile *tile) {
if (tile == Tile::chest) {
if (tile == Tile_chest) {
// Don't Render "Simple" Chest Model
return -1;
} else {
// Call Original Method
return tile->getRenderShape();
return tile->vtable->getRenderShape(tile);
}
}
static ChestTileEntity *ChestTileEntity_injection(ChestTileEntity_constructor_t original, ChestTileEntity *tile_entity) {
@ -518,7 +517,7 @@ static void ModelPart_render_injection(ModelPart *model_part, float scale) {
is_rendering_chest = true;
// Call Original Method
model_part->render(scale);
ModelPart_render(model_part, scale);
// Stop
is_rendering_chest = false;
@ -545,7 +544,7 @@ static ContainerMenu *ContainerMenu_injection(ContainerMenu_constructor_t origin
ChestTileEntity *tile_entity = (ChestTileEntity *) (((unsigned char *) container) - offsetof(ChestTileEntity, container));
bool is_client = tile_entity->is_client;
if (!is_client) {
container->startOpen();
container->vtable->startOpen(container);
}
// Return
@ -557,7 +556,7 @@ static ContainerMenu *ContainerMenu_destructor_injection(ContainerMenu_destructo
ChestTileEntity *tile_entity = (ChestTileEntity *) (((unsigned char *) container) - offsetof(ChestTileEntity, container));
bool is_client = tile_entity->is_client;
if (!is_client) {
container->stopOpen();
container->vtable->stopOpen(container);
}
// Call Original Method
@ -578,7 +577,7 @@ static void glColor4f_injection(__attribute__((unused)) GLfloat red, __attribute
line_width = strtof(custom_line_width, nullptr);
} else {
// Guess
line_width = 1.5f / Gui::InvGuiScale;
line_width = 2 / Gui_InvGuiScale;
}
// Clamp Line Width
float range[2];
@ -621,7 +620,7 @@ static void Player_stopUsingItem_injection(Player_stopUsingItem_t original, Play
}
// Java Light Ramp
static void Dimension_updateLightRamp_injection(__attribute__((unused)) Dimension_updateLightRamp_t original, Dimension *self) {
static void Dimension_updateLightRamp_injection(Dimension *self) {
// https://github.com/ReMinecraftPE/mcpe/blob/d7a8b6baecf8b3b050538abdbc976f690312aa2d/source/world/level/Dimension.cpp#L92-L105
for (int i = 0; i <= 15; i++) {
float f1 = 1.0f - (((float) i) / 15.0f);
@ -633,9 +632,9 @@ static void Dimension_updateLightRamp_injection(__attribute__((unused)) Dimensio
}
// Read Asset File
static AppPlatform_readAssetFile_return_value AppPlatform_readAssetFile_injection(__attribute__((unused)) AppPlatform_readAssetFile_t original, __attribute__((unused)) AppPlatform *app_platform, std::string *path) {
static AppPlatform_readAssetFile_return_value AppPlatform_readAssetFile_injection(__attribute__((unused)) AppPlatform *app_platform, std::string const& path) {
// Open File
std::ifstream stream("data/" + *path, std::ios_base::binary | std::ios_base::ate);
std::ifstream stream("data/" + path, std::ios_base::binary | std::ios_base::ate);
if (!stream) {
// Does Not Exist
AppPlatform_readAssetFile_return_value ret;
@ -666,7 +665,7 @@ static void PauseScreen_init_injection(PauseScreen_init_t original, PauseScreen
Minecraft *minecraft = screen->minecraft;
RakNetInstance *rak_net_instance = minecraft->rak_net_instance;
if (rak_net_instance != nullptr) {
if (rak_net_instance->isServer()) {
if (rak_net_instance->vtable->isServer(rak_net_instance)) {
// Add Button
std::vector<Button *> *rendered_buttons = &screen->rendered_buttons;
std::vector<Button *> *selectable_buttons = &screen->selectable_buttons;
@ -675,7 +674,7 @@ static void PauseScreen_init_injection(PauseScreen_init_t original, PauseScreen
selectable_buttons->push_back(button);
// Update Button Text
screen->updateServerVisibilityText();
PauseScreen_updateServerVisibilityText(screen);
}
}
}
@ -686,24 +685,24 @@ void PaneCraftingScreen_craftSelectedItem_PaneCraftingScreen_recheckRecipes_inje
CItem *item = self->item;
for (size_t i = 0; i < item->ingredients.size(); i++) {
ItemInstance requested_item_instance = item->ingredients[i].requested_item;
Item *requested_item = Item::items[requested_item_instance.id];
ItemInstance *craftingRemainingItem = requested_item->getCraftingRemainingItem(&requested_item_instance);
Item *requested_item = Item_items[requested_item_instance.id];
ItemInstance *craftingRemainingItem = requested_item->vtable->getCraftingRemainingItem(requested_item, &requested_item_instance);
if (craftingRemainingItem != nullptr) {
// Add or drop remainder
LocalPlayer *player = self->minecraft->player;
if (!player->inventory->add(craftingRemainingItem)) {
if (!player->inventory->vtable->add(player->inventory, craftingRemainingItem)) {
// Drop
player->drop(craftingRemainingItem, false);
player->vtable->drop(player, craftingRemainingItem, false);
}
}
}
// Call Original Method
self->recheckRecipes();
PaneCraftingScreen_recheckRecipes(self);
}
ItemInstance *Item_getCraftingRemainingItem_injection(__attribute__((unused)) Item_getCraftingRemainingItem_t original, Item *self, ItemInstance *item_instance) {
ItemInstance *Item_getCraftingRemainingItem_injection(Item *self, ItemInstance *item_instance) {
if (self->craftingRemainingItem != nullptr) {
ItemInstance *ret = new ItemInstance;
ItemInstance *ret = alloc_ItemInstance();
ret->id = self->craftingRemainingItem->id;
ret->count = item_instance->count;
ret->auxiliary = 0;
@ -727,7 +726,7 @@ static void sort_chunks(Chunk **chunks_begin, Chunk **chunks_end, DistanceChunkS
}
for (int i = 0; i < chunks_size; i++) {
Chunk *chunk = chunks_begin[i];
float distance = chunk->distanceToSqr((Entity *) sorter.mob);
float distance = Chunk_distanceToSqr(chunk, (Entity *) sorter.mob);
if ((1024.0 <= distance) && chunk->y < 0x40) {
distance *= 10.0;
}
@ -766,6 +765,7 @@ void init_misc() {
}
// Classic HUD
Gui_blit_renderHearts_original = Gui_blit;
if (feature_has("Classic HUD", server_disabled)) {
use_classic_hud = 1;
overwrite_call((void *) 0x26758, (void *) Gui_renderHearts_GuiComponent_blit_hearts_injection);
@ -773,6 +773,7 @@ void init_misc() {
overwrite_call((void *) 0x268c4, (void *) Gui_renderBubbles_GuiComponent_blit_injection);
overwrite_call((void *) 0x266f8, (void *) Gui_renderHearts_GuiComponent_blit_hearts_injection);
overwrite_call((void *) 0x267c8, (void *) Gui_renderHearts_GuiComponent_blit_hearts_injection);
Gui_blit_renderHearts_original = Gui_renderHearts_GuiComponent_blit_hearts_injection;
}
// Food overlay
@ -822,10 +823,10 @@ void init_misc() {
#ifdef MCPI_HEADLESS_MODE
// Don't Render Game In Headless Mode
overwrite_manual(GameRenderer_render, nop);
overwrite_manual(NinecraftApp_initGLStates, nop);
overwrite_manual(Gui_onConfigChanged, nop);
overwrite_manual(LevelRenderer_generateSky, nop);
overwrite(GameRenderer_render, nop);
overwrite(NinecraftApp_initGLStates, nop);
overwrite(Gui_onConfigChanged, nop);
overwrite(LevelRenderer_generateSky, nop);
#else
// Improved Cursor Rendering
if (feature_has("Improved Cursor Rendering", server_disabled)) {
@ -849,7 +850,7 @@ void init_misc() {
// Remove Forced GUI Lag
if (feature_has("Remove Forced GUI Lag (Can Break Joining Servers)", server_enabled)) {
overwrite_manual(Common_sleepMs, nop);
overwrite(Common_sleepMs, nop);
}
#ifndef MCPI_HEADLESS_MODE
@ -858,7 +859,7 @@ void init_misc() {
#endif
// Fix Graphics Bug When Switching To First-Person While Sneaking
patch_vtable(PlayerRenderer_render, PlayerRenderer_render_injection);
patch_address(PlayerRenderer_render_vtable_addr, PlayerRenderer_render_injection);
// Disable Speed Bridging
if (feature_has("Disable Speed Bridging", server_disabled)) {
@ -874,7 +875,7 @@ void init_misc() {
// Change Grass Color
if (feature_has("Add Biome Colors To Grass", server_disabled)) {
patch_vtable(GrassTile_getColor, GrassTile_getColor_injection);
patch_address(GrassTile_getColor_vtable_addr, GrassTile_getColor_injection);
overwrite_virtual_calls(TallGrass_getColor, TallGrass_getColor_injection);
}
@ -885,11 +886,11 @@ void init_misc() {
// Disable Block Tinting
if (feature_has("Disable Block Tinting", server_disabled)) {
overwrite_virtual_calls(GrassTile_getColor, Tile_getColor_injection);
overwrite_virtual_calls(TallGrass_getColor, Tile_getColor_injection);
overwrite_virtual_calls(StemTile_getColor, Tile_getColor_injection);
overwrite_virtual_calls(LeafTile_getColor, Tile_getColor_injection);
overwrite_virtual_calls(LiquidTile_getColor, Tile_getColor_injection);
patch_address(GrassTile_getColor_vtable_addr, Tile_getColor_injection);
patch_address(TallGrass_getColor_vtable_addr, Tile_getColor_injection);
patch_address(StemTile_getColor_vtable_addr, Tile_getColor_injection);
patch_address(LeafTile_getColor_vtable_addr, Tile_getColor_injection);
overwrite(*LiquidTile_getColor_vtable_addr, Tile_getColor_injection);
}
// Custom GUI Scale
@ -931,7 +932,7 @@ void init_misc() {
#ifndef MCPI_HEADLESS_MODE
// Replace Block Highlight With Outline
if (feature_has("Replace Block Highlight With Outline", server_disabled)) {
overwrite(LevelRenderer_renderHitSelect, LevelRenderer_renderHitOutline);
overwrite((void *) LevelRenderer_renderHitSelect, (void *) LevelRenderer_renderHitOutline);
unsigned char fix_outline_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop"
patch((void *) 0x4d830, fix_outline_patch);
overwrite_call((void *) 0x4d764, (void *) glColor4f_injection);
@ -951,7 +952,7 @@ void init_misc() {
// Java Light Ramp
if (feature_has("Use Java Beta 1.3 Light Ramp", server_disabled)) {
overwrite_virtual_calls(Dimension_updateLightRamp, Dimension_updateLightRamp_injection);
overwrite(*Dimension_updateLightRamp_vtable_addr, Dimension_updateLightRamp_injection);
}
// Fix used items transferring durability
@ -959,7 +960,7 @@ void init_misc() {
overwrite_calls(Player_stopUsingItem, Player_stopUsingItem_injection);
// Fix invalid ItemInHandRenderer texture cache
if (feature_has("Fix Held Item Caching", server_disabled)) {
if (feature_has("Disable Buggy Held Item Caching", server_disabled)) {
// This works by forcing MCPI to always use the branch that enables using the
// cache, but then patches that as well to do the opposite
uchar ensure_equal_patch[] = {0x07, 0x00, 0x57, 0xe1}; // "cmp r7, r7"
@ -970,7 +971,7 @@ void init_misc() {
// Implement AppPlatform::readAssetFile So Translations Work
if (feature_has("Load Language Files", server_enabled)) {
overwrite_virtual_calls(AppPlatform_readAssetFile, AppPlatform_readAssetFile_injection);
overwrite(*AppPlatform_readAssetFile_vtable_addr, AppPlatform_readAssetFile_injection);
}
// Fix Pause Menu
@ -981,16 +982,16 @@ void init_misc() {
// Implement Crafting Remainders
overwrite_call((void *) 0x2e230, (void *) PaneCraftingScreen_craftSelectedItem_PaneCraftingScreen_recheckRecipes_injection);
overwrite_virtual_calls(Item_getCraftingRemainingItem, Item_getCraftingRemainingItem_injection);
overwrite(*Item_getCraftingRemainingItem_vtable_addr, Item_getCraftingRemainingItem_injection);
// Replace 2011 std::sort With Optimized(TM) Code
if (feature_has("Optimized Chunk Sorting", server_enabled)) {
overwrite_manual((void *) 0x51fac, (void *) sort_chunks);
overwrite((void *) 0x51fac, (void *) sort_chunks);
}
// Display Date In Select World Screen
if (feature_has("Display Date In Select World Screen", server_disabled)) {
patch_vtable(AppPlatform_linux_getDateString, AppPlatform_linux_getDateString_injection);
patch_address(AppPlatform_linux_getDateString_vtable_addr, AppPlatform_linux_getDateString_injection);
}
// Init Logging

View File

@ -122,7 +122,7 @@ static void RakNetInstance_pingForHosts_injection(RakNetInstance_pingForHosts_t
// Add External Servers
iterate_servers([rak_peer](const char *address, int port) {
rak_peer->Ping(address, port, 1, 0);
rak_peer->vtable->Ping(rak_peer, address, port, 1, 0);
});
}

View File

@ -1,268 +0,0 @@
#include <libreborn/libreborn.h>
#include <symbols/minecraft.h>
#include <GLES/gl.h>
#include <mods/touch/touch.h>
#include <mods/misc/misc.h>
#include "options-internal.h"
// Button IDs
#define DISCORD_ID 0
#define BACK_ID 1
#define INFO_ID_START 2
// Constants
static int line_button_padding = 8;
static int line_height = 8;
static int line_button_height = (line_button_padding * 2) + line_height;
static int padding = 4;
static int bottom_padding = padding;
static int inner_padding = padding;
static int title_padding = 8;
static int info_text_y_offset = (line_button_height - line_height) / 2;
static int content_y_offset_top = (title_padding * 2) + line_height;
static int content_y_offset_bottom = (bottom_padding * 2) + line_button_height;
// Extra Version Info
static std::string extra_version_info =
#ifdef MCPI_IS_APPIMAGE_BUILD
"AppImage"
#elif defined(MCPI_IS_FLATPAK_BUILD)
"Flatpak"
#else
""
#endif
;
static std::string extra_version_info_full = !extra_version_info.empty() ? (" (" + extra_version_info + ")") : "";
// Profile Directory
static std::string profile_directory_suffix =
#ifdef MCPI_IS_FLATPAK_BUILD
"/.var/app/" MCPI_APP_ID
#endif
HOME_SUBDIRECTORY_FOR_GAME_DATA
;
static std::string get_profile_directory_url() {
const char *home = getenv("HOME");
if (home == nullptr) {
IMPOSSIBLE();
}
return std::string("file://") + home + profile_directory_suffix;
}
// Info Data
struct info_line {
std::string (*get_text)();
std::string button_url;
std::string button_text;
};
std::string info_sound_data_state = "N/A";
static info_line info[] = {
{
.get_text = []() {
return std::string("Version: v") + reborn_get_version() + extra_version_info_full;
},
.button_url = "https://gitea.thebrokenrail.com/minecraft-pi-reborn/minecraft-pi-reborn/src/branch/master/docs/CHANGELOG.md",
.button_text = "Changelog"
},
{
.get_text = []() {
return std::string("Profile Directory");
},
.button_url = get_profile_directory_url(),
.button_text = "Open"
},
{
.get_text = []() {
return std::string("Sound Data: ") + info_sound_data_state;
},
.button_url = "https://gitea.thebrokenrail.com/minecraft-pi-reborn/minecraft-pi-reborn/src/branch/master/docs/SOUND.md",
.button_text = "More Info"
},
};
#define info_size int(sizeof(info) / sizeof(info_line))
// Positioned Info
struct info_pos {
int x;
int y;
};
struct info_line_position {
info_pos text;
info_pos button;
};
static info_line_position positioned_info[info_size];
static int content_height = 0;
static int line_button_width = 0;
static void position_info(Font *font, int width, int height) {
// First Stage (Find Max Text Width)
int info_text_width = 0;
for (int i = 0; i < info_size; i++) {
std::string text = info[i].get_text();
int text_width = font->width(&text);
if (text_width > info_text_width) {
info_text_width = text_width;
}
}
// Second Stage (Initial Positioning)
int y = 0;
for (int i = 0; i < info_size; i++) {
// Padding
if (i != 0) {
y += padding;
}
// Y
positioned_info[i].button.y = y;
positioned_info[i].text.y = y + info_text_y_offset;
// X
positioned_info[i].button.x = info_text_width + padding;
positioned_info[i].text.x = 0;
// Advance
y += line_button_height;
}
// Third Stage (Find Line Button Width)
line_button_width = 0;
for (int i = 0; i < info_size; i++) {
int text_width = font->width(&info[i].button_text);
if (text_width > line_button_width) {
line_button_width = text_width;
}
}
line_button_width += line_button_padding * 2;
// Fourth Stage (Centering)
int info_height = y;
int info_width = info_text_width + padding + line_button_width;
content_height = height - content_y_offset_top - content_y_offset_bottom;
int info_y_offset = ((content_height - info_height) / 2) + content_y_offset_top;
int info_x_offset = (width - info_width) / 2;
for (int i = 0; i < info_size; i++) {
positioned_info[i].button.x += info_x_offset;
positioned_info[i].button.y += info_y_offset;
positioned_info[i].text.x += info_x_offset;
positioned_info[i].text.y += info_y_offset;
}
}
// Open URL
static void open_url(const std::string &url) {
int return_code;
const char *command[] = {"xdg-open", url.c_str(), nullptr};
char *output = run_command(command, &return_code, nullptr);
if (output != nullptr) {
free(output);
}
if (!is_exit_status_success(return_code)) {
WARN("Unable To Open URL: %s", url.c_str());
}
}
// Create VTable
CUSTOM_VTABLE(info_screen, Screen) {
// Buttons
static Button *discord;
static Button *back;
static Button *info_buttons[info_size];
// Init
vtable->init = [](Screen *self) {
// Info
for (int i = 0; i < info_size; i++) {
Button *button = touch_create_button(INFO_ID_START + i, info[i].button_text);
self->rendered_buttons.push_back(button);
self->selectable_buttons.push_back(button);
info_buttons[i] = button;
}
// Discord Button
discord = touch_create_button(DISCORD_ID, "Discord");
self->rendered_buttons.push_back(discord);
self->selectable_buttons.push_back(discord);
// Back Button
back = touch_create_button(BACK_ID, "Back");
self->rendered_buttons.push_back(back);
self->selectable_buttons.push_back(back);
};
// Handle Back
vtable->handleBackEvent = [](Screen *self, bool do_nothing) {
if (!do_nothing) {
OptionsScreen *screen = new OptionsScreen;
ALLOC_CHECK(screen);
screen->constructor();
self->minecraft->setScreen((Screen *) screen);
}
return true;
};
// Rendering
static Screen_render_t original_render = vtable->render;
vtable->render = [](Screen *self, int x, int y, float param_1) {
// Background
misc_render_background(80, self->minecraft, 0, 0, self->width, self->height);
misc_render_background(32, self->minecraft, 0, content_y_offset_top, self->width, content_height);
// Call Original Method
original_render(self, x, y, param_1);
// Title
std::string title = "Reborn Information";
self->drawCenteredString(self->font, &title, self->width / 2, title_padding, 0xffffffff);
// Info Text
for (int i = 0; i < info_size; i++) {
std::string text = info[i].get_text();
self->drawString(self->font, &text, positioned_info[i].text.x, positioned_info[i].text.y, 0xffffffff);
}
};
// Positioning
vtable->setupPositions = [](Screen *self) {
// Height/Width
int width = 120;
discord->width = back->width = width;
discord->height = back->height = line_button_height;
// X/Y
discord->y = back->y = self->height - bottom_padding - line_button_height;
discord->x = (self->width / 2) - inner_padding - width;
back->x = (self->width / 2) + inner_padding;
// Info
position_info(self->font, self->width, self->height);
for (int i = 0; i < info_size; i++) {
Button *button = info_buttons[i];
button->width = line_button_width;
button->height = line_button_height;
button->x = positioned_info[i].button.x;
button->y = positioned_info[i].button.y;
}
};
// Cleanup
vtable->removed = [](Screen *self) {
for (Button *button : self->rendered_buttons) {
button->destructor_deleting();
}
};
// Handle Button Click
vtable->buttonClicked = [](Screen *self, Button *button) {
if (button->id == BACK_ID) {
// Back
self->handleBackEvent(false);
} else if (button->id == DISCORD_ID) {
// Open Discord Invite
open_url(MCPI_DISCORD_INVITE);
} else if (button->id >= INFO_ID_START) {
// Open Info URL
int i = button->id - INFO_ID_START;
open_url(info[i].button_url);
}
};
}
// Create Screen
Screen *_create_options_info_screen() {
// Allocate
Screen *screen = new Screen;
ALLOC_CHECK(screen);
screen->constructor();
// Set VTable
screen->vtable = get_info_screen_vtable();
// Return
return screen;
}

View File

@ -1,7 +0,0 @@
#pragma once
#include <symbols/minecraft.h>
__attribute__((visibility("internal"))) void _init_options_ui();
__attribute__((visibility("internal"))) extern Options *stored_options;
__attribute__((visibility("internal"))) Screen *_create_options_info_screen();

View File

@ -10,10 +10,8 @@
#include <mods/init/init.h>
#include <mods/home/home.h>
#include "options-internal.h"
// Force Mob Spawning
static bool LevelData_getSpawnMobs_injection(__attribute__((unused)) LevelData *level_data) {
static bool LevelData_getSpawnMobs_injection(__attribute__((unused)) unsigned char *level_data) {
return true;
}
@ -51,7 +49,7 @@ __attribute__((destructor)) static void _free_safe_username() {
static int render_distance;
// Configure Options
Options *stored_options = nullptr;
static Options *stored_options = nullptr;
static void Options_initDefaultValue_injection(Options_initDefaultValue_t original, Options *options) {
// Call Original Method
original(options);
@ -79,27 +77,40 @@ static void Minecraft_init_injection(Minecraft_init_t original, Minecraft *minec
// Smooth Lighting
static bool TileRenderer_tesselateBlockInWorld_injection(TileRenderer_tesselateBlockInWorld_t original, TileRenderer *tile_renderer, Tile *tile, int32_t x, int32_t y, int32_t z) {
// Set Variable
Minecraft::useAmbientOcclusion = stored_options->ambient_occlusion;
Minecraft_useAmbientOcclusion = stored_options->ambient_occlusion;
// Call Original Method
return original(tile_renderer, tile, x, y, z);
}
// Fix Initial Option Button Rendering
// The calling function doesn't exist in MCPE v0.6.1, so its name is unknown.
static OptionButton *OptionsPane_unknown_toggle_creating_function_OptionButton_injection(OptionButton *option_button, Options_Option *option) {
// Call Original Method
OptionButton *ret = OptionButton_constructor(option_button, option);
// Setup Image
OptionButton_updateImage(option_button, stored_options);
// Return
return ret;
}
// Actually Save options.txt
// Hook Last Options::addOptionToSaveOutput Call
static void Options_save_Options_addOptionToSaveOutput_injection(Options *options, std::vector<std::string> *data, std::string option, int32_t value) {
// Call Original Method
options->addOptionToSaveOutput(data, option, value);
Options_addOptionToSaveOutput(options, data, option, value);
// Save Fancy Graphics
options->addOptionToSaveOutput(data, "gfx_fancygraphics", options->fancy_graphics);
Options_addOptionToSaveOutput(options, data, "gfx_fancygraphics", options->fancy_graphics);
// Save 3D Anaglyph
options->addOptionToSaveOutput(data, "gfx_anaglyph", options->anaglyph_3d);
Options_addOptionToSaveOutput(options, data, "gfx_anaglyph", options->anaglyph_3d);
// Save File
OptionsFile *options_file = &options->options_file;
options_file->save(data);
OptionsFile_save(options_file, data);
}
// MCPI's OptionsFile::getOptionStrings is broken, this is the version in v0.7.0
@ -154,11 +165,80 @@ static char *get_new_options_txt_path() {
}
#endif
// Modify Option Toggles
static void OptionsPane_unknown_toggle_creating_function_injection(OptionsPane_unknown_toggle_creating_function_t original, OptionsPane *options_pane, uint32_t group_id, std::string *name_ptr, Options_Option *option) {
// Modify
std::string name = *name_ptr;
std::string new_name = name;
if (name == "Fancy Graphics") {
option = &Options_Option_GRAPHICS;
} else if (name == "Soft shadows") {
option = &Options_Option_AMBIENT_OCCLUSION;
} else if (name == "Fancy Skies" || name == "Animated water") {
// These have no corresponding option, so disable the toggle.
return;
} else if (name == "Third person camera") {
// This isn't saved/loaded, so disable the toggle.
return;
} else if (name == "Lefty" || name == "Use touch screen" || name == "Split touch controls") {
// These toggles require touch support, so disable them.
return;
} else if (name == "Vibrate on destroy") {
// This toggle requires vibration support, so disable it.
return;
} else if (name == "Invert X-axis") {
// Fix Incorrect Name
new_name = "Invert Y-axis";
}
// Call Original Method
original(options_pane, group_id, &new_name, option);
// Add 3D Anaglyph
if (option == &Options_Option_GRAPHICS) {
std::string cpp_string = "3D Anaglyph";
original(options_pane, group_id, &cpp_string, &Options_Option_ANAGLYPH);
}
// Add Peaceful Mode
if (option == &Options_Option_SERVER_VISIBLE) {
std::string cpp_string = "Peaceful mode";
original(options_pane, group_id, &cpp_string, &Options_Option_DIFFICULTY);
}
}
// Add Missing Options To Options::getBooleanValue
static bool Options_getBooleanValue_injection(Options_getBooleanValue_t original, Options *options, Options_Option *option) {
// Check
if (option == &Options_Option_GRAPHICS) {
return options->fancy_graphics;
} else if (option == &Options_Option_DIFFICULTY) {
return options->game_difficulty == 0;
} else {
// Call Original Method
return original(options, option);
}
}
// Fix Difficulty When Toggling
static void OptionButton_toggle_Options_save_injection(Options *self) {
// Fix Value
if (self->game_difficulty == 1) {
// Disable Peaceful
self->game_difficulty = 2;
} else if (self->game_difficulty == 3) {
// Switch To Peaceful
self->game_difficulty = 0;
}
// Call Original Method
Options_save(self);
}
// Init
void init_options() {
// Force Mob Spawning
if (feature_has("Force Mob Spawning", server_auto)) {
overwrite(LevelData_getSpawnMobs, LevelData_getSpawnMobs_injection);
overwrite((void *) LevelData_getSpawnMobs, (void *) LevelData_getSpawnMobs_injection);
}
// Render Distance
@ -172,11 +252,11 @@ void init_options() {
// Change Username
const char *username = get_username();
DEBUG("Setting Username: %s", username);
if (strcmp(Strings::default_username, "StevePi") != 0) {
if (strcmp(Strings_default_username, "StevePi") != 0) {
ERR("Default Username Is Invalid");
}
safe_username = to_cp437(username);
patch_address((void *) &Strings::default_username, (void *) safe_username);
patch_address((void *) Strings_default_username_pointer, (void *) safe_username);
// Disable Autojump By Default
if (feature_has("Disable Autojump By Default", server_disabled)) {
@ -197,12 +277,36 @@ void init_options() {
// NOP
unsigned char nop_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop"
// Fix Options Screen
if (feature_has("Fix Options Screen", server_disabled)) {
// Fix Initial Option Button Rendering
overwrite_call((void *) 0x24510, (void *) OptionsPane_unknown_toggle_creating_function_OptionButton_injection);
// "Gui Scale" slider is broken, so disable it.
patch((void *) 0x35a10, nop_patch);
// "Vibrate on destroy" is disabled, so "Feedback" is empty, so disable it.
patch((void *) 0x35960, nop_patch);
// Disconnect "This works?" Slider From Difficulty
unsigned char this_works_slider_patch[4] = {0x00, 0x30, 0xa0, 0xe3}; // "mov r3, #0x0"
patch((void *) 0x3577c, this_works_slider_patch);
// Modify Option Toggles
overwrite_calls(OptionsPane_unknown_toggle_creating_function, OptionsPane_unknown_toggle_creating_function_injection);
// Add Missing Options To Options::getBooleanValue
overwrite_calls(Options_getBooleanValue, Options_getBooleanValue_injection);
// Fix Difficulty When Toggling
overwrite_call((void *) 0x1cd00, (void *) OptionButton_toggle_Options_save_injection);
}
// Actually Save options.txt
overwrite_call((void *) 0x197fc, (void *) Options_save_Options_addOptionToSaveOutput_injection);
// Fix options.txt Path
patch_address((void *) &Strings::options_txt_path, (void *) get_new_options_txt_path());
patch_address((void *) Strings_options_txt_path_pointer, (void *) get_new_options_txt_path());
// When Loading, options.txt Should Be Opened In Read Mode
patch_address((void *) &Strings::options_txt_fopen_mode_when_loading, (void *) "r");
patch_address((void *) Strings_options_txt_fopen_mode_when_loading_pointer, (void *) "r");
// Fix OptionsFile::getOptionStrings
overwrite(OptionsFile_getOptionStrings, OptionsFile_getOptionStrings_injection);
@ -221,7 +325,7 @@ void init_options() {
// Replace "feedback_vibration" Loading/Saving With "gfx_ao"
{
// Replace String
patch_address((void *) &Strings::feedback_vibration_options_txt_name, (void *) "gfx_ao");
patch_address((void *) Strings_feedback_vibration_options_txt_name_pointer, (void *) "gfx_ao");
// Loading
unsigned char offset = (unsigned char) offsetof(Options, ambient_occlusion);
unsigned char gfx_ao_loading_patch[4] = {offset, 0x10, 0x84, 0xe2}; // "add r1, r4, #OFFSET"
@ -234,7 +338,7 @@ void init_options() {
// Replace "gfx_lowquality" Loading With "gfx_anaglyph"
{
// Replace String
patch_address((void *) &Strings::gfx_lowquality_options_txt_name, (void *) "gfx_anaglyph");
patch_address((void *) Strings_gfx_lowquality_options_txt_name_pointer, (void *) "gfx_anaglyph");
// Loading
unsigned char offset = (unsigned char) offsetof(Options, anaglyph_3d);
unsigned char gfx_anaglyph_loading_patch[4] = {offset, 0x10, 0x84, 0xe2}; // "add r1, r4, #OFFSET"
@ -243,7 +347,4 @@ void init_options() {
patch((void *) 0x19414, nop_patch);
patch((void *) 0x1941c, nop_patch);
}
// UI
_init_options_ui();
}

View File

@ -1,197 +0,0 @@
#include <string>
#include <algorithm>
#include <libreborn/libreborn.h>
#include <symbols/minecraft.h>
#include <mods/feature/feature.h>
#include "options-internal.h"
// Fix Initial Option Button Rendering
// The calling function doesn't exist in MCPE v0.6.1, so its name is unknown.
static OptionButton *OptionsPane_unknown_toggle_creating_function_OptionButton_injection(OptionButton *option_button, Options_Option *option) {
// Call Original Method
OptionButton *ret = option_button->constructor(option);
// Setup Image
option_button->updateImage(stored_options);
// Return
return ret;
}
// Modify Option Toggles
static void OptionsPane_unknown_toggle_creating_function_injection(OptionsPane_unknown_toggle_creating_function_t original, OptionsPane *options_pane, uint32_t group_id, std::string *name_ptr, Options_Option *option) {
// Modify
std::string name = *name_ptr;
std::string new_name = name;
if (name == "Fancy Graphics") {
option = &Options_Option::GRAPHICS;
} else if (name == "Soft shadows") {
option = &Options_Option::AMBIENT_OCCLUSION;
} else if (name == "Fancy Skies" || name == "Animated water") {
// These have no corresponding option, so disable the toggle.
return;
} else if (name == "Third person camera") {
// This isn't saved/loaded, so disable the toggle.
return;
} else if (name == "Lefty" || name == "Use touch screen" || name == "Split touch controls") {
// These toggles require touch support, so disable them.
return;
} else if (name == "Vibrate on destroy") {
// This toggle requires vibration support, so disable it.
return;
} else if (name == "Invert X-axis") {
// Fix Incorrect Name
new_name = "Invert Y-axis";
}
// Call Original Method
original(options_pane, group_id, &new_name, option);
// Add 3D Anaglyph
if (option == &Options_Option::GRAPHICS) {
std::string cpp_string = "3D Anaglyph";
original(options_pane, group_id, &cpp_string, &Options_Option::ANAGLYPH);
}
// Add Peaceful Mode
if (option == &Options_Option::SERVER_VISIBLE) {
std::string cpp_string = "Peaceful mode";
original(options_pane, group_id, &cpp_string, &Options_Option::DIFFICULTY);
}
}
// Add Missing Options To Options::getBooleanValue
static bool Options_getBooleanValue_injection(Options_getBooleanValue_t original, Options *options, Options_Option *option) {
// Check
if (option == &Options_Option::GRAPHICS) {
return options->fancy_graphics;
} else if (option == &Options_Option::DIFFICULTY) {
return options->game_difficulty == 0;
} else {
// Call Original Method
return original(options, option);
}
}
// Fix Difficulty When Toggling
static void OptionButton_toggle_Options_save_injection(Options *self) {
// Fix Value
if (self->game_difficulty == 1) {
// Disable Peaceful
self->game_difficulty = 2;
} else if (self->game_difficulty == 3) {
// Switch To Peaceful
self->game_difficulty = 0;
}
// Call Original Method
self->save();
}
// Add "Reborn" Info Button
#define INFO_BUTTON_ID 99
static void OptionsScreen_init_injection(OptionsScreen_init_t original, OptionsScreen *self) {
// Call Original Method
original(self);
// Add Button
Touch_TButton *button = new Touch_TButton;
ALLOC_CHECK(button);
std::string name = "Reborn";
button->constructor(INFO_BUTTON_ID, &name);
self->rendered_buttons.push_back((Button *) button);
self->selectable_buttons.push_back((Button *) button);
}
static void OptionsScreen_setupPositions_injection(OptionsScreen_setupPositions_t original, OptionsScreen *self) {
// Call Original Method
original(self);
// Find Button
Button *prevButton = nullptr;
Button *button = nullptr;
for (Button *x : self->selectable_buttons) {
if (x->id == INFO_BUTTON_ID) {
button = x;
break;
}
prevButton = x;
}
if (button == nullptr || prevButton == nullptr) {
IMPOSSIBLE();
}
// Setup Button
button->width = prevButton->width;
button->height = prevButton->height;
button->x = prevButton->x;
button->y = prevButton->y + prevButton->height;
}
static void OptionsScreen_buttonClicked_injection(OptionsScreen_buttonClicked_t original, OptionsScreen *self, Button *button) {
// Check ID
if (button->id == INFO_BUTTON_ID) {
// Show Screen
self->minecraft->setScreen(_create_options_info_screen());
} else {
// Call Original Method
original(self, button);
}
}
static void OptionsScreen_removed_injection(OptionsScreen_removed_t original, OptionsScreen *self) {
// Delete Button
Button *button = nullptr;
for (Button *x : self->selectable_buttons) {
if (x->id == INFO_BUTTON_ID) {
button = x;
break;
}
}
if (button == nullptr) {
IMPOSSIBLE();
}
button->destructor_deleting();
// Call Original Method
original(self);
}
// Init
void _init_options_ui() {
// Fix Options Screen
unsigned char nop_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop"
if (feature_has("Fix Options Screen", server_disabled)) {
// Fix Initial Option Button Rendering
overwrite_call((void *) 0x24510, (void *) OptionsPane_unknown_toggle_creating_function_OptionButton_injection);
// "Gui Scale" slider is broken, so disable it.
patch((void *) 0x35a10, nop_patch);
// "Vibrate on destroy" is disabled, so "Feedback" is empty, so disable it.
patch((void *) 0x35960, nop_patch);
// Disconnect "This works?" Slider From Difficulty
unsigned char this_works_slider_patch[4] = {0x00, 0x30, 0xa0, 0xe3}; // "mov r3, #0x0"
patch((void *) 0x3577c, this_works_slider_patch);
// Modify Option Toggles
overwrite_calls(OptionsPane_unknown_toggle_creating_function, OptionsPane_unknown_toggle_creating_function_injection);
// Add Missing Options To Options::getBooleanValue
overwrite_calls(Options_getBooleanValue, Options_getBooleanValue_injection);
// Fix Difficulty When Toggling
overwrite_call((void *) 0x1cd00, (void *) OptionButton_toggle_Options_save_injection);
}
// Info Button
if (feature_has("Add Reborn Info To Options", server_disabled)) {
// Add Button
overwrite_virtual_calls(OptionsScreen_init, OptionsScreen_init_injection);
// Position Button
overwrite_virtual_calls(OptionsScreen_setupPositions, OptionsScreen_setupPositions_injection);
// Handle Click
overwrite_virtual_calls(OptionsScreen_buttonClicked, OptionsScreen_buttonClicked_injection);
// Cleanup
overwrite_virtual_calls(OptionsScreen_removed, OptionsScreen_removed_injection);
}
}

View File

@ -31,8 +31,16 @@
// --only-generate: Ony Generate World And Then Exit
static bool only_generate = false;
__attribute__((constructor)) static void _init_only_generate() {
only_generate = getenv("_MCPI_ONLY_GENERATE") != nullptr;
__attribute__((constructor)) static void _init_only_generate(int argc, char *argv[]) {
// Iterate Arguments
for (int i = 1; i < argc; i++) {
// Check Argument
if (strcmp(argv[i], "--only-generate") == 0) {
// Enabled
only_generate = true;
break;
}
}
}
// Server Properties
@ -84,21 +92,21 @@ static void start_world(Minecraft *minecraft) {
settings.seed = seed;
// Select Level
minecraft->selectLevel(&world_name, &world_name, &settings);
minecraft->vtable->selectLevel(minecraft, &world_name, &world_name, &settings);
// Don't Open Port When Using --only-generate
if (!only_generate) {
// Open Port
int port = get_server_properties().get_int("port", DEFAULT_PORT);
INFO("Listening On: %i", port);
minecraft->hostMultiplayer(port);
Minecraft_hostMultiplayer(minecraft, port);
}
// Open ProgressScreen
ProgressScreen *screen = new ProgressScreen;
ProgressScreen *screen = alloc_ProgressScreen();
ALLOC_CHECK(screen);
screen = screen->constructor();
minecraft->setScreen((Screen *) screen);
screen = ProgressScreen_constructor(screen);
Minecraft_setScreen(minecraft, (Screen *) screen);
}
// Check If Running In Whitelist Mode
@ -156,7 +164,7 @@ static RakNet_RakNetGUID get_rak_net_guid(Player *player) {
}
static RakNet_SystemAddress get_system_address(RakNet_RakPeer *rak_peer, RakNet_RakNetGUID guid) {
// Get SystemAddress
return rak_peer->GetSystemAddressFromGuid(guid);
return rak_peer->vtable->GetSystemAddressFromGuid(rak_peer, guid);
}
static RakNet_RakPeer *get_rak_peer(Minecraft *minecraft) {
return minecraft->rak_net_instance->peer;
@ -164,7 +172,7 @@ static RakNet_RakPeer *get_rak_peer(Minecraft *minecraft) {
static char *get_rak_net_guid_ip(RakNet_RakPeer *rak_peer, RakNet_RakNetGUID guid) {
RakNet_SystemAddress address = get_system_address(rak_peer, guid);
// Get IP
return address.ToString(false, '|');
return RakNet_SystemAddress_ToString(&address, false, '|');
}
// Get IP From Player
@ -199,7 +207,7 @@ static void ban_callback(Minecraft *minecraft, std::string username, Player *pla
// Kill Player
static void kill_callback(__attribute__((unused)) Minecraft *minecraft, __attribute__((unused)) std::string username, Player *player) {
player->hurt(nullptr, INT32_MAX);
player->vtable->hurt(player, nullptr, INT32_MAX);
INFO("Killed: %s", username.c_str());
}
@ -215,9 +223,9 @@ static void handle_server_stop(Minecraft *minecraft) {
// Save And Exit
Level *level = get_level(minecraft);
if (level != nullptr) {
level->saveLevelData();
Level_saveLevelData_injection(level);
}
minecraft->leaveGame(false);
Minecraft_leaveGame(minecraft, false);
// Stop Game
SDL_Event event;
event.type = SDL_QUIT;
@ -228,7 +236,7 @@ static void handle_server_stop(Minecraft *minecraft) {
// Track TPS
#define NANOSECONDS_IN_SECOND 1000000000ll
static long long int get_time() {
timespec ts = {};
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;
@ -258,7 +266,7 @@ static volatile bool stdin_buffer_complete = false;
static volatile char *stdin_buffer = nullptr;
static void *read_stdin_thread(__attribute__((unused)) void *data) {
// Loop
while (true) {
while (1) {
int bytes_available;
if (ioctl(fileno(stdin), FIONREAD, &bytes_available) == -1) {
bytes_available = 0;
@ -293,7 +301,7 @@ __attribute__((destructor)) static void _free_stdin_buffer() {
// Handle Commands
static void handle_commands(Minecraft *minecraft) {
// Check If Level Is Generated
if (minecraft->isLevelGenerated() && stdin_buffer_complete) {
if (Minecraft_isLevelGenerated(minecraft) && stdin_buffer_complete) {
// Command Ready; Run It
if (stdin_buffer != nullptr) {
ServerSideNetworkHandler *server_side_network_handler = get_server_side_network_handler(minecraft);
@ -325,7 +333,7 @@ static void handle_commands(Minecraft *minecraft) {
char *safe_message = to_cp437(message.c_str());
std::string cpp_string = safe_message;
// Post Message To Chat
server_side_network_handler->displayGameMessage(&cpp_string);
ServerSideNetworkHandler_displayGameMessage(server_side_network_handler, &cpp_string);
// Free
free(safe_message);
} else if (data == list_command) {
@ -373,7 +381,7 @@ static void Minecraft_update_injection(Minecraft *minecraft) {
}
// Handle --only-generate
if (only_generate && minecraft->isLevelGenerated()) {
if (only_generate && Minecraft_isLevelGenerated(minecraft)) {
// Request Exit
compat_request_exit();
// Disable Special Behavior After Requesting Exit
@ -425,7 +433,7 @@ static bool is_ip_in_blacklist(const char *ip) {
}
// Ban Players
static bool RakNet_RakPeer_IsBanned_injection(__attribute__((unused)) RakNet_RakPeer_IsBanned_t original, __attribute__((unused)) RakNet_RakPeer *rakpeer, const char *ip) {
static bool RakNet_RakPeer_IsBanned_injection(__attribute__((unused)) RakNet_RakPeer *rakpeer, const char *ip) {
// Check List
bool ret = is_ip_in_blacklist(ip);
if (is_whitelist()) {
@ -438,7 +446,7 @@ static bool RakNet_RakPeer_IsBanned_injection(__attribute__((unused)) RakNet_Rak
// Log IPs
static Player *ServerSideNetworkHandler_onReady_ClientGeneration_ServerSideNetworkHandler_popPendingPlayer_injection(ServerSideNetworkHandler *server_side_network_handler, RakNet_RakNetGUID *guid) {
// Call Original Method
Player *player = server_side_network_handler->popPendingPlayer(guid);
Player *player = ServerSideNetworkHandler_popPendingPlayer(server_side_network_handler, guid);
// Check If Player Is Null
if (player != nullptr) {
@ -568,7 +576,7 @@ static void server_init() {
unsigned char max_players_patch[4] = {get_max_players(), 0x30, 0xa0, 0xe3}; // "mov r3, #MAX_PLAYERS"
patch((void *) 0x166d0, max_players_patch);
// Custom Banned IP List
overwrite_virtual_calls(RakNet_RakPeer_IsBanned, RakNet_RakPeer_IsBanned_injection);
patch_address(RakNet_RakPeer_IsBanned_vtable_addr, (void *) RakNet_RakPeer_IsBanned_injection);
// Show The MineCon Icon Next To MOTD In Server List
if (get_server_properties().get_bool("show-minecon-badge", DEFAULT_SHOW_MINECON_BADGE)) {

View File

@ -33,23 +33,23 @@ static int32_t sdl_key_to_minecraft_key_injection(Common_sdl_key_to_minecraft_ke
static void LocalPlayer_openTextEdit_injection(LocalPlayer *local_player, TileEntity *sign) {
if (sign->type == 4) {
Minecraft *minecraft = local_player->minecraft;
TextEditScreen *screen = new TextEditScreen;
TextEditScreen *screen = alloc_TextEditScreen();
ALLOC_CHECK(screen);
screen = screen->constructor((SignTileEntity *) sign);
minecraft->setScreen((Screen *) screen);
screen = TextEditScreen_constructor(screen, (SignTileEntity *) sign);
Minecraft_setScreen(minecraft, (Screen *) screen);
}
}
// Store Text Input
void sign_key_press(char key) {
Keyboard::_inputText.push_back(key);
Keyboard__inputText.push_back(key);
}
// Init
void init_sign() {
if (feature_has("Fix Sign Placement", server_disabled)) {
// Fix Signs
patch_vtable(LocalPlayer_openTextEdit, LocalPlayer_openTextEdit_injection);
patch_address(LocalPlayer_openTextEdit_vtable_addr, (void *) LocalPlayer_openTextEdit_injection);
}
// Handle Backspace

View File

@ -69,12 +69,12 @@ static int32_t Textures_loadAndBindTexture_injection(Textures *textures, __attri
// Change Texture
static std::string new_texture;
if (new_texture.length() == 0) {
std::string username = base64_encode(Strings::default_username);
std::string username = base64_encode(Strings_default_username);
new_texture = '$' + username;
}
// Call Original Method
return textures->loadAndBindTexture(&new_texture);
return Textures_loadAndBindTexture(textures, &new_texture);
}
// Init

View File

@ -11,7 +11,6 @@
// Resolve Source File Path
#define SOURCE_FILE_BASE "data/libminecraftpe.so"
extern std::string info_sound_data_state;
std::string _sound_get_source_file() {
static bool source_loaded = false;
static std::string source;
@ -39,11 +38,9 @@ std::string _sound_get_source_file() {
// Fail
WARN("Audio Source File Doesn't Exist: " SOURCE_FILE_BASE " (See: https://gitea.thebrokenrail.com/minecraft-pi-reborn/minecraft-pi-reborn/src/branch/master/docs/SOUND.md)");
source.assign("");
info_sound_data_state = "Missing";
} else {
// Set
source.assign(path);
info_sound_data_state = "Loaded";
}
// Free
@ -69,11 +66,11 @@ static void play(std::string name, float x, float y, float z, float volume, floa
media_audio_play(source.c_str(), resolved_name.c_str(), x, y, z, pitch, volume, is_ui);
}
}
static void SoundEngine_playUI_injection(__attribute__((unused)) SoundEngine *sound_engine, std::string *name, float volume, float pitch) {
play(*name, 0, 0, 0, volume, pitch, true);
static void SoundEngine_playUI_injection(__attribute__((unused)) unsigned char *sound_engine, std::string const& name, float volume, float pitch) {
play(name, 0, 0, 0, volume, pitch, true);
}
static void SoundEngine_play_injection(__attribute__((unused)) SoundEngine *sound_engine, std::string *name, float x, float y, float z, float volume, float pitch) {
play(*name, x, y, z, volume, pitch, false);
static void SoundEngine_play_injection(__attribute__((unused)) unsigned char *sound_engine, std::string const& name, float x, float y, float z, float volume, float pitch) {
play(name, x, y, z, volume, pitch, false);
}
// Refresh Data

View File

@ -5,7 +5,7 @@
TextInputBox *TextInputBox::create(const std::string &placeholder, const std::string &text) {
// Construct
TextInputBox *self = new TextInputBox;
self->super.constructor();
GuiComponent_constructor(&self->super);
// Setup
self->m_xPos = 0;
@ -111,11 +111,11 @@ void TextInputBox::keyPressed(int key) {
void TextInputBox::tick() {
if (!m_lastFlashed) {
m_lastFlashed = Common::getTimeMs();
m_lastFlashed = Common_getTimeMs();
}
if (m_bFocused) {
if (Common::getTimeMs() > m_lastFlashed + 500) {
if (Common_getTimeMs() > m_lastFlashed + 500) {
m_lastFlashed += 500;
m_bCursorOn ^= 1;
}
@ -131,7 +131,7 @@ void TextInputBox::setFocused(bool b) {
m_bFocused = b;
if (b) {
m_lastFlashed = Common::getTimeMs();
m_lastFlashed = Common_getTimeMs();
m_bCursorOn = true;
m_insertHead = int(m_text.size());
recalculateScroll();
@ -167,7 +167,7 @@ void TextInputBox::charPressed(int k) {
static std::string get_rendered_text(Font *font, int width, int scroll_pos, std::string text) {
std::string rendered_text = text.substr(scroll_pos);
int max_width = width - (PADDING * 2);
while (font->width(&rendered_text) > max_width) {
while (Font_width(font, &rendered_text) > max_width) {
rendered_text.pop_back();
}
return rendered_text;
@ -176,8 +176,8 @@ static std::string get_rendered_text(Font *font, int width, int scroll_pos, std:
static char CURSOR_CHAR = '_';
void TextInputBox::render() {
super.fill(m_xPos, m_yPos, m_xPos + m_width, m_yPos + m_height, 0xFFAAAAAA);
super.fill(m_xPos + 1, m_yPos + 1, m_xPos + m_width - 1, m_yPos + m_height - 1, 0xFF000000);
GuiComponent_fill(&super, m_xPos, m_yPos, m_xPos + m_width, m_yPos + m_height, 0xFFAAAAAA);
GuiComponent_fill(&super, m_xPos + 1, m_yPos + 1, m_xPos + m_width - 1, m_yPos + m_height - 1, 0xFF000000);
int text_color;
int scroll_pos;
@ -194,17 +194,17 @@ void TextInputBox::render() {
rendered_text = get_rendered_text(m_pFont, m_width, scroll_pos, rendered_text);
int textYPos = (m_height - 8) / 2;
super.drawString(m_pFont, &rendered_text, m_xPos + PADDING, m_yPos + textYPos, text_color);
GuiComponent_drawString(&super, m_pFont, &rendered_text, m_xPos + PADDING, m_yPos + textYPos, text_color);
if (m_bCursorOn) {
int cursor_pos = m_insertHead - m_scrollPos;
if (cursor_pos >= 0 && cursor_pos <= int(rendered_text.length())) {
std::string substr = rendered_text.substr(0, cursor_pos);
int xPos = PADDING + m_pFont->width(&substr);
int xPos = PADDING + Font_width(m_pFont, &substr);
std::string str;
str += CURSOR_CHAR;
super.drawString(m_pFont, &str, m_xPos + xPos, m_yPos + textYPos + 2, 0xffffff);
GuiComponent_drawString(&super, m_pFont, &str, m_xPos + xPos, m_yPos + textYPos + 2, 0xffffff);
}
}
}

View File

@ -3,7 +3,7 @@
#include <mods/init/init.h>
// Disable Texture Loading
static Texture AppPlatform_linux_loadTexture_injection(__attribute__((unused)) AppPlatform_linux_loadTexture_t original, __attribute__((unused)) AppPlatform_linux *app_platform, __attribute__((unused)) std::string *path, __attribute__((unused)) bool b) {
static Texture AppPlatform_linux_loadTexture_injection(__attribute__((unused)) AppPlatform_linux *app_platform, __attribute__((unused)) std::string *path, __attribute__((unused)) bool b) {
Texture out;
out.width = 0;
out.height = 0;
@ -19,5 +19,5 @@ static Texture AppPlatform_linux_loadTexture_injection(__attribute__((unused)) A
// Init
void init_textures() {
// Disable Texture Loading
overwrite_virtual_calls(AppPlatform_linux_loadTexture, AppPlatform_linux_loadTexture_injection);
overwrite((void *) AppPlatform_linux_loadTexture_non_virtual, (void *) AppPlatform_linux_loadTexture_injection);
}

View File

@ -42,8 +42,8 @@ CUSTOM_VTABLE(lava_texture, DynamicTexture) {
for (int x = 0; x < 16; x++) {
for (int y = 0; y < 16; y++) {
float f = 0.0F;
int ax = int(Mth::sin((float(x) * float(M_PI) * 2) / 16.0f) * 1.2f);
int ay = int(Mth::sin((float(y) * float(M_PI) * 2) / 16.0f) * 1.2f);
int ax = int(Mth_sin((float(x) * float(M_PI) * 2) / 16.0f) * 1.2f);
int ay = int(Mth_sin((float(y) * float(M_PI) * 2) / 16.0f) * 1.2f);
for (int bx = x - 1; bx <= x + 1; bx++) {
for (int by = y - 1; by <= y + 1; by++) {
int k2 = (bx + ay) & 0xf;
@ -57,7 +57,7 @@ CUSTOM_VTABLE(lava_texture, DynamicTexture) {
self->m_data3[x + y * 16] = 0.0f;
}
self->m_data4[x + y * 16] -= 0.06f;
if (Mth::random() < 0.005f) {
if (Mth_random() < 0.005f) {
self->m_data4[x + y * 16] = 1.5f;
}
}
@ -82,7 +82,7 @@ static DynamicTexture *create_lava_texture() {
// Construct
LavaTexture *texture = new LavaTexture;
ALLOC_CHECK(texture);
texture->super.constructor(Tile::lava->texture);
DynamicTexture_constructor(&texture->super, Tile_lava->texture);
// Set VTable
texture->super.vtable = get_lava_texture_vtable();
// Setup
@ -106,8 +106,8 @@ CUSTOM_VTABLE(lava_side_texture, DynamicTexture) {
for (int x = 0; x < 16; x++) {
for (int y = 0; y < 16; y++) {
float f = 0.0F;
int ax = int(Mth::sin((float(x) * float(M_PI) * 2) / 16.0f) * 1.2f);
int ay = int(Mth::sin((float(y) * float(M_PI) * 2) / 16.0f) * 1.2f);
int ax = int(Mth_sin((float(x) * float(M_PI) * 2) / 16.0f) * 1.2f);
int ay = int(Mth_sin((float(y) * float(M_PI) * 2) / 16.0f) * 1.2f);
for (int bx = x - 1; bx <= x + 1; bx++) {
for (int by = y - 1; by <= y + 1; by++) {
int k2 = (bx + ay) & 0xf;
@ -121,7 +121,7 @@ CUSTOM_VTABLE(lava_side_texture, DynamicTexture) {
self->m_data3[x + y * 16] = 0.0f;
}
self->m_data4[x + y * 16] -= 0.06f;
if (Mth::random() < 0.005f) {
if (Mth_random() < 0.005f) {
self->m_data4[x + y * 16] = 1.5f;
}
}
@ -146,7 +146,7 @@ static DynamicTexture *create_lava_side_texture() {
// Construct
LavaSideTexture *texture = new LavaSideTexture;
ALLOC_CHECK(texture);
texture->super.constructor(Tile::lava->texture + 1);
DynamicTexture_constructor(&texture->super, Tile_lava->texture + 1);
// Set VTable
texture->super.vtable = get_lava_side_texture_vtable();
// Setup
@ -189,7 +189,7 @@ CUSTOM_VTABLE(fire_texture, DynamicTexture) {
uint32_t x;
uint8_t b[4];
} a;
a.x = self->m_random.genrand_int32();
a.x = Random_genrand_int32(&self->m_random);
self->m_data2[i + j * 16] = 0.2f + (((a.b[3] / 256.0f) * 0.1f) + ((((a.b[0] / 256.0f) * (a.b[1] / 256.0f)) * (a.b[2] / 256.0f)) * 4.0f));
}
}
@ -214,11 +214,11 @@ static DynamicTexture *create_fire_texture(int a2) {
// Construct
FireTexture *texture = new FireTexture;
ALLOC_CHECK(texture);
texture->super.constructor(Tile::fire->texture + (16 * a2));
DynamicTexture_constructor(&texture->super, Tile_fire->texture + (16 * a2));
// Set VTable
texture->super.vtable = get_fire_texture_vtable();
// Setup Random
int seed = Common::getTimeMs();
int seed = Common_getTimeMs();
texture->m_random.seed = seed;
texture->m_random.param_1 = 0x271;
texture->m_random.param_2 = false;
@ -238,17 +238,17 @@ static bool animated_fire = false;
static void Textures_addDynamicTexture_injection(Textures *textures, DynamicTexture *dynamic_texture) {
// Call Original Method
if (animated_water) {
textures->addDynamicTexture(dynamic_texture);
Textures_addDynamicTexture(textures, dynamic_texture);
}
// Add Lava
if (animated_lava) {
textures->addDynamicTexture(create_lava_texture());
textures->addDynamicTexture(create_lava_side_texture());
Textures_addDynamicTexture(textures, create_lava_texture());
Textures_addDynamicTexture(textures, create_lava_side_texture());
}
if (animated_fire) {
textures->addDynamicTexture(create_fire_texture(0));
textures->addDynamicTexture(create_fire_texture(1));
Textures_addDynamicTexture(textures, create_fire_texture(0));
Textures_addDynamicTexture(textures, create_fire_texture(1));
}
}

View File

@ -20,7 +20,7 @@ static void Minecraft_tick_injection(Minecraft *minecraft) {
// Tick Dynamic Textures
Textures *textures = minecraft->textures;
if (textures != nullptr) {
textures->tick(true);
Textures_tick(textures, true);
}
}
@ -170,7 +170,7 @@ static void Textures_tick_glTexSubImage2D_injection(GLenum target, GLint level,
}
// Load Textures
static Texture AppPlatform_linux_loadTexture_injection(__attribute__((unused)) AppPlatform_linux_loadTexture_t original, __attribute__((unused)) AppPlatform_linux *app_platform, std::string *path, bool b) {
static Texture AppPlatform_linux_loadTexture_injection(__attribute__((unused)) AppPlatform_linux *app_platform, std::string *path, bool b) {
Texture out;
std::string real_path = *path;
if (b) {
@ -234,5 +234,5 @@ void init_textures() {
overwrite_call((void *) 0x53274, (void *) Textures_tick_glTexSubImage2D_injection);
// Load Textures
overwrite_virtual_calls(AppPlatform_linux_loadTexture, AppPlatform_linux_loadTexture_injection);
overwrite(*AppPlatform_linux_loadTexture_vtable_addr, AppPlatform_linux_loadTexture_injection);
}

View File

@ -17,8 +17,8 @@ static void StartMenuScreen_render_Screen_renderBackground_injection(Screen *scr
Minecraft *minecraft = screen->minecraft;
Textures *textures = minecraft->textures;
std::string texture = "gui/titleBG.png";
textures->loadAndBindTexture(&texture);
screen->blit(0, 0, 0, 0, screen->width, screen->height, 0x100, 0x100);
Textures_loadAndBindTexture(textures, &texture);
Screen_blit(screen, 0, 0, 0, 0, screen->width, screen->height, 0x100, 0x100);
}
// Add Buttons Back To Classic Start Screen
@ -88,21 +88,20 @@ static void StartMenuScreen_render_Screen_render_injection(Screen *screen, int x
current_splash = splashes[rand() % splashes.size()];
}
// Choose Position
float multiplier = touch_gui ? 0.5f : 1.0f;
float multiplier = touch_gui ? 0.5f: 1.0f;
float splash_x = (float(screen->width) / 2.0f) + (94.0f * multiplier);
float splash_y = 4.0f + (36.0f * multiplier);
float max_width = 86;
float max_scale = 2.0f;
// Draw (From https://github.com/ReMinecraftPE/mcpe/blob/d7a8b6baecf8b3b050538abdbc976f690312aa2d/source/client/gui/screens/StartMenuScreen.cpp#L699-L718)
glPushMatrix();
// Position
glTranslatef(splash_x, splash_y, 0.0f);
glRotatef(-20.0f, 0.0f, 0.0f, 1.0f);
// Scale
int textWidth = screen->font->width(&current_splash);
float timeMS = float(Common::getTimeMs() % 1000) / 1000.0f;
float scale = max_scale - Mth::abs(0.1f * Mth::sin(2.0f * float(M_PI) * timeMS));
float real_text_width = textWidth * max_scale;
int textWidth = Font_width(screen->font, &current_splash);
float timeMS = float(Common_getTimeMs() % 1000) / 1000.0f;
float scale = 2.0f - Mth_abs(0.1f * Mth_sin(2.0f * float(M_PI) * timeMS));
float real_text_width = textWidth * scale;
if (real_text_width > max_width) {
scale *= max_width / real_text_width;
}
@ -110,7 +109,7 @@ static void StartMenuScreen_render_Screen_render_injection(Screen *screen, int x
glScalef(scale, scale, scale);
// Render
static int line_height = 8;
screen->drawCenteredString(screen->font, &current_splash, 0, -(float(line_height) / 2), 0xffff00);
Screen_drawCenteredString(screen, screen->font, &current_splash, 0, -(float(line_height) / 2), 0xffff00);
// Finish
glPopMatrix();
}
@ -152,7 +151,7 @@ void init_title_screen() {
}
// Rename "Create" Button To "Quit"
patch_address((void *) &Strings::classic_create_button_text, (void *) "Quit");
patch_address((void *) Strings_classic_create_button_text_pointer, (void *) "Quit");
// Add Functionality To Quit Button
overwrite_virtual_calls(StartMenuScreen_buttonClicked, StartMenuScreen_buttonClicked_injection);

View File

@ -13,14 +13,14 @@ static int32_t Minecraft_isTouchscreen_injection(__attribute__((unused)) Minecra
// IngameBlockSelectionScreen Memory Allocation Override
static unsigned char *operator_new_IngameBlockSelectionScreen_injection(__attribute__((unused)) uint32_t size) {
return (unsigned char *) ::operator new(sizeof(Touch_IngameBlockSelectionScreen));
return (unsigned char *) ::operator new(TOUCH_INGAME_BLOCK_SELECTION_SCREEN_SIZE);
}
// Improved Button Hover Behavior
static int32_t Button_hovered_injection(__attribute__((unused)) Button *button, __attribute__((unused)) Minecraft *minecraft, __attribute__((unused)) int32_t click_x, __attribute__((unused)) int32_t click_y) {
// Get Mouse Position
int32_t x = Mouse::getX() * Gui::InvGuiScale;
int32_t y = Mouse::getY() * Gui::InvGuiScale;
int32_t x = Mouse_getX() * Gui_InvGuiScale;
int32_t y = Mouse_getY() * Gui_InvGuiScale;
// Get Button Position
int32_t button_x1 = button->x;
@ -38,24 +38,25 @@ static void LargeImageButton_render_GuiComponent_drawCenteredString_injection(Gu
}
// Call Original Method
component->drawCenteredString(font, text, x, y, color);
GuiComponent_drawCenteredString(component, font, text, x, y, color);
}
// Create Button
int touch_gui = 0;
template <typename T>
static Button *create_button(int id, std::string text) {
T *button = new T;
ALLOC_CHECK(button);
button->constructor(id, &text);
return (Button *) button;
}
Button *touch_create_button(int id, std::string text) {
Button *button = nullptr;
if (touch_gui) {
return create_button<Touch_TButton>(id, text);
button = (Button *) new Touch_TButton;
} else {
return create_button<Button>(id, text);
button = new Button;
}
ALLOC_CHECK(button);
if (touch_gui) {
Touch_TButton_constructor((Touch_TButton *) button, id, &text);
} else {
Button_constructor(button, id, &text);
}
return button;
}
// Init
@ -64,7 +65,7 @@ void init_touch() {
int touch_buttons = touch_gui;
if (touch_gui) {
// Main UI
overwrite(Minecraft_isTouchscreen, Minecraft_isTouchscreen_injection);
overwrite((void *) Minecraft_isTouchscreen, (void *) Minecraft_isTouchscreen_injection);
// Force Correct Toolbar Size
unsigned char toolbar_patch[4] = {0x01, 0x00, 0x50, 0xe3}; // "cmp r0, #0x1"
@ -97,7 +98,7 @@ void init_touch() {
// Improved Button Hover Behavior
if (touch_buttons && feature_has("Improved Button Hover Behavior", server_disabled)) {
overwrite(Button_hovered, Button_hovered_injection);
overwrite((void *) Button_hovered, (void *) Button_hovered_injection);
overwrite_call((void *) 0x1ebd4, (void *) LargeImageButton_render_GuiComponent_drawCenteredString_injection);
}

View File

@ -9,7 +9,7 @@ char *version_get() {
static char *version = nullptr;
// Load
if (version == nullptr) {
safe_asprintf(&version, "%s / Reborn v%s", Strings::minecraft_pi_version, reborn_get_version());
safe_asprintf(&version, "%s / Reborn v%s", Strings_minecraft_pi_version, reborn_get_version());
}
// Return
return version;
@ -20,7 +20,7 @@ __attribute__((destructor)) static void _free_version() {
}
// Injection For Touch GUI Version
static std::string Common_getGameVersionString_injection(__attribute__((unused)) std::string *version_suffix) {
static std::string Common_getGameVersionString_injection(__attribute__((unused)) std::string const& version_suffix) {
// Set Version
return version_get();
}
@ -28,9 +28,9 @@ static std::string Common_getGameVersionString_injection(__attribute__((unused))
// Init
void init_version() {
// Touch GUI
overwrite(Common_getGameVersionString, Common_getGameVersionString_injection);
overwrite((void *) Common_getGameVersionString, (void *) Common_getGameVersionString_injection);
// Normal GUI
patch_address((void *) &Strings::minecraft_pi_version, version_get());
patch_address((void *) Strings_minecraft_pi_version_pointer, version_get());
// Log
INFO("Starting Minecraft: Pi Edition (%s)", version_get());

Some files were not shown because too many files have changed in this diff Show More