Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
cfce26b3ba | ||
|
7566890c7c | ||
|
d7c6771cdd | ||
|
e03caceb6d | ||
|
2401fa3a6f | ||
|
0feef96eca | ||
|
d9c25f22ad |
|
@ -36,7 +36,7 @@ jobs:
|
|||
- name: Build
|
||||
run: ./scripts/build.mjs appimage ${{ matrix.mode }} ${{ matrix.arch }}
|
||||
- name: Upload Artifacts
|
||||
uses: christopherhx/gitea-upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ matrix.mode }} (${{ matrix.arch }})
|
||||
path: ./out/*.AppImage*
|
||||
|
@ -89,7 +89,7 @@ jobs:
|
|||
- name: Build Example Mods
|
||||
run: ./example-mods/build.sh
|
||||
- name: Upload Artifacts
|
||||
uses: christopherhx/gitea-upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: Example Mods
|
||||
path: ./example-mods/out/*
|
||||
|
@ -104,12 +104,12 @@ jobs:
|
|||
steps:
|
||||
# Dependencies
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '>=1.20.1'
|
||||
# Download Artifacts
|
||||
- name: Download Artifacts
|
||||
uses: christopherhx/gitea-download-artifact@v4
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
path: out
|
||||
# Create Release
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -2,13 +2,7 @@
|
|||
<img alt="Start Screen" src="images/start.png">
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://gitea.thebrokenrail.com/minecraft-pi-reborn/minecraft-pi-reborn/releases/latest"><img height="80" alt="Download as an AppImage" src="https://docs.appimage.org/_images/download-appimage-banner.svg" /></a>
|
||||
<a href="https://flathub.org/apps/details/com.thebrokenrail.MCPIReborn"><img height="80" alt="Download on Flathub" src="https://flathub.org/assets/badges/flathub-badge-en.svg" /></a>
|
||||
<a href="https://github.com/Botspot/pi-apps"><img height="80" alt="Get it from Pi-Apps" src="https://github.com/Botspot/pi-apps/blob/master/icons/badge.png?raw=true" /></a>
|
||||
</p>
|
||||
|
||||
# Minecraft: Pi Edition: Reborn [![Gitea Actions](https://gitea.thebrokenrail.com/minecraft-pi-reborn/minecraft-pi-reborn/actions/workflows/build.yml/badge.svg)](https://gitea.thebrokenrail.com/minecraft-pi-reborn/minecraft-pi-reborn/actions)
|
||||
# Minecraft: Pi Edition: Reborn
|
||||
Minecraft: Pi Edition Modding Project
|
||||
|
||||
## Documentation
|
||||
|
|
2
archives
2
archives
|
@ -1 +1 @@
|
|||
Subproject commit 0d7025eb709351d2db51d19a46ff8e2640bd4465
|
||||
Subproject commit 37d4baec5874b39e10cafda2f9fcf6b63129c85a
|
|
@ -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()
|
||||
|
|
|
@ -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,8 +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 URL
|
||||
mcpi_option(DISCORD_INVITE "Discord Invite URL" STRING "https://discord.gg/mcpi-revival-740287937727561779")
|
||||
|
||||
# Documentation URL
|
||||
mcpi_option(DOCUMENTATION "Documentation URL" STRING "https://gitea.thebrokenrail.com/minecraft-pi-reborn/minecraft-pi-reborn/src/branch/master/docs/")
|
||||
# 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()
|
||||
|
|
|
@ -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")
|
||||
|
|
2
dependencies/gles-compatibility-layer/src
vendored
2
dependencies/gles-compatibility-layer/src
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 3ee682f6f26dd4344f0701ae346d10250c040d8e
|
||||
Subproject commit 67a8d026aa5aef062dae654d418c3cd09417c0c1
|
18
dependencies/qemu/CMakeLists.txt
vendored
18
dependencies/qemu/CMakeLists.txt
vendored
|
@ -3,12 +3,12 @@ project(qemu)
|
|||
## QEMU
|
||||
|
||||
# Version
|
||||
set(QEMU_VERSION "9.0.0")
|
||||
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
|
||||
|
|
56
dependencies/qemu/trampoline.patch
vendored
56
dependencies/qemu/trampoline.patch
vendored
|
@ -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,
|
2
dependencies/symbol-processor/src
vendored
2
dependencies/symbol-processor/src
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 8249a305df07a0b087b44341afa8c3b27ad7a156
|
||||
Subproject commit db3879f7a51c5413e1c17e17cd6949d711132468
|
106
docs/ARCHITECTURE.md
Normal file
106
docs/ARCHITECTURE.md
Normal file
|
@ -0,0 +1,106 @@
|
|||
# Architecture
|
||||
|
||||
## Launch Sequence
|
||||
|
||||
### Common
|
||||
1. The launcher forks itself.
|
||||
1. The child process continues the launch sequence.
|
||||
2. The original process monitors the child process for crashes.
|
||||
|
||||
### Client
|
||||
1. The launcher is started by the user.
|
||||
1. The launcher starts several Zenity dialogs to configure MCPI-Reborn.
|
||||
1. If the corresponding environmental variable for a setting is specified, it will be used instead of the dialog.
|
||||
2. If a setting is cached, then the dialog's default value will be the cached value instead of the normal default.
|
||||
3. When configuration has been completed, the settings specified will be cached.
|
||||
2. The launcher replaces itself with MCPI.
|
||||
1. MCPI-Reborn components are loaded using `LD_PRELOAD` and `LD_LIBRARY_PATH`.
|
||||
2. If the Media Layer Proxy is enabled, then the Media Layer Proxy Client is started as a sub-process.
|
||||
|
||||
### Server
|
||||
1. The launcher is started by the user.
|
||||
2. The launcher replaces itself with MCPI.
|
||||
|
||||
## Components
|
||||
|
||||
### Launcher
|
||||
This component configures the various environmental variables required for MCPI-Reborn to work. When running in client-mode, this component will also launch several Zenity dialogs for interactive configuration.
|
||||
|
||||
The environmental variables configured by this component includes:
|
||||
* `LD_PRELOAD`
|
||||
* `LD_LIBRARY_PATH`
|
||||
* `MCPI_FEATURE_FLAGS`
|
||||
* `MCPI_RENDER_DISTANCE`
|
||||
* `MCPI_USERNAME`
|
||||
|
||||
This is always compiled for the host system's architecture.
|
||||
|
||||
### Media Layer
|
||||
The Media Layer handles MCPI's graphics calls and user input. It replaces MCPI's native usage of SDL 1.2 with GLFW.
|
||||
|
||||
#### Core
|
||||
This sub-component re-implements a subset of SDL 1.2 calls with GLFW. It also provides a few utility functions that are used internally by MCPI-Reborn.
|
||||
|
||||
The utility functions include:
|
||||
* Fullscreen
|
||||
* Audio
|
||||
* Etc
|
||||
|
||||
This is always compiled for the host system's architecture unless the Media Layer Proxy is disabled.
|
||||
|
||||
This was created because SDL 1.2 has numerous bugs and is in-general unsupported.
|
||||
|
||||
#### Proxy
|
||||
This sub-component must be used if the host system's architecture isn't ARM. It uses UNIX pipes to cross architectural boundaries and allow MCPI to use the Media Layer Core (which is always compiled for the host system's architecture).
|
||||
|
||||
It is made of two parts:
|
||||
* Media Layer Proxy Server
|
||||
* Links To MCPI
|
||||
* Creates The UNIX Pipes
|
||||
* Same External API As The Media Layer Core
|
||||
* Compiled For ARM
|
||||
* Media Layer Proxy Client
|
||||
* Links To The Media Layer Core
|
||||
* Connects To The Media Layer Proxy Server
|
||||
* Uses The System's Native GLES Driver (ie. Mesa)
|
||||
* Compiled For The Host System's Architecture
|
||||
|
||||
While proxying all Media Layer Core API calls across UNIX pipes does hurt performance, it is better than emulating the entire graphics stack.
|
||||
|
||||
Using this in server-mode is redundant (but is possible).
|
||||
|
||||
#### Extras
|
||||
This sub-component contains code that must always be linked directly to MCPI.
|
||||
|
||||
This is always compiled for ARM.
|
||||
|
||||
#### Headers
|
||||
This sub-component includes headers for SDL, GLES, and EGL allowing easy (cross-)compilation.
|
||||
|
||||
### Mods
|
||||
This component patches MCPI to modify its behavior. It's loaded using `LD_PRELOAD`.
|
||||
|
||||
This is always compiled for ARM.
|
||||
|
||||
### `libreborn`
|
||||
This component contains various utility functions including:
|
||||
|
||||
* Code Patching (ARM Only)
|
||||
* Logging
|
||||
* Etc
|
||||
|
||||
The code patching is ARM only because it relies on hard-coded ARM instructions. However, this is irrelevant since code patching is only needed in ARM code (to patch MCPI).
|
||||
|
||||
### `symbols`
|
||||
This component contains all MCPI symbols.
|
||||
|
||||
## Dependencies
|
||||
MCPI-Reborn has several dependencies:
|
||||
* MCPI (Bundled)
|
||||
* GLFW (Only In Client Mode; Bundled)
|
||||
* OpenGL ES 2.0
|
||||
* OpenAL (Only In Client Mode)
|
||||
* ZLib (Required By LibPNG; Bundled)
|
||||
* LibPNG (Bundled)
|
||||
* QEMU User Mode (Only On Non-ARM Hosts; Runtime Only)
|
||||
* Zenity (Only In Client Mode; Runtime Only; Bundled)
|
13
docs/BUILDING.md
Normal file
13
docs/BUILDING.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Building
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Debian/Ubuntu
|
||||
```sh
|
||||
./scripts/install-dependencies.sh
|
||||
```
|
||||
|
||||
## Instructions
|
||||
```sh
|
||||
./scripts/build.mjs <none|appimage|flatpak> <client|server> <armhf|arm64|amd64|host> [--clean] [--install] [-Dvar=value...]
|
||||
```
|
|
@ -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
|
||||
|
|
64
docs/COMMAND_LINE.md
Normal file
64
docs/COMMAND_LINE.md
Normal file
|
@ -0,0 +1,64 @@
|
|||
# Command Line Usage
|
||||
|
||||
## Command Line Arguments
|
||||
|
||||
### `--version` (Or `-v`)
|
||||
If you run MCPI-Reborn with `--version` it will print its version to `stdout`.
|
||||
|
||||
### `--debug`
|
||||
This sets `MCPI_DEBUG`.
|
||||
|
||||
### `--copy-sdk`
|
||||
This extracts the modding SDK and immediately exits. (This allows the SDK to be extracted without starting the game.)
|
||||
|
||||
### Client Mode Only
|
||||
|
||||
#### `--print-available-feature-flags`
|
||||
This print the available feature flags (and their default values) to `stdout` and then immediately exit.
|
||||
|
||||
The feature flags are printed in the following format:
|
||||
```
|
||||
TRUE This Flag Is On By Default
|
||||
FALSE This Flag Is Off By Default
|
||||
```
|
||||
|
||||
#### `--default`
|
||||
This will skip the startup configuration dialogs and just use the default values. This will use the cached configuration unless `--no-cache` is used.
|
||||
|
||||
#### `--benchmark`
|
||||
This will make MCPI-Reborn enter a simple benchmark mode. This means automatically loading a newly generated world, then rotating the camera for a period of time. When it has finished, it will then exit and print the average FPS while the world was loaded. In this mode, all user input is blocked. However you can still modify rendering settings by changing feature flags.
|
||||
|
||||
The world used will always be re-created on start and uses a hard-coded seed.
|
||||
|
||||
#### `--no-cache`
|
||||
This will skip loading and saving the cached launcher configuration.
|
||||
|
||||
#### `--wipe-cache`
|
||||
This will wipe the cached launcher configuration.
|
||||
|
||||
### Server Mode Only
|
||||
|
||||
#### `--only-generate`
|
||||
This will make MCPI-Reborn immediately exit once world generation has completed. This is mainly used for automatically testing MCPI-Reborn.
|
||||
|
||||
## Environmental Variables
|
||||
|
||||
### `MCPI_DEBUG`
|
||||
This enables debug logging if it is set.
|
||||
|
||||
### `MCPI_API_PORT`
|
||||
This configures the API to use a different port (the default is 4711).
|
||||
|
||||
### Client Mode Only
|
||||
If any of the following variables aren't set, one configuration dialog will open on startup for each unset variable.
|
||||
|
||||
#### `MCPI_FEATURE_FLAGS`
|
||||
This corresponds to `--print-available-feature-flags`. This is just a list of all enabled feature flags separated by `|`.
|
||||
|
||||
For instance, the string `Feature A|Feature B` would enable both `Feature A` and `Feature B` and *disable every other available feature flag*.
|
||||
|
||||
#### `MCPI_RENDER_DISTANCE`
|
||||
This is the render distance. The possible values are: `Far`, `Normal`, `Short`, and `Tiny`.
|
||||
|
||||
#### `MCPI_USERNAME`
|
||||
This is the username.
|
|
@ -1,70 +0,0 @@
|
|||
---
|
||||
gitea: none
|
||||
include_toc: true
|
||||
---
|
||||
|
||||
# Getting Started
|
||||
Welcome to the official guide for setting up Minecraft: Pi Edition: Reborn (also known as MCPI-Reborn)! This document will help you through the installation and setup process.
|
||||
|
||||
## System Requirements
|
||||
MCPI-Reborn requires support for OpenGL ES v2.0 (unlike the original game, which used OpenGL ES v1.1). It also only supports Linux-based systems.
|
||||
|
||||
In addition, while the original game could only be run on the Raspberry Pi, MCPI-Reborn is much more flexible. It supports running on 32-bit ARM (known as `armhf`), 64-bit ARM (known as `arm64`), and 64-bit x86 (known as `amd64`).
|
||||
|
||||
## Installation
|
||||
There are three supported ways to install MCPI-Reborn.
|
||||
|
||||
### AppImage
|
||||
The first supported way to install MCPI-Reborn is with an [AppImage](https://appimage.org). An AppImage is a portable application format that allows users to run software without installation.
|
||||
|
||||
To run MCPI-Reborn, all you need to do is [download the latest AppImage](https://gitea.thebrokenrail.com/minecraft-pi-reborn/minecraft-pi-reborn/releases/latest) for your architecture and run it.
|
||||
|
||||
More details can be found [here](https://docs.appimage.org/introduction/quickstart.html#how-to-run-an-appimage).
|
||||
|
||||
<details>
|
||||
<summary><b>Additional System Requirements</b></summary>
|
||||
The AppImage requires Debian Bullseye or higher. This is equivalent to Ubuntu 20.04.
|
||||
|
||||
It also requires some additional packages. To install them, run:
|
||||
```sh
|
||||
sudo apt install -y libfuse2 libgtk-3-0 libopenal1
|
||||
```
|
||||
</details>
|
||||
|
||||
### Flatpak
|
||||
The next method is the official [Flatpak](https://www.flatpak.org/). This method has the additional benefit of built-in sandboxing. Unfortunately, it does not support 32-bit ARM systems.
|
||||
|
||||
It can be installed through [Flathub](https://flathub.org/apps/details/com.thebrokenrail.MCPIReborn).
|
||||
|
||||
### Pi-Apps
|
||||
The final supported method is [Pi-Apps](https://github.com/Botspot/pi-apps). It is a ["well-maintained collection of app installation-scripts"](https://github.com/Botspot/pi-apps#:~:text=well-maintained%20collection%20of%20app%20installation-scripts) that includes support for MCPI-Reborn.
|
||||
|
||||
The list of systems supported by Pi-Apps can be found [here](https://github.com/Botspot/pi-apps?tab=readme-ov-file#supported-systems).
|
||||
|
||||
## Managing Game Data
|
||||
Just as regular Minecraft stores game data at `~/.minecraft`, MCPI-Reborn uses `~/.minecraft-pi`[^1]. This is the profile directory and is where your worlds, screenshots, and game settings are stored.
|
||||
|
||||
The profile directory can easily be accessed by opening MCPI-Reborn and going to `Options -> Reborn -> Profile Directory`.
|
||||
|
||||
## Sound
|
||||
One of MCPI-Reborn's most important modifications is the addition of a sound engine. However, due to copyright limitations, Minecraft's sounds cannot be distributed with MCPI-Reborn and must be installed manually.
|
||||
|
||||
Fortunately, installing the sound data is simple:
|
||||
1. Obtain a valid Minecraft: Pocket Edition v0.6.1[^2] APK file.
|
||||
2. Extract `lib/*/libminecraftpe.so` from the APK.
|
||||
3. Create the directory `<Profile Directory>/overrides` if it does not already exist.
|
||||
4. Copy `libminecraftpe.so` into `<Profile Directory>/overrides`.
|
||||
5. Sound should now be fully functional!
|
||||
|
||||
## Custom Textures
|
||||
MCPI-Reborn allows users to easily use custom textures through the use of an "overrides directory." Any files placed in this directory will automatically replace their equivalent file in MCPI-Reborn.
|
||||
|
||||
For instance, to override `data/images/terrain.png`, you would copy the replacement file to `<Overrides Folder>/images/terrain.png`.
|
||||
|
||||
The overrides directory is located at `<Profile Directory>/overrides`.
|
||||
|
||||
## Discord
|
||||
If you have any questions or just want to talk about Minecraft: Pi Edition, there is an [official Discord server](https://discord.com/invite/aDqejQGMMy)!
|
||||
|
||||
[^1]: When using the Flatpak, the profile directory is located at `~/.var/app/com.thebrokenrail.MCPIReborn/.minecraft-pi`.
|
||||
[^2]: This is not a strict requirement; a Minecraft: Pocket Edition v0.8.1 APK would likely work, but it is not guaranteed.
|
30
docs/INSTALL.md
Normal file
30
docs/INSTALL.md
Normal file
|
@ -0,0 +1,30 @@
|
|||
# Installation
|
||||
|
||||
## AppImage
|
||||
Download packages [here](https://gitea.thebrokenrail.com/minecraft-pi-reborn/minecraft-pi-reborn/releases).
|
||||
|
||||
### System Requirements
|
||||
* Debian Bullseye/Ubuntu 20.04 Or Higher
|
||||
* FUSE 2
|
||||
* Debian/Ubuntu: `sudo apt install libfuse2`
|
||||
* Arch: `sudo pacman -S fuse2`
|
||||
* Client-Only Dependencies
|
||||
* Graphics Drivers
|
||||
* GTK+ 3
|
||||
* Debian/Ubuntu: `sudo apt install libgtk-3-0`
|
||||
* Arch: `sudo pacman -S gtk3`
|
||||
* OpenAL
|
||||
* Debian/Ubuntu: `sudo apt install libopenal1`
|
||||
* Arch: `sudo pacman -S openal`
|
||||
|
||||
### Running
|
||||
Follow [these](https://docs.appimage.org/introduction/quickstart.html#how-to-run-an-appimage) instructions.
|
||||
|
||||
## Flatpak
|
||||
<a href="https://flathub.org/apps/details/com.thebrokenrail.MCPIReborn"><img width="240" alt="Download On Flathub" src="https://flathub.org/assets/badges/flathub-badge-en.svg" /></a>
|
||||
|
||||
### Note
|
||||
Game data is stored in `~/.var/app/com.thebrokenrail.MCPIReborn/.minecraft-pi` instead of `~/.minecraft-pi`.
|
||||
|
||||
## Arch User Repository (Arch Linux Only)
|
||||
The [`minecraft-pi-reborn-git`](https://aur.archlinux.org/packages/minecraft-pi-reborn-git) is available in the AUR.
|
8
docs/OVERRIDING_ASSETS.md
Normal file
8
docs/OVERRIDING_ASSETS.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Overriding Assets
|
||||
To make overriding assets easier, MCPI-Reborn provides an overrides folder. Any file located in Minecraft: Pi Edition's `data` folder can be overridden by placing a file with the same name and path in the overrides folder. The overrides folder is located at `~/.minecraft-pi/overrides`[^1].
|
||||
|
||||
## Examples
|
||||
- `data/images/terrain.png` -> `~/.minecraft-pi/overrides/images/terrain.png`
|
||||
- `data/lang/en_US.lang` -> `~/.minecraft-pi/overrides/lang/en_US.lang`
|
||||
|
||||
[^1]: On Flatpak, the path is `~/.var/app/com.thebrokenrail.MCPIReborn/.minecraft-pi/overrides`.
|
|
@ -1,9 +1,14 @@
|
|||
# Documentation
|
||||
* [View Getting Started](GETTING_STARTED.md)
|
||||
* [View Installation](INSTALL.md)
|
||||
* [View Overriding Assets](OVERRIDING_ASSETS.md)
|
||||
* [View Dedicated Server](DEDICATED_SERVER.md)
|
||||
* [View Credits](CREDITS.md)
|
||||
* [View Terminology](TERMINOLOGY.md)
|
||||
* [View Building](BUILDING.md)
|
||||
* [View Architecture](ARCHITECTURE.md)
|
||||
* [View Command Line Arguments](COMMAND_LINE.md)
|
||||
* [View Multiplayer](MULTIPLAYER.md)
|
||||
* [View Sound](SOUND.md)
|
||||
* [View In-Game Controls](CONTROLS.md)
|
||||
* [View Custom Skins](CUSTOM_SKINS.md)
|
||||
* [View Changelog](CHANGELOG.md)
|
||||
|
|
8
docs/SOUND.md
Normal file
8
docs/SOUND.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Sound
|
||||
One of MCPI-Reborn's main modifications is a sound-engine since MCPI does not include one by default[^1]. However, it can't be used out-of-box because MCPI does not contain any sound data and MCPI-Reborn can't include it because of copyright.
|
||||
|
||||
MCPE's sound data can be extracted from any MCPE v0.6.1[^2] APK file, just place its `libminecraftpe.so` into `~/.minecraft-pi/overrides`[^3] and you should have sound!
|
||||
|
||||
[^1]: The mute button is just leftover code from MCPE, it does not actually do anything in un-modded MCPI, however it is connected to MCPI-Reborn's sound-engine.
|
||||
[^2]: This is not a hard limit, an MCPE v0.8.1 APK would probably work, but don't rely on it.
|
||||
[^3]: On Flatpak, the path is `~/.var/app/com.thebrokenrail.MCPIReborn/.minecraft-pi/overrides`.
|
|
@ -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();
|
||||
|
|
|
@ -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
361
launcher/src/bootstrap.c
Normal 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);
|
||||
}
|
|
@ -1,205 +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
|
||||
|
||||
// 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);
|
||||
}
|
||||
#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";
|
||||
std::string linker;
|
||||
{
|
||||
// Log
|
||||
DEBUG("Patching ELF Dependencies...");
|
||||
|
||||
// Find Linker
|
||||
linker = "/lib/ld-linux-armhf.so.3";
|
||||
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
|
||||
// Use ARM Sysroot Linker
|
||||
linker = binary_directory + "/sysroot" + linker;
|
||||
#endif
|
||||
|
||||
// Patch
|
||||
patch_mcpi_elf_dependencies(resolved_path, new_mcpi_exe_path);
|
||||
|
||||
// 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
|
||||
std::string mcpi_ld_path = "";
|
||||
{
|
||||
// Log
|
||||
DEBUG("Setting Linker Search Paths...");
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Configure Preloaded Objects
|
||||
std::string mcpi_ld_preload;
|
||||
{
|
||||
// Log
|
||||
DEBUG("Locating Mods...");
|
||||
|
||||
// ARM Components
|
||||
mcpi_ld_preload = 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.push_back(QEMU_BINARY);
|
||||
// Fix Bug
|
||||
args.push_back("-B");
|
||||
args.push_back("0x40000"); // Arbitary Value (Aligns To 4k And 16k Page Sizes)
|
||||
#endif
|
||||
|
||||
// Setup Linker
|
||||
args.push_back(linker);
|
||||
args.push_back("--library-path");
|
||||
args.push_back(mcpi_ld_path);
|
||||
args.push_back("--preload");
|
||||
args.push_back(mcpi_ld_preload);
|
||||
|
||||
// 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);
|
||||
}
|
|
@ -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);
|
||||
std::string 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
|
||||
|
|
|
@ -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,7 +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 Add Welcome Screen
|
||||
TRUE Disable Buggy Held Item Caching
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
12
launcher/src/client/launcher.h
Normal file
12
launcher/src/client/launcher.h
Normal 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);
|
|
@ -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,19 +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();
|
||||
unsetenv(MCPI_LOG_ENV);
|
||||
// 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
|
||||
|
|
|
@ -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
110
launcher/src/mods.c
Normal 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);
|
||||
}
|
|
@ -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/"
|
||||
std::string 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;
|
||||
}
|
||||
}
|
||||
|
||||
// Return
|
||||
return preload;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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[]);
|
|
@ -15,7 +15,7 @@ static void duplicate_mcpi_executable(char *new_path) {
|
|||
// Ensure Temporary Directory
|
||||
{
|
||||
// Check If It Exists
|
||||
struct stat tmp_stat = {};
|
||||
struct stat tmp_stat;
|
||||
int exists = stat(MCPI_PATCHED_DIR, &tmp_stat) != 0 ? 0 : S_ISDIR(tmp_stat.st_mode);
|
||||
if (!exists) {
|
||||
// Doesn't Exist
|
||||
|
@ -44,13 +44,16 @@ static const char *libraries_to_remove[] = {
|
|||
static const char *libraries_to_add[] = {
|
||||
"libmedia-layer-core.so"
|
||||
};
|
||||
void patch_mcpi_elf_dependencies(const char *original_path, char *new_path) {
|
||||
void patch_mcpi_elf_dependencies(const char *original_path, char *new_path, const char *linker) {
|
||||
// Duplicate MCPI executable into /tmp so it can be modified.
|
||||
duplicate_mcpi_executable(new_path);
|
||||
|
||||
// Patch File
|
||||
{
|
||||
std::unique_ptr<LIEF::ELF::Binary> binary = LIEF::ELF::Parser::parse(original_path);
|
||||
if (linker != NULL) {
|
||||
binary->interpreter(linker);
|
||||
}
|
||||
for (size_t i = 0; i < (sizeof (libraries_to_remove) / sizeof (const char *)); i++) {
|
||||
binary->remove_library(libraries_to_remove[i]);
|
||||
}
|
||||
|
@ -67,3 +70,25 @@ void patch_mcpi_elf_dependencies(const char *original_path, char *new_path) {
|
|||
ERR("Unable To Set File Permissions: %s: %s", new_path, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
// Get Interpreter
|
||||
static int dl_iterate_callback(struct dl_phdr_info *info, __attribute__((unused)) size_t size, void *data) {
|
||||
// Only Search Current Program
|
||||
if (strcmp(info->dlpi_name, "") == 0) {
|
||||
for (int i = 0; i < info->dlpi_phnum; i++) {
|
||||
if (info->dlpi_phdr[i].p_type == PT_INTERP) {
|
||||
// Callback
|
||||
*(char **) data = (char *) (info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
char *patch_get_interpreter() {
|
||||
char *interpreter = NULL;
|
||||
dl_iterate_phdr(dl_iterate_callback, &interpreter);
|
||||
if (interpreter != NULL) {
|
||||
interpreter = strdup(interpreter);
|
||||
}
|
||||
return interpreter;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@ extern "C" {
|
|||
|
||||
#define MCPI_PATCHED_DIR "/tmp/.minecraft-pi-patched"
|
||||
|
||||
void patch_mcpi_elf_dependencies(const char *original_path, char *new_path);
|
||||
void patch_mcpi_elf_dependencies(const char *original_path, char *new_path, const char *linker);
|
||||
char *patch_get_interpreter();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
67
launcher/src/sdk.c
Normal file
67
launcher/src/sdk.c
Normal 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);
|
||||
}
|
|
@ -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);
|
||||
}
|
19
launcher/src/server/launcher.c
Normal file
19
launcher/src/server/launcher.c
Normal 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);
|
||||
}
|
|
@ -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,5 +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@"
|
||||
#cmakedefine MCPI_DOCUMENTATION "@MCPI_DOCUMENTATION@"
|
||||
|
|
|
@ -20,6 +20,10 @@ extern "C" {
|
|||
void set_and_print_env(const char *name, const char *value);
|
||||
|
||||
// Safe execvpe()
|
||||
#define for_each_special_environmental_variable(handle) \
|
||||
handle("LD_LIBRARY_PATH"); \
|
||||
handle("LD_PRELOAD");
|
||||
void setup_exec_environment(int is_arm_component);
|
||||
__attribute__((noreturn)) void safe_execvpe(const char *const argv[], const char *const envp[]);
|
||||
|
||||
// Debug Tag
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -1,103 +1,66 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Patching Functions
|
||||
|
||||
#if defined(REBORN_HAS_PATCH_CODE) && defined(__cplusplus)
|
||||
#ifdef REBORN_HAS_PATCH_CODE
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
// Init
|
||||
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(...) \
|
||||
_overwrite_call(__FILE__, __LINE__, __VA_ARGS__)
|
||||
#define overwrite_call(start, target) _overwrite_call(__FILE__, __LINE__, start, target)
|
||||
|
||||
// Replace All Calls To Method start With target
|
||||
void *_overwrite_calls_manual(const char *file, int line, void *start, void *target);
|
||||
#define overwrite_calls_manual(...) \
|
||||
_overwrite_calls_manual(__FILE__, __LINE__, __VA_ARGS__)
|
||||
template <typename overwrite_t>
|
||||
void _overwrite_calls(const char *file, int line, std::string (*set_overwrite)(const overwrite_t &, const std::function<void *(void *, void *)> &), const overwrite_t &target) {
|
||||
std::string ret = set_overwrite(target, [&file, &line](void *start, void *target2) {
|
||||
return _overwrite_calls_manual(file, line, start, target2);
|
||||
});
|
||||
if (!ret.empty()) {
|
||||
ERR("%s", ret.c_str());
|
||||
#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)
|
||||
|
||||
#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; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
}
|
||||
#define overwrite_calls(start, ...) \
|
||||
_overwrite_calls< \
|
||||
__overwrite_##start##_t \
|
||||
>( \
|
||||
__FILE__, __LINE__, \
|
||||
__set_overwrite_for_##start, \
|
||||
__VA_ARGS__ \
|
||||
)
|
||||
|
||||
// Replace All Calls To start With target Within [to, from)
|
||||
void _overwrite_calls_within_manual(const char *file, int line, void *from, void *to, void *start, void *target);
|
||||
#define overwrite_calls_within_manual(...) \
|
||||
_overwrite_calls_within(__FILE__, __LINE__, __VA_ARGS__)
|
||||
template <typename start_t>
|
||||
void _overwrite_calls_within(const char *file, int line, void *from, void *to, start_t start, start_t target) {
|
||||
_overwrite_calls_within_manual(file, line, from, to, (void *) start, (void *) target);
|
||||
}
|
||||
#define overwrite_calls_within(from, to, start, ...) \
|
||||
_overwrite_calls_within< \
|
||||
__raw_##start##_t \
|
||||
>( \
|
||||
__FILE__, __LINE__, \
|
||||
from, to, \
|
||||
start, \
|
||||
__VA_ARGS__ \
|
||||
)
|
||||
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); \
|
||||
overwrite_calls_manual((void *) start, (void *) _helper_for_##target); \
|
||||
_update_references(start, _helper_for_##target); \
|
||||
}
|
||||
|
||||
#define overwrite_virtual_calls(start, target) \
|
||||
{ \
|
||||
_setup_fancy_overwrite(*start##_vtable_addr, start, target); \
|
||||
overwrite_calls_manual((void *) *start##_vtable_addr, (void *) _helper_for_##target); \
|
||||
}
|
||||
|
||||
void _overwrite_calls_within(const char *file, int line, void *from, void *to, void *start, void *target);
|
||||
#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_manual(const char *file, int line, void *start, void *target);
|
||||
#define overwrite_manual(...) \
|
||||
_overwrite(__FILE__, __LINE__, __VA_ARGS__)
|
||||
template <typename start_t>
|
||||
void _overwrite(const char *file, int line, start_t start, start_t target) {
|
||||
_overwrite_manual(file, line, (void *) start, (void *) target);
|
||||
}
|
||||
#define overwrite(start, ...) \
|
||||
_overwrite< \
|
||||
__raw_##start##_t \
|
||||
>( \
|
||||
__FILE__, __LINE__, \
|
||||
start, \
|
||||
__VA_ARGS__ \
|
||||
)
|
||||
void _overwrite(const char *file, int line, void *start, void *target);
|
||||
#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(...) \
|
||||
_patch(__FILE__, __LINE__, __VA_ARGS__)
|
||||
#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(...) \
|
||||
_patch_address(__FILE__, __LINE__, __VA_ARGS__)
|
||||
|
||||
// Patch VTable Entry
|
||||
// This does not affect sub-classes.
|
||||
template <typename start_t>
|
||||
void _patch_vtable(const char *file, int line, start_t *start, start_t target) {
|
||||
_patch_address(file, line, (void *) start, (void *) target);
|
||||
}
|
||||
#define patch_vtable(start, ...) \
|
||||
_patch_vtable< \
|
||||
__raw_##start##_t \
|
||||
>( \
|
||||
__FILE__, __LINE__, \
|
||||
start##_vtable_addr, \
|
||||
__VA_ARGS__ \
|
||||
)
|
||||
#define patch_address(start, target) _patch_address(__FILE__, __LINE__, (void *) start, (void *) target)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -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_manual(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,11 +93,8 @@ void *_overwrite_calls_manual(const char *file, int line, void *start, void *tar
|
|||
if (found < 1) {
|
||||
ERR(NO_CALLSITE_ERROR, file, line, start);
|
||||
}
|
||||
|
||||
// Return
|
||||
return code_block;
|
||||
}
|
||||
void _overwrite_calls_within_manual(const char *file, int line, void *from /* inclusive */, void *to /* exclusive */, void *target, void *replacement) {
|
||||
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
|
||||
void *code_block = update_code_block(replacement);
|
||||
|
||||
|
@ -113,7 +110,7 @@ void _overwrite_calls_within_manual(const char *file, int line, void *from /* in
|
|||
}
|
||||
|
||||
// Overwrite Function
|
||||
void _overwrite_manual(const char *file, int line, void *start, void *target) {
|
||||
void _overwrite(const char *file, int line, void *start, void *target) {
|
||||
// Replace the function's start with a call
|
||||
// to the replacement function.
|
||||
_overwrite_call_internal(file, line, start, target, 1);
|
||||
|
|
|
@ -19,6 +19,15 @@ 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_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) {
|
||||
for_each_special_environmental_variable(handle_environmental_variable);
|
||||
}
|
||||
__attribute__((noreturn)) void safe_execvpe(const char *const argv[], const char *const envp[]) {
|
||||
// Log
|
||||
DEBUG("Running Command:");
|
||||
|
@ -55,8 +64,16 @@ 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);
|
||||
|
||||
// Run
|
||||
safe_execvpe(command, (const char *const *) environ);
|
||||
|
@ -72,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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -646,8 +644,8 @@ void SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *ic
|
|||
|
||||
// Setup Compatibility Layer
|
||||
#ifdef MCPI_USE_GLES1_COMPATIBILITY_LAYER
|
||||
extern void init_gles_compatibility_layer(void *);
|
||||
init_gles_compatibility_layer(glfwGetProcAddress);
|
||||
extern void init_gles_compatibility_layer();
|
||||
init_gles_compatibility_layer();
|
||||
#endif
|
||||
|
||||
// Debug
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
27
media-layer/proxy/CMakeLists.txt
Normal file
27
media-layer/proxy/CMakeLists.txt
Normal 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()
|
1276
media-layer/proxy/src/GLESv1_CM.c
Normal file
1276
media-layer/proxy/src/GLESv1_CM.c
Normal file
File diff suppressed because it is too large
Load Diff
107
media-layer/proxy/src/client/client.cpp
Normal file
107
media-layer/proxy/src/client/client.cpp
Normal 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;
|
||||
}
|
13
media-layer/proxy/src/client/client.h
Normal file
13
media-layer/proxy/src/client/client.h
Normal 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 ()
|
245
media-layer/proxy/src/common/common.c
Normal file
245
media-layer/proxy/src/common/common.c
Normal 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;
|
||||
}
|
62
media-layer/proxy/src/common/common.h
Normal file
62
media-layer/proxy/src/common/common.h
Normal 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
|
354
media-layer/proxy/src/media-layer-core.c
Normal file
354
media-layer/proxy/src/media-layer-core.c
Normal 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
|
||||
}
|
170
media-layer/proxy/src/server/server.cpp
Normal file
170
media-layer/proxy/src/server/server.cpp
Normal 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() {
|
||||
}
|
27
media-layer/proxy/src/server/server.h
Normal file
27
media-layer/proxy/src/server/server.h
Normal 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
|
|
@ -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()
|
|
@ -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
|
||||
}
|
|
@ -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; \
|
||||
})
|
|
@ -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];
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
|
@ -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
|
||||
}
|
|
@ -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,12 +82,13 @@ else()
|
|||
src/input/crafting.cpp
|
||||
# sign
|
||||
src/sign/sign.cpp
|
||||
# touch
|
||||
src/touch/touch.cpp
|
||||
# atlas
|
||||
src/atlas/atlas.cpp
|
||||
# title-screen
|
||||
src/title-screen/title-screen.cpp
|
||||
src/title-screen/splashes.txt # Show In IDE
|
||||
src/title-screen/welcome.cpp
|
||||
# skin
|
||||
src/skin/skin.cpp
|
||||
src/skin/loader.cpp
|
||||
|
@ -103,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
|
||||
)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -1,25 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
|
||||
#include <symbols/minecraft.h>
|
||||
|
||||
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 *
|
||||
void misc_run_on_tick(misc_update_function_Minecraft_t function); // obj == Minecraft *
|
||||
typedef void (*misc_update_function_Recipes_t)(Recipes *obj);
|
||||
void misc_run_on_recipes_setup(misc_update_function_Recipes_t function); // obj == Recipes *
|
||||
typedef void (*misc_update_function_FurnaceRecipes_t)(FurnaceRecipes *obj);
|
||||
void misc_run_on_furnace_recipes_setup(misc_update_function_FurnaceRecipes_t function); // obj == FurnaceRecipes *
|
||||
typedef void (*misc_update_function_FillingContainer_t)(FillingContainer *obj);
|
||||
void misc_run_on_creative_inventory_setup(misc_update_function_FillingContainer_t function); // obj == FillingContainer *
|
||||
typedef void (*misc_update_function_void_t)(void *obj);
|
||||
void misc_run_on_tiles_setup(misc_update_function_void_t function); // obj == NULL
|
||||
void misc_run_on_items_setup(misc_update_function_void_t function); // obj == NULL
|
||||
void misc_run_on_language_setup(misc_update_function_void_t function); // obj == NULL
|
||||
typedef bool (*misc_update_function_key_press_t)(Minecraft *minecrtaft, int key);
|
||||
void misc_run_on_game_key_press(misc_update_function_key_press_t function); // In-Game Key Presses Only
|
||||
|
||||
extern bool is_in_chat;
|
||||
}
|
||||
|
||||
void misc_run_on_update(const std::function<void(Minecraft *)> &func);
|
||||
void misc_run_on_tick(const std::function<void(Minecraft *)> &func);
|
||||
void misc_run_on_recipes_setup(const std::function<void(Recipes *)> &func);
|
||||
void misc_run_on_furnace_recipes_setup(const std::function<void(FurnaceRecipes *)> &func);
|
||||
void misc_run_on_tiles_setup(const std::function<void()> &func);
|
||||
void misc_run_on_items_setup(const std::function<void()> &func);
|
||||
void misc_run_on_language_setup(const std::function<void()> &func);
|
||||
void misc_run_on_game_key_press(const std::function<bool(Minecraft *, int)> &func);
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#define CHANGELOG_FILE "CHANGELOG.md"
|
||||
|
||||
extern "C" {
|
||||
void open_url(const std::string &url);
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
@ -207,7 +207,7 @@ static FoodItem *create_bucket(int32_t id, int32_t texture_x, int32_t texture_y,
|
|||
// Return
|
||||
return item;
|
||||
}
|
||||
static void Item_initItems_injection() {
|
||||
static void Item_initItems_injection(__attribute__((unused)) void *null) {
|
||||
bucket = create_bucket(69, 10, 4, "bucket");
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -340,8 +340,8 @@ static void FurnaceTileEntity_tick_ItemInstance_setNull_injection(ItemInstance *
|
|||
}
|
||||
|
||||
// Add the bucket name to the language file
|
||||
static void Language_injection() {
|
||||
I18n::_strings.insert(std::make_pair("item.bucketMilk.name", "Milk Bucket"));
|
||||
static void Language_injection(__attribute__((unused)) void *null) {
|
||||
I18n__strings.insert(std::make_pair("item.bucketMilk.name", "Milk Bucket"));
|
||||
}
|
||||
|
||||
// Init
|
||||
|
@ -355,7 +355,7 @@ void init_bucket() {
|
|||
// Change Max Stack Size Based On Auxiliary
|
||||
overwrite_calls(ItemInstance_getMaxStackSize, ItemInstance_getMaxStackSize_injection);
|
||||
// Enable milking
|
||||
overwrite_calls(Cow_interact, Cow_interact_injection);
|
||||
overwrite_virtual_calls(Cow_interact, Cow_interact_injection);
|
||||
// Creative Inventory
|
||||
misc_run_on_creative_inventory_setup(Inventory_setupDefault_FillingContainer_addItem_call_injection);
|
||||
// Make Liquids Selectable
|
||||
|
|
|
@ -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,15 +148,15 @@ 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() {
|
||||
static void Tile_initTiles_injection(__attribute__((unused)) void *null) {
|
||||
make_cake();
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
|
|
|
@ -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,13 +30,13 @@ 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
|
||||
void init_camera() {
|
||||
// Implement AppPlatform_linux::saveScreenshot So Cameras Work
|
||||
overwrite_calls(AppPlatform_saveScreenshot, AppPlatform_saveScreenshot_injection);
|
||||
overwrite_virtual_calls(AppPlatform_saveScreenshot, AppPlatform_saveScreenshot_injection);
|
||||
|
||||
// Fix Camera Rendering
|
||||
if (feature_has("Fix Camera Rendering", server_disabled)) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,13 +129,11 @@ 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_calls(LocalPlayer_die, LocalPlayer_die_injection);
|
||||
patch_vtable(LocalPlayer_actuallyHurt, LocalPlayer_actuallyHurt_injection);
|
||||
patch_vtable(ServerPlayer_actuallyHurt, ServerPlayer_actuallyHurt_injection);
|
||||
overwrite_calls(Mob_hurt, Mob_hurt_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);
|
||||
}
|
||||
|
||||
// Fix TNT
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
__attribute__((visibility("internal"))) void _init_game_mode_ui();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 { \
|
||||
|
@ -252,6 +252,11 @@ create_SelectWorldScreen_tick_injection(Touch_)
|
|||
// Init
|
||||
void _init_game_mode_ui() {
|
||||
// Hijack Create World Button
|
||||
overwrite_calls(SelectWorldScreen_tick, SelectWorldScreen_tick_injection);
|
||||
overwrite_calls(Touch_SelectWorldScreen_tick, Touch_SelectWorldScreen_tick_injection);
|
||||
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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,14 +49,14 @@ static ItemInstance *Player_attack_Inventory_getSelected_injection(Inventory *in
|
|||
}
|
||||
|
||||
// Call Original Method
|
||||
return inventory->getSelected();
|
||||
return Inventory_getSelected(inventory);
|
||||
}
|
||||
|
||||
// Init
|
||||
void _init_attack() {
|
||||
// Allow Attacking Mobs
|
||||
if (feature_has("Fix Attacking", server_disabled)) {
|
||||
overwrite_calls(MouseBuildInput_tickBuild, MouseBuildInput_tickBuild_injection);
|
||||
overwrite_virtual_calls(MouseBuildInput_tickBuild, MouseBuildInput_tickBuild_injection);
|
||||
|
||||
// Fix Holding Attack
|
||||
overwrite_call((void *) 0x8fc1c, (void *) Player_attack_Entity_hurt_injection);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
@ -27,95 +24,137 @@
|
|||
} \
|
||||
}
|
||||
|
||||
// Run Functions On Update
|
||||
SETUP_CALLBACK(update, Minecraft);
|
||||
// Handle Custom Update Behavior
|
||||
static void Minecraft_update_injection(Minecraft_update_t original, Minecraft *minecraft) {
|
||||
// Call Original Method
|
||||
original(minecraft);
|
||||
|
||||
// Run Functions
|
||||
handle_misc_update(minecraft);
|
||||
}
|
||||
|
||||
// Run Functions On Tick
|
||||
SETUP_CALLBACK(tick, Minecraft);
|
||||
// Handle Custom Tick Behavior
|
||||
static void Minecraft_tick_injection(Minecraft_tick_t original, Minecraft *minecraft, int32_t param_1, int32_t param_2) {
|
||||
// Call Original Method
|
||||
original(minecraft, param_1, param_2);
|
||||
|
||||
// Run Functions
|
||||
handle_misc_tick(minecraft);
|
||||
}
|
||||
|
||||
// Run Functions On Recipes Setup
|
||||
SETUP_CALLBACK(recipes_setup, Recipes);
|
||||
// Handle Custom Recipes Setup Behavior
|
||||
static Recipes *Recipes_injection(Recipes_constructor_t original, Recipes *recipes) {
|
||||
// Call Original Method
|
||||
original(recipes);
|
||||
|
||||
// Run Functions
|
||||
handle_misc_recipes_setup(recipes);
|
||||
|
||||
// Return
|
||||
return recipes;
|
||||
}
|
||||
|
||||
// Run Functions On Furnace Recipes Setup
|
||||
SETUP_CALLBACK(furnace_recipes_setup, FurnaceRecipes);
|
||||
// Handle Custom Furnace Recipes Setup Behavior
|
||||
static FurnaceRecipes *FurnaceRecipes_injection(FurnaceRecipes_constructor_t original, FurnaceRecipes *recipes) {
|
||||
// Call Original Method
|
||||
original(recipes);
|
||||
|
||||
// Run Functions
|
||||
handle_misc_furnace_recipes_setup(recipes);
|
||||
|
||||
// Return
|
||||
return recipes;
|
||||
}
|
||||
|
||||
// Run Functions On Creative Inventory Setup
|
||||
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);
|
||||
}
|
||||
|
||||
// API
|
||||
void misc_run_on_update(const std::function<void(Minecraft *)> &func) {
|
||||
overwrite_calls(Minecraft_update, [func](Minecraft_update_t original, Minecraft *self) {
|
||||
original(self);
|
||||
func(self);
|
||||
});
|
||||
}
|
||||
void misc_run_on_tick(const std::function<void(Minecraft *)> &func) {
|
||||
overwrite_calls(Minecraft_tick, [func](Minecraft_tick_t original, Minecraft *self, int tick, int max_ticks) {
|
||||
original(self, tick, max_ticks);
|
||||
func(self);
|
||||
});
|
||||
}
|
||||
void misc_run_on_recipes_setup(const std::function<void(Recipes *)> &func) {
|
||||
overwrite_calls(Recipes_constructor, [func](Recipes_constructor_t original, Recipes *self) {
|
||||
original(self);
|
||||
func(self);
|
||||
return self;
|
||||
});
|
||||
}
|
||||
void misc_run_on_furnace_recipes_setup(const std::function<void(FurnaceRecipes *)> &func) {
|
||||
overwrite_calls(FurnaceRecipes_constructor, [func](FurnaceRecipes_constructor_t original, FurnaceRecipes *self) {
|
||||
original(self);
|
||||
func(self);
|
||||
return self;
|
||||
});
|
||||
}
|
||||
void misc_run_on_tiles_setup(const std::function<void()> &func) {
|
||||
overwrite_calls(Tile_initTiles, [func](Tile_initTiles_t original) {
|
||||
func();
|
||||
// Run Functions On Tiles Setup
|
||||
SETUP_CALLBACK(tiles_setup, void);
|
||||
// Handle Custom Tiles Setup Behavior
|
||||
static void Tile_initTiles_injection(Tile_initTiles_t original) {
|
||||
// Run Functions
|
||||
handle_misc_tiles_setup(nullptr);
|
||||
|
||||
// Call Original Method
|
||||
original();
|
||||
});
|
||||
}
|
||||
void misc_run_on_items_setup(const std::function<void()> &func) {
|
||||
overwrite_calls(Item_initItems, [func](Item_initItems_t original) {
|
||||
original();
|
||||
func();
|
||||
});
|
||||
}
|
||||
void misc_run_on_language_setup(const std::function<void()> &func) {
|
||||
overwrite_calls(I18n_loadLanguage, [func](I18n_loadLanguage_t original, AppPlatform *self, std::string language_name) {
|
||||
original(self, language_name);
|
||||
func();
|
||||
});
|
||||
}
|
||||
void misc_run_on_game_key_press(const std::function<bool(Minecraft *, int)> &func) {
|
||||
overwrite_calls(Gui_handleKeyPressed, [func](Gui_handleKeyPressed_t original, Gui *self, int key) {
|
||||
if (func(self->minecraft, key)) {
|
||||
return;
|
||||
}
|
||||
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();
|
||||
// Run Functions On Items Setup
|
||||
SETUP_CALLBACK(items_setup, void);
|
||||
// Handle Custom Items Setup Behavior
|
||||
static void Item_initItems_injection(Item_initItems_t original) {
|
||||
// Call Original Method
|
||||
original();
|
||||
|
||||
// Run Functions
|
||||
handle_misc_items_setup(nullptr);
|
||||
}
|
||||
|
||||
// Run Functions On Language Setup
|
||||
SETUP_CALLBACK(language_setup, void);
|
||||
// Handle Custom Items Setup Behavior
|
||||
static void I18n_loadLanguage_injection(I18n_loadLanguage_t original, AppPlatform *app, std::string language_name) {
|
||||
// Call Original Method
|
||||
original(app, std::move(language_name));
|
||||
|
||||
// Run Functions
|
||||
handle_misc_language_setup(nullptr);
|
||||
}
|
||||
|
||||
// Run Functions On GUI Key Press
|
||||
STORE_CALLBACK(game_key_press, key_press)
|
||||
static bool handle_misc_game_key_press(Minecraft *minecraft, int key) {
|
||||
for (misc_update_function_key_press_t function : get_misc_game_key_press_functions()) {
|
||||
if (function(minecraft, key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Handle Key Presses
|
||||
static void Gui_handleKeyPressed_injection(Gui_handleKeyPressed_t original, Gui *self, int key) {
|
||||
// Run Functions
|
||||
if (handle_misc_game_key_press(self->minecraft, key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Call Original Method
|
||||
original(self, key);
|
||||
}
|
||||
|
||||
// Init
|
||||
void _init_misc_api() {
|
||||
// Handle Custom Update Behavior
|
||||
overwrite_virtual_calls(Minecraft_update, Minecraft_update_injection);
|
||||
// Handle Custom Tick Behavior
|
||||
overwrite_calls(Minecraft_tick, Minecraft_tick_injection);
|
||||
// Handle Custom Recipe Setup Behavior
|
||||
overwrite_calls(Recipes_constructor, Recipes_injection);
|
||||
overwrite_calls(FurnaceRecipes_constructor, FurnaceRecipes_injection);
|
||||
// Handle Custom Creative Inventory Setup Behavior
|
||||
overwrite_call((void *) 0x8e0fc, (void *) Inventory_setupDefault_FillingContainer_addItem_call_injection);
|
||||
// Handle Custom Item/Tile Init Behavior
|
||||
overwrite_calls(Tile_initTiles, Tile_initTiles_injection);
|
||||
overwrite_calls(Item_initItems, Item_initItems_injection);
|
||||
// Handle Custom Language Entries
|
||||
overwrite_calls(I18n_loadLanguage, I18n_loadLanguage_injection);
|
||||
// Handle Key Presses
|
||||
overwrite_calls(Gui_handleKeyPressed, Gui_handleKeyPressed_injection);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
static int32_t Tile_getColor_injection(__attribute__((unused)) std::function<int(T *, LevelSource *, int, int, int)> original, __attribute__((unused)) T *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
|
||||
|
@ -796,10 +797,10 @@ void init_misc() {
|
|||
}
|
||||
|
||||
// Fix Screen Rendering When GUI is Hidden
|
||||
overwrite_calls(Screen_render, Screen_render_injection);
|
||||
overwrite_virtual_calls(Screen_render, Screen_render_injection);
|
||||
|
||||
// Sanitize Username
|
||||
overwrite_calls(LoginPacket_read, LoginPacket_read_injection);
|
||||
overwrite_virtual_calls(LoginPacket_read, LoginPacket_read_injection);
|
||||
|
||||
// Fix RakNet::RakString Security Bug
|
||||
overwrite_calls_manual((void *) RakNet_RakString_constructor, (void *) RakNet_RakString_injection);
|
||||
|
@ -812,7 +813,7 @@ void init_misc() {
|
|||
|
||||
// Close Current Screen On Death To Prevent Bugs
|
||||
if (feature_has("Close Current Screen On Death", server_disabled)) {
|
||||
overwrite_calls(LocalPlayer_die, LocalPlayer_die_injection);
|
||||
overwrite_virtual_calls(LocalPlayer_die, LocalPlayer_die_injection);
|
||||
}
|
||||
|
||||
// Fix Furnace Not Checking Item Auxiliary When Inserting New Item
|
||||
|
@ -822,10 +823,10 @@ void init_misc() {
|
|||
|
||||
#ifdef MCPI_HEADLESS_MODE
|
||||
// Don't Render Game In Headless Mode
|
||||
overwrite_manual((void *) GameRenderer_render, (void *) nop);
|
||||
overwrite_manual((void *) NinecraftApp_initGLStates, (void *) nop);
|
||||
overwrite_manual((void *) Gui_onConfigChanged, (void *) nop);
|
||||
overwrite_manual((void *) LevelRenderer_generateSky, (void *) 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((void *) Common_sleepMs, (void *) 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,8 +875,8 @@ void init_misc() {
|
|||
|
||||
// Change Grass Color
|
||||
if (feature_has("Add Biome Colors To Grass", server_disabled)) {
|
||||
patch_vtable(GrassTile_getColor, GrassTile_getColor_injection);
|
||||
overwrite_calls(TallGrass_getColor, TallGrass_getColor_injection);
|
||||
patch_address(GrassTile_getColor_vtable_addr, GrassTile_getColor_injection);
|
||||
overwrite_virtual_calls(TallGrass_getColor, TallGrass_getColor_injection);
|
||||
}
|
||||
|
||||
// Generate Caves
|
||||
|
@ -885,11 +886,11 @@ void init_misc() {
|
|||
|
||||
// Disable Block Tinting
|
||||
if (feature_has("Disable Block Tinting", server_disabled)) {
|
||||
overwrite_calls(GrassTile_getColor, Tile_getColor_injection<GrassTile>);
|
||||
overwrite_calls(TallGrass_getColor, Tile_getColor_injection<TallGrass>);
|
||||
overwrite_calls(StemTile_getColor, Tile_getColor_injection<StemTile>);
|
||||
overwrite_calls(LeafTile_getColor, Tile_getColor_injection<LeafTile>);
|
||||
overwrite_calls(LiquidTile_getColor, Tile_getColor_injection<LiquidTile>);
|
||||
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
|
||||
|
@ -924,14 +925,14 @@ void init_misc() {
|
|||
|
||||
// Animation
|
||||
overwrite_calls(ContainerMenu_constructor, ContainerMenu_injection);
|
||||
overwrite_calls(ContainerMenu_destructor_complete, ContainerMenu_destructor_injection);
|
||||
overwrite_virtual_calls(ContainerMenu_destructor_complete, ContainerMenu_destructor_injection);
|
||||
}
|
||||
patch_address((void *) 0x115b48, (void *) ChestTileEntity_shouldSave_injection);
|
||||
|
||||
#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_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,32 +971,29 @@ void init_misc() {
|
|||
|
||||
// Implement AppPlatform::readAssetFile So Translations Work
|
||||
if (feature_has("Load Language Files", server_enabled)) {
|
||||
overwrite_calls(AppPlatform_readAssetFile, AppPlatform_readAssetFile_injection);
|
||||
overwrite(*AppPlatform_readAssetFile_vtable_addr, AppPlatform_readAssetFile_injection);
|
||||
}
|
||||
|
||||
// Fix Pause Menu
|
||||
if (feature_has("Fix Pause Menu", server_disabled)) {
|
||||
// Add Missing Buttons To Pause Menu
|
||||
overwrite_calls(PauseScreen_init, PauseScreen_init_injection);
|
||||
overwrite_virtual_calls(PauseScreen_init, PauseScreen_init_injection);
|
||||
}
|
||||
|
||||
// Implement Crafting Remainders
|
||||
overwrite_call((void *) 0x2e230, (void *) PaneCraftingScreen_craftSelectedItem_PaneCraftingScreen_recheckRecipes_injection);
|
||||
overwrite_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);
|
||||
}
|
||||
|
||||
// Don't Wrap Text On '\r' Or '\t' Because THey Are Actual Characters In MCPI
|
||||
patch_address(&Strings::text_wrapping_delimiter, (void *) " \n");
|
||||
|
||||
// Init Logging
|
||||
_init_misc_logging();
|
||||
_init_misc_api();
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -130,6 +130,6 @@ static void RakNetInstance_pingForHosts_injection(RakNetInstance_pingForHosts_t
|
|||
void init_multiplayer() {
|
||||
// Inject Code
|
||||
if (feature_has("External Server Support", server_disabled)) {
|
||||
overwrite_calls(RakNetInstance_pingForHosts, RakNetInstance_pingForHosts_injection);
|
||||
overwrite_virtual_calls(RakNetInstance_pingForHosts, RakNetInstance_pingForHosts_injection);
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user