Compare commits

..

7 Commits

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

View File

@ -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

View File

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

View File

@ -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

@ -1 +1 @@
Subproject commit 0d7025eb709351d2db51d19a46ff8e2640bd4465
Subproject commit 37d4baec5874b39e10cafda2f9fcf6b63129c85a

View File

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

View File

@ -16,38 +16,25 @@ if(BUILD_NATIVE_COMPONENTS)
if(NOT IS_ARM_TARGETING)
set(MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN TRUE)
endif()
if(MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN)
include("${CMAKE_CURRENT_LIST_DIR}/prebuilt-armhf-toolchain.cmake")
endif()
endif()
# Media Layer
if(NOT MCPI_HEADLESS_MODE)
set(DEFAULT_USE_MEDIA_LAYER_TRAMPOLINE FALSE)
set(DEFAULT_USE_MEDIA_LAYER_PROXY FALSE)
if(BUILD_NATIVE_COMPONENTS AND NOT IS_ARM_TARGETING)
set(DEFAULT_USE_MEDIA_LAYER_TRAMPOLINE TRUE)
set(DEFAULT_USE_MEDIA_LAYER_PROXY TRUE)
endif()
mcpi_option(USE_MEDIA_LAYER_TRAMPOLINE "Whether To Enable The Media Layer Trampoline (Requires QEMU)" BOOL "${DEFAULT_USE_MEDIA_LAYER_TRAMPOLINE}")
mcpi_option(USE_MEDIA_LAYER_PROXY "Whether To Enable The Media Layer Proxy" BOOL "${DEFAULT_USE_MEDIA_LAYER_PROXY}")
mcpi_option(USE_GLES1_COMPATIBILITY_LAYER "Whether To Enable The GLESv1_CM Compatibility Layer" BOOL TRUE)
else()
set(MCPI_USE_MEDIA_LAYER_TRAMPOLINE FALSE)
set(MCPI_USE_MEDIA_LAYER_PROXY FALSE)
endif()
if(MCPI_USE_MEDIA_LAYER_TRAMPOLINE)
if(MCPI_USE_MEDIA_LAYER_PROXY)
set(BUILD_MEDIA_LAYER_CORE "${BUILD_NATIVE_COMPONENTS}")
else()
set(BUILD_MEDIA_LAYER_CORE "${BUILD_ARM_COMPONENTS}")
endif()
# QEMU
if(BUILD_NATIVE_COMPONENTS)
include(CheckSymbolExists)
check_symbol_exists("__ARM_ARCH" "" MCPI_IS_ARM32_OR_ARM64_TARGETING)
set(MCPI_USE_QEMU TRUE)
if(MCPI_IS_ARM32_OR_ARM64_TARGETING AND NOT MCPI_USE_MEDIA_LAYER_TRAMPOLINE)
set(MCPI_USE_QEMU FALSE)
endif()
endif()
# Specify Variant Name
set(MCPI_VARIANT_NAME "minecraft-pi-reborn")
if(MCPI_SERVER_MODE)
@ -63,7 +50,7 @@ if(MCPI_SERVER_MODE)
else()
string(APPEND DEFAULT_APP_ID "Client")
endif()
mcpi_option(APP_ID "App ID" STRING "${DEFAULT_APP_ID}")
set(MCPI_APP_ID "${DEFAULT_APP_ID}" CACHE STRING "App ID")
# App Title
mcpi_option(APP_BASE_TITLE "Base App Title" STRING "Minecraft: Pi Edition: Reborn")
@ -78,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()

View File

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

@ -1 +1 @@
Subproject commit 3ee682f6f26dd4344f0701ae346d10250c040d8e
Subproject commit 67a8d026aa5aef062dae654d418c3cd09417c0c1

View File

@ -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

View File

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

@ -1 +1 @@
Subproject commit 8249a305df07a0b087b44341afa8c3b27ad7a156
Subproject commit db3879f7a51c5413e1c17e17cd6949d711132468

106
docs/ARCHITECTURE.md Normal file
View 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
View 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...]
```

View File

@ -3,7 +3,6 @@
**3.0.0**
* Modding API Revamped
* `*(unsigned char **)` Is Dead!
* Now C++ Only
* Add Peaceful Mode To Options Screen
* Proper Create New World Screen
* Proper Chat Screen
@ -15,15 +14,6 @@
* Add `Display Date In Select World Screen` Feature Flag (Enabled By Default)
* Add `Optimized Chunk Sorting` Feature Flag (Enabled By Default)
* Add `Add Cake` Feature Flag (Enabled By Default)
* Add `Add Reborn Info To Options` Feature Flag (Enabled By Default)
* Add `Track FPS` Feature Flag (Disabled By Default)
* Split Up `Remove Creative Mode Restrictions` Feature Flag
* `Remove Creative Mode Restrictions` (Disabled By Default)
* `Display Slot Count In Creative Mode` (Disabled By Default)
* `Force Survival Mode Inventory UI` (Disabled By Default)
* `Force Survival Mode Inventory Behavior` (Disabled By Default)
* `Maximize Creative Mode Inventory Stack Size` (Disabled By Default)
* Rename `Disable Buggy Held Item Caching` Feature Flag To `Fix Held Item Caching`
* Add Milk Buckets
* Implement Crafting Remainders
* Improve Death Messages

64
docs/COMMAND_LINE.md Normal file
View 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.

View File

@ -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
View 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.

View 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`.

View File

@ -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
View 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`.

View File

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

View File

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

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

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

View File

@ -1,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);
}

View File

@ -1,7 +1,14 @@
#pragma once
#include <string>
#ifdef __cplusplus
extern "C" {
#endif
void bootstrap();
void copy_sdk(const std::string &binary_directory, bool log_with_debug);
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

View File

@ -8,10 +8,6 @@ TRUE Fix Sign Placement
TRUE Show Block Outlines
FALSE Expand Creative Mode Inventory
FALSE Remove Creative Mode Restrictions
FALSE Display Slot Count In Creative Mode
FALSE Force Survival Mode Inventory UI
FALSE Force Survival Mode Inventory Behavior
FALSE Maximize Creative Mode Inventory Stack Size
TRUE Animated Water
TRUE Animated Lava
TRUE Animated Fire
@ -64,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

View File

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

View File

@ -1,23 +0,0 @@
#pragma once
#include <string>
#include <functional>
#include "../options/parser.h"
// Defaults
#define DEFAULT_USERNAME "StevePi"
#define DEFAULT_RENDER_DISTANCE "Short"
// Feature Flags
std::string strip_feature_flag_default(const std::string& flag, bool *default_ret);
void load_available_feature_flags(const std::function<void(std::string)> &callback);
// Handle Non-Launch Commands
void handle_non_launch_client_only_commands(const options_t &options);
// Check Environment
void check_environment_client();
// Configure Client Options
void configure_client(const options_t &options);

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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;
}

View File

@ -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
View File

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

View File

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

View File

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

View File

@ -3,7 +3,6 @@
#cmakedefine MCPI_SERVER_MODE
#cmakedefine MCPI_HEADLESS_MODE
#cmakedefine MCPI_IS_APPIMAGE_BUILD
#cmakedefine MCPI_IS_FLATPAK_BUILD
#cmakedefine MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
#cmakedefine MCPI_USE_GLES1_COMPATIBILITY_LAYER
#cmakedefine MCPI_APP_BASE_TITLE "@MCPI_APP_BASE_TITLE@"
@ -14,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@"

View File

@ -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

View File

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

View File

@ -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

View File

@ -77,7 +77,7 @@ static int _overwrite_calls_within_internal(const char *file, int line, void *fr
#define TEXT_END 0x1020c0
// Overwrite All B(L) Intrusctions That Target The Specified Address
#define NO_CALLSITE_ERROR "(%s:%i) Unable To Find Callsites For %p"
void *_overwrite_calls_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);

View File

@ -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) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,170 @@
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include <csignal>
#include <sys/wait.h>
#include <fcntl.h>
#include <string>
#include <unordered_map>
#include <fstream>
#include <media-layer/core.h>
#include "../common/common.h"
// Track Client State
static int _client_is_alive = 0;
static int _client_status = 0;
static void update_client_state(int is_alive, int status) {
_client_is_alive = is_alive;
_client_status = status;
}
// Check State Of Proxy And Exit If Invalid
void _check_proxy_state() {
// Check Client State
if (!_client_is_alive) {
void_write_cache(); // Child Is Dead, No Reason To Send A Dead Process Data
char *exit_status = NULL;
get_exit_status_string(_client_status, &exit_status);
PROXY_ERR("Client Terminated%s", exit_status);
}
}
// Start Proxy Client
static pid_t _client_pid;
static void sigchld_handler(__attribute__((unused)) int sig) {
// Track
int status;
// Reap
int saved_errno = errno;
// Only waitpid() Proxy Client, Other Sub-Processes Are Handled By pclose()
if (waitpid(_client_pid, &status, WNOHANG) == _client_pid) {
// Handle Client Death
untrack_child(_client_pid);
update_client_state(0, status);
}
errno = saved_errno;
}
static void start_media_layer_proxy_client(int read, int write) {
// Reap Children
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_NOCLDSTOP;
sa.sa_handler = &sigchld_handler;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
PROXY_ERR("Unable To Install Signal Handler: %s", strerror(errno));
}
// Fork And Start
pid_t ret = fork();
if (ret == -1) {
PROXY_ERR("Unable To Launch Client: %s", strerror(errno));
} else if (ret == 0) {
// Child Process
// Set Debug Tag
reborn_debug_tag = CHILD_PROCESS_TAG;
// Prepare Arguments
char *read_str = NULL;
safe_asprintf(&read_str, "%i", read);
char *write_str = NULL;
safe_asprintf(&write_str, "%i", write);
const char *argv[] = {"media-layer-proxy-client", read_str, write_str, NULL};
// Setup Environment
setup_exec_environment(0);
// Run
safe_execvpe(argv, (const char *const *) environ);
} else {
// Parent Process
_client_pid = ret;
track_child(_client_pid);
}
update_client_state(1, 0);
}
// Maximize Pipe Buffer Size
static void maximize_pipe_fd_size(int fd) {
// Read Maximum Pipe Size
std::ifstream max_size_file("/proc/sys/fs/pipe-max-size");
if (!max_size_file.good()) {
PROXY_ERR("%s", "Unable To Open Maximum Pipe Size File");
}
// Read One Line
int max_size;
std::string line;
if (std::getline(max_size_file, line) && line.size() > 0) {
max_size = std::stoi(line);
} else {
PROXY_ERR("%s", "Unable To Read Maximum Pipe Size File");
}
// Close
max_size_file.close();
// Set Maximum Pipe Size
errno = 0;
if (fcntl(fd, F_SETPIPE_SZ, max_size) < max_size) {
PROXY_ERR("Unable To Set Maximum Pipe Size: %s", errno != 0 ? strerror(errno) : "Unknown Error");
}
}
static void maximize_pipe_size(int pipe[2]) {
maximize_pipe_fd_size(pipe[0]);
maximize_pipe_fd_size(pipe[1]);
}
// Start Server
static int loaded = 0;
__attribute__((constructor)) void media_ensure_loaded() {
if (!loaded) {
loaded = 1;
// Log
PROXY_INFO("Starting...");
// Create Connection
int server_to_client_pipe[2];
safe_pipe2(server_to_client_pipe, 0);
maximize_pipe_size(server_to_client_pipe);
int client_to_server_pipe[2];
safe_pipe2(client_to_server_pipe, 0);
maximize_pipe_size(client_to_server_pipe);
// Set Connection
set_connection(client_to_server_pipe[0], server_to_client_pipe[1]);
// Start Client
start_media_layer_proxy_client(server_to_client_pipe[0], client_to_server_pipe[1]);
// Wait For Connection Message
char *str = read_string();
if (strcmp(str, CONNECTED_MSG) == 0) {
PROXY_INFO("Connected");
} else {
PROXY_ERR("Unable To Connect");
}
// Free
free(str);
}
}
// Assign Unique ID To Function
static std::unordered_map<std::string, unsigned char> &get_unique_ids() {
static std::unordered_map<std::string, unsigned char> unique_ids;
return unique_ids;
}
void _assign_unique_id(const char *name, unsigned char id) {
get_unique_ids()[name] = id;
}
unsigned char _get_unique_id(const char *name) {
return get_unique_ids()[name]; // Assume ID Exists
}
// Proxy Call Functions
void _start_proxy_call(unsigned char call_id) {
// Start Call
write_byte(call_id);
}
void end_proxy_call() {
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -31,19 +31,12 @@ set(SRC
src/misc/api.cpp
# options
src/options/options.cpp
src/options/ui.cpp
src/options/info.cpp
# bucket
src/bucket/bucket.cpp
# cake
src/cake/cake.cpp
# home
src/home/home.cpp
# touch
src/touch/touch.cpp
# text-input-box
src/text-input-box/TextInputBox.cpp
src/text-input-box/TextInputScreen.cpp
# test
src/test/test.cpp
# init
@ -89,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
)

View File

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

View File

@ -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);

View File

@ -1,9 +0,0 @@
#pragma once
#include <string>
#define CHANGELOG_FILE "CHANGELOG.md"
extern "C" {
void open_url(const std::string &url);
}

View File

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

View File

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

View File

@ -11,9 +11,9 @@ static FoodItem *bucket = nullptr;
// Description And Texture
static std::string BucketItem_getDescriptionId(__attribute__((unused)) FoodItem *item, ItemInstance *item_instance) {
if (item_instance->auxiliary == Tile::water->id) {
if (item_instance->auxiliary == Tile_water->id) {
return "item.bucketWater";
} else if (item_instance->auxiliary == Tile::lava->id) {
} else if (item_instance->auxiliary == Tile_lava->id) {
return "item.bucketLava";
} else if (item_instance->auxiliary == 1) {
return "item.bucketMilk";
@ -22,9 +22,9 @@ static std::string BucketItem_getDescriptionId(__attribute__((unused)) FoodItem
}
}
static int32_t BucketItem_getIcon(__attribute__((unused)) FoodItem *item, int32_t auxiliary) {
if (auxiliary == Tile::water->id) {
if (auxiliary == Tile_water->id) {
return 75;
} else if (auxiliary == Tile::lava->id) {
} else if (auxiliary == Tile_lava->id) {
return 76;
} else if (auxiliary == 1) {
return 77;
@ -45,7 +45,7 @@ static bool fill_bucket(ItemInstance *item_instance, Player *player, int new_aux
new_item.count = 1;
new_item.auxiliary = new_auxiliary;
Inventory *inventory = player->inventory;
if (inventory->add(&new_item)) {
if (inventory->vtable->add(inventory, &new_item)) {
// Added To Inventory
success = true;
item_instance->count -= 1;
@ -62,16 +62,16 @@ static int32_t BucketItem_useOn(__attribute__((unused)) FoodItem *item, ItemInst
} else if (item_instance->auxiliary == 0) {
// Empty Bucket
int32_t new_auxiliary = 0;
int32_t tile = level->getTile(x, y, z);
if (tile == Tile::calmWater->id) {
new_auxiliary = Tile::water->id;
} else if (tile == Tile::calmLava->id) {
new_auxiliary = Tile::lava->id;
int32_t tile = level->vtable->getTile(level, x, y, z);
if (tile == Tile_calmWater->id) {
new_auxiliary = Tile_water->id;
} else if (tile == Tile_calmLava->id) {
new_auxiliary = Tile_lava->id;
}
if (new_auxiliary != 0) {
// Valid
if (fill_bucket(item_instance, player, new_auxiliary)) {
level->setTileAndData(x, y, z, 0, 0);
Level_setTileAndData(level, x, y, z, 0, 0);
return 1;
} else {
return 0;
@ -110,15 +110,15 @@ static int32_t BucketItem_useOn(__attribute__((unused)) FoodItem *item, ItemInst
}
// Get Current Tile
bool valid = false;
Material *material = level->getMaterial(x, y, z);
Material *material = level->vtable->getMaterial(level, x, y, z);
if (material != nullptr) {
valid = !material->isSolid();
valid = !material->vtable->isSolid(material);
}
if (item_instance->auxiliary != Tile::water->id && item_instance->auxiliary != Tile::lava->id) {
if (item_instance->auxiliary != Tile_water->id && item_instance->auxiliary != Tile_lava->id) {
valid = false;
}
if (valid) {
level->setTileAndData(x, y, z, item_instance->auxiliary, 0);
Level_setTileAndData(level, x, y, z, item_instance->auxiliary, 0);
item_instance->auxiliary = 0;
return 1;
} else {
@ -163,7 +163,7 @@ static ItemInstance *BucketItem_getCraftingRemainingItem(FoodItem *item, ItemIns
if (item_instance->auxiliary == 0) {
return nullptr;
}
ItemInstance *ret = new ItemInstance;
ItemInstance *ret = alloc_ItemInstance();
ret->id = item->id;
ret->count = item_instance->count;
ret->auxiliary = 0;
@ -186,16 +186,16 @@ CUSTOM_VTABLE(bucket, FoodItem) {
// Create Items
static FoodItem *create_bucket(int32_t id, int32_t texture_x, int32_t texture_y, std::string name) {
// Construct
FoodItem *item = new FoodItem;
FoodItem *item = alloc_FoodItem();
ALLOC_CHECK(item);
Item_constructor((Item *) item, id); // FoodItem's Constructor Was Inlined
Item_constructor((Item *) item, id);
// Set VTable
item->vtable = get_bucket_vtable();
// Setup
item->setIcon(texture_x, texture_y);
item->setDescriptionId(&name);
item->vtable->setIcon(item, texture_x, texture_y);
item->vtable->setDescriptionId(item, &name);
item->is_stacked_by_data = 1;
item->category = 2;
item->max_damage = 0;
@ -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

View File

@ -31,7 +31,7 @@ static int Cake_getTexture2(__attribute__((unused)) Tile *tile, int face, __attr
static int Cake_getTexture3(__attribute__((unused)) Tile *tile, LevelSource *level, int x, int y, int z, int face) {
// Eaten face
if (face == 3) {
int data = level->getData(x, y, z);
int data = level->vtable->getData(level, x, y, z);
if (data != 0 && data < 6) {
// Sliced texture
return 123;
@ -59,7 +59,8 @@ static bool Cake_isCubeShaped(__attribute__((unused)) Tile *tile) {
// Size
static void Cake_updateDefaultShape(Tile *tile) {
// Set the default shape
tile->setShape(
tile->vtable->setShape(
tile,
CAKE_LEN, 0.0, CAKE_LEN,
1.0 - CAKE_LEN, 0.5, 1.0 - CAKE_LEN
);
@ -67,7 +68,7 @@ static void Cake_updateDefaultShape(Tile *tile) {
static AABB *Cake_getAABB(Tile *tile, Level *level, int x, int y, int z) {
// Get the size of the slices
int data = level->getData(x, y, z);
int data = level->vtable->getData(level, x, y, z);
if (data >= 6) data = 0;
float slice_size = (1.0 / 7.0) * (float) data;
@ -87,11 +88,12 @@ static AABB *Cake_getAABB(Tile *tile, Level *level, int x, int y, int z) {
static void Cake_updateShape(Tile *tile, LevelSource *level, int x, int y, int z) {
// Set cake
int data = level->getData(x, y, z);
int data = level->vtable->getData(level, x, y, z);
if (data >= 6) data = 0;
// Get slice amount
float slice_size = (1.0 / 7.0) * (float) data;
tile->setShape(
tile->vtable->setShape(
tile,
CAKE_LEN, 0.0, CAKE_LEN,
1.0 - CAKE_LEN, 0.5, (1.0 - CAKE_LEN) - slice_size
);
@ -100,15 +102,15 @@ static void Cake_updateShape(Tile *tile, LevelSource *level, int x, int y, int z
// Eating
static int Cake_use(__attribute__((unused)) Tile *tile, Level *level, int x, int y, int z, Player *player) {
// Eat
player->foodData.eat(3);
SimpleFoodData_eat(&player->foodData, 3);
// Set the new tile
int data = level->getData(x, y, z);
int data = level->vtable->getData(level, x, y, z);
if (data >= 5) {
// Remove the cake, it has been completely gobbled up
level->setTileAndData(x, y, z, 0, 0);
Level_setTileAndData(level, x, y, z, 0, 0);
} else {
// Remove a slice
level->setTileAndData(x, y, z, 92, data + 1);
Level_setTileAndData(level, x, y, z, 92, data + 1);
}
return 1;
}
@ -116,10 +118,10 @@ static int Cake_use(__attribute__((unused)) Tile *tile, Level *level, int x, int
// Makes the cakes
static void make_cake() {
// Construct
cake = new Tile;
cake = alloc_Tile();
ALLOC_CHECK(cake);
int texture = 122;
cake->constructor(92, texture, Material::dirt);
Tile_constructor(cake, 92, texture, Material_dirt);
cake->texture = texture;
// Set VTable
@ -127,7 +129,8 @@ static void make_cake() {
ALLOC_CHECK(cake->vtable);
// Set shape
cake->setShape(
cake->vtable->setShape(
cake,
CAKE_LEN, 0.0, CAKE_LEN,
1.0 - CAKE_LEN, 0.5, 1.0 - CAKE_LEN
);
@ -145,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() {

View File

@ -19,10 +19,10 @@ static EntityRenderDispatcher *EntityRenderDispatcher_injection(EntityRenderDisp
original(dispatcher);
// Register TripodCameraRenderer
TripodCameraRenderer *renderer = new TripodCameraRenderer;
TripodCameraRenderer *renderer = alloc_TripodCameraRenderer();
ALLOC_CHECK(renderer);
renderer->constructor();
dispatcher->assign((unsigned char) 0x5, (EntityRenderer *) renderer);
TripodCameraRenderer_constructor(renderer);
EntityRenderDispatcher_assign(dispatcher, (unsigned char) 0x5, (EntityRenderer *) renderer);
return dispatcher;
}
@ -30,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)) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,9 +3,6 @@
#include <libreborn/libreborn.h>
#include <symbols/minecraft.h>
#ifndef MCPI_HEADLESS_MODE
#include <GLES/gl.h>
#endif
#include <mods/misc/misc.h>
#include "misc-internal.h"
@ -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);
}

View File

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

View File

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

View File

@ -24,61 +24,17 @@
#include "misc-internal.h"
#include <mods/misc/misc.h>
// Classic HUD
#define DEFAULT_HUD_PADDING 2
#define NEW_HUD_PADDING 1
#define HUD_ELEMENT_WIDTH 82
#define HUD_ELEMENT_HEIGHT 9
#define TOOLBAR_HEIGHT 22
#define SLOT_WIDTH 20
#define DEFAULT_BUBBLES_PADDING 1
#define NUMBER_OF_SLOTS 9
static int use_classic_hud = 0;
static void Gui_renderHearts_GuiComponent_blit_hearts_injection(GuiComponent *component, int32_t x_dest, int32_t y_dest, int32_t x_src, int32_t y_src, int32_t width_dest, int32_t height_dest, int32_t width_src, int32_t height_src) {
Minecraft *minecraft = ((Gui *) component)->minecraft;
x_dest -= DEFAULT_HUD_PADDING;
float width = ((float) minecraft->screen_width) * Gui::InvGuiScale;
float height = ((float) minecraft->screen_height) * Gui::InvGuiScale;
x_dest += (width - (NUMBER_OF_SLOTS * SLOT_WIDTH)) / 2;
y_dest -= DEFAULT_HUD_PADDING;
y_dest += height - HUD_ELEMENT_HEIGHT - TOOLBAR_HEIGHT - NEW_HUD_PADDING;
// Call Original Method
component->blit(x_dest, y_dest, x_src, y_src, width_dest, height_dest, width_src, height_src);
}
static void Gui_renderHearts_GuiComponent_blit_armor_injection(Gui *component, int32_t x_dest, int32_t y_dest, int32_t x_src, int32_t y_src, int32_t width_dest, int32_t height_dest, int32_t width_src, int32_t height_src) {
Minecraft *minecraft = component->minecraft;
x_dest -= DEFAULT_HUD_PADDING + HUD_ELEMENT_WIDTH;
float width = ((float) minecraft->screen_width) * Gui::InvGuiScale;
float height = ((float) minecraft->screen_height) * Gui::InvGuiScale;
x_dest += width - ((width - (NUMBER_OF_SLOTS * SLOT_WIDTH)) / 2) - HUD_ELEMENT_WIDTH;
y_dest -= DEFAULT_HUD_PADDING;
y_dest += height - HUD_ELEMENT_HEIGHT - TOOLBAR_HEIGHT - NEW_HUD_PADDING;
// Call Original Method
component->blit(x_dest, y_dest, x_src, y_src, width_dest, height_dest, width_src, height_src);
}
static void Gui_renderBubbles_GuiComponent_blit_injection(Gui *component, int32_t x_dest, int32_t y_dest, int32_t x_src, int32_t y_src, int32_t width_dest, int32_t height_dest, int32_t width_src, int32_t height_src) {
Minecraft *minecraft = component->minecraft;
x_dest -= DEFAULT_HUD_PADDING;
float width = ((float) minecraft->screen_width) * Gui::InvGuiScale;
float height = ((float) minecraft->screen_height) * Gui::InvGuiScale;
x_dest += (width - (NUMBER_OF_SLOTS * SLOT_WIDTH)) / 2;
y_dest -= DEFAULT_HUD_PADDING + DEFAULT_BUBBLES_PADDING + HUD_ELEMENT_HEIGHT;
y_dest += height - HUD_ELEMENT_HEIGHT - TOOLBAR_HEIGHT - HUD_ELEMENT_HEIGHT - NEW_HUD_PADDING;
// Call Original Method
component->blit(x_dest, y_dest, x_src, y_src, width_dest, height_dest, width_src, height_src);
}
// Heart Food Overlay
// Heart food overlay
static int heal_amount = 0, heal_amount_drawing = 0;
static void Gui_renderHearts_injection(Gui_renderHearts_t original, Gui *gui) {
// Get heal_amount
heal_amount = heal_amount_drawing = 0;
Inventory *inventory = gui->minecraft->player->inventory;
ItemInstance *held_ii = inventory->getSelected();
ItemInstance *held_ii = Inventory_getSelected(inventory);
if (held_ii) {
Item *held = Item::items[held_ii->id];
if (held->isFood() && held_ii->id) {
Item *held = Item_items[held_ii->id];
if (held->vtable->isFood(held) && held_ii->id) {
int nutrition = ((FoodItem *) held)->nutrition;
int cur_health = gui->minecraft->player->health;
int heal_num = fmin(cur_health + nutrition, 20) - cur_health;
@ -89,42 +45,86 @@ static void Gui_renderHearts_injection(Gui_renderHearts_t original, Gui *gui) {
// Call original
original(gui);
}
static GuiComponent_blit_t get_blit_with_classic_hud_offset() {
return use_classic_hud ? Gui_renderHearts_GuiComponent_blit_hearts_injection : GuiComponent_blit;
}
#define PINK_HEART_FULL 70
#define PINK_HEART_HALF 79
static Gui_blit_t Gui_blit_renderHearts_original = nullptr;
static void Gui_renderHearts_GuiComponent_blit_overlay_empty_injection(Gui *gui, int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t w1, int32_t h1, int32_t w2, int32_t h2) {
// Call Original Method
get_blit_with_classic_hud_offset()((GuiComponent *) gui, x1, y1, x2, y2, w1, h1, w2, h2);
// Render The Overlay
// Call original
Gui_blit_renderHearts_original(gui, x1, y1, x2, y2, w1, h1, w2, h2);
// Render the overlay
if (heal_amount_drawing == 1) {
// Half Heart
get_blit_with_classic_hud_offset()((GuiComponent *) gui, x1, y1, PINK_HEART_HALF, 0, w1, h1, w2, h2);
// Half heart
Gui_blit_renderHearts_original(gui, x1, y1, PINK_HEART_HALF, 0, w1, h1, w2, h2);
heal_amount_drawing = 0;
} else if (heal_amount_drawing > 0) {
// Full Heart
get_blit_with_classic_hud_offset()((GuiComponent *) gui, x1, y1, PINK_HEART_FULL, 0, w1, h1, w2, h2);
// Full heart
Gui_blit_renderHearts_original(gui, x1, y1, PINK_HEART_FULL, 0, w1, h1, w2, h2);
heal_amount_drawing -= 2;
}
}
static void Gui_renderHearts_GuiComponent_blit_overlay_hearts_injection(Gui *gui, int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t w1, int32_t h1, int32_t w2, int32_t h2) {
// Offset the overlay
if (x2 == 52) {
heal_amount_drawing += 2;
} else if (x2 == 61 && heal_amount) {
// Half heart, flipped
get_blit_with_classic_hud_offset()((GuiComponent *) gui, x1, y1, PINK_HEART_FULL, 0, w1, h1, w2, h2);
Gui_blit_renderHearts_original(gui, x1, y1, PINK_HEART_FULL, 0, w1, h1, w2, h2);
heal_amount_drawing += 1;
}
// Call Original Method
get_blit_with_classic_hud_offset()((GuiComponent *) gui, x1, y1, x2, y2, w1, h1, w2, h2);
};
// Call original
Gui_blit_renderHearts_original(gui, x1, y1, x2, y2, w1, h1, w2, h2);
heal_amount_drawing = fmin(heal_amount_drawing, heal_amount);
}
// Classic HUD
#define DEFAULT_HUD_PADDING 2
#define NEW_HUD_PADDING 1
#define HUD_ELEMENT_WIDTH 82
#define HUD_ELEMENT_HEIGHT 9
#define TOOLBAR_HEIGHT 22
#define SLOT_WIDTH 20
#define DEFAULT_BUBBLES_PADDING 1
#define NUMBER_OF_SLOTS 9
static int use_classic_hud = 0;
static void Gui_renderHearts_GuiComponent_blit_hearts_injection(Gui *component, int32_t x_dest, int32_t y_dest, int32_t x_src, int32_t y_src, int32_t width_dest, int32_t height_dest, int32_t width_src, int32_t height_src) {
Minecraft *minecraft = component->minecraft;
x_dest -= DEFAULT_HUD_PADDING;
float width = ((float) minecraft->screen_width) * Gui_InvGuiScale;
float height = ((float) minecraft->screen_height) * Gui_InvGuiScale;
x_dest += (width - (NUMBER_OF_SLOTS * SLOT_WIDTH)) / 2;
y_dest -= DEFAULT_HUD_PADDING;
y_dest += height - HUD_ELEMENT_HEIGHT - TOOLBAR_HEIGHT - NEW_HUD_PADDING;
// Call Original Method
Gui_blit(component, x_dest, y_dest, x_src, y_src, width_dest, height_dest, width_src, height_src);
}
static void Gui_renderHearts_GuiComponent_blit_armor_injection(Gui *component, int32_t x_dest, int32_t y_dest, int32_t x_src, int32_t y_src, int32_t width_dest, int32_t height_dest, int32_t width_src, int32_t height_src) {
Minecraft *minecraft = component->minecraft;
x_dest -= DEFAULT_HUD_PADDING + HUD_ELEMENT_WIDTH;
float width = ((float) minecraft->screen_width) * Gui_InvGuiScale;
float height = ((float) minecraft->screen_height) * Gui_InvGuiScale;
x_dest += width - ((width - (NUMBER_OF_SLOTS * SLOT_WIDTH)) / 2) - HUD_ELEMENT_WIDTH;
y_dest -= DEFAULT_HUD_PADDING;
y_dest += height - HUD_ELEMENT_HEIGHT - TOOLBAR_HEIGHT - NEW_HUD_PADDING;
// Call Original Method
Gui_blit(component, x_dest, y_dest, x_src, y_src, width_dest, height_dest, width_src, height_src);
}
static void Gui_renderBubbles_GuiComponent_blit_injection(Gui *component, int32_t x_dest, int32_t y_dest, int32_t x_src, int32_t y_src, int32_t width_dest, int32_t height_dest, int32_t width_src, int32_t height_src) {
Minecraft *minecraft = component->minecraft;
x_dest -= DEFAULT_HUD_PADDING;
float width = ((float) minecraft->screen_width) * Gui_InvGuiScale;
float height = ((float) minecraft->screen_height) * Gui_InvGuiScale;
x_dest += (width - (NUMBER_OF_SLOTS * SLOT_WIDTH)) / 2;
y_dest -= DEFAULT_HUD_PADDING + DEFAULT_BUBBLES_PADDING + HUD_ELEMENT_HEIGHT;
y_dest += height - HUD_ELEMENT_HEIGHT - TOOLBAR_HEIGHT - HUD_ELEMENT_HEIGHT - NEW_HUD_PADDING;
// Call Original Method
Gui_blit(component, x_dest, y_dest, x_src, y_src, width_dest, height_dest, width_src, height_src);
}
// Additional GUI Rendering
static int hide_chat_messages = 0;
bool is_in_chat = false;
bool is_in_chat = 0;
static int render_selected_item_text = 0;
static void Gui_renderChatMessages_injection(Gui_renderChatMessages_t original, Gui *gui, int32_t y_offset, uint32_t max_messages, bool disable_fading, Font *font) {
// Handle Classic HUD
@ -149,9 +149,9 @@ static void Gui_renderChatMessages_injection(Gui_renderChatMessages_t original,
// Calculate Selected Item Text Scale
Minecraft *minecraft = gui->minecraft;
int32_t screen_width = minecraft->screen_width;
float scale = ((float) screen_width) * Gui::InvGuiScale;
float scale = ((float) screen_width) * Gui_InvGuiScale;
// Render Selected Item Text
gui->renderOnSelectItemNameText((int32_t) scale, font, y_offset - 0x13);
Gui_renderOnSelectItemNameText(gui, (int32_t) scale, font, y_offset - 0x13);
}
}
// Reset Selected Item Text Timer On Slot Select
@ -235,7 +235,7 @@ static void LoginPacket_read_injection(LoginPacket_read_t original, LoginPacket
ALLOC_CHECK(new_username);
sanitize_string(&new_username, MAX_USERNAME_LENGTH, 0);
// Set New Username
rak_string->Assign(new_username);
RakNet_RakString_Assign(rak_string, new_username);
// Free
free(new_username);
}
@ -272,7 +272,7 @@ static const char *RAKNET_ERROR_NAMES[] = {
#endif
static RakNet_StartupResult RakNetInstance_host_RakNet_RakPeer_Startup_injection(RakNet_RakPeer *rak_peer, unsigned short maxConnections, unsigned char *socketDescriptors, uint32_t socketDescriptorCount, int32_t threadPriority) {
// Call Original Method
RakNet_StartupResult result = rak_peer->Startup(maxConnections, socketDescriptors, socketDescriptorCount, threadPriority);
RakNet_StartupResult result = rak_peer->vtable->Startup(rak_peer, maxConnections, socketDescriptors, socketDescriptorCount, threadPriority);
// Print Error
if (result != RAKNET_STARTED) {
@ -297,7 +297,7 @@ static RakNetInstance *RakNetInstance_injection(RakNetInstance_constructor_t ori
static void LocalPlayer_die_injection(LocalPlayer_die_t original, LocalPlayer *entity, Entity *cause) {
// Close Screen
Minecraft *minecraft = entity->minecraft;
minecraft->setScreen(nullptr);
Minecraft_setScreen(minecraft, nullptr);
// Call Original Method
original(entity, cause);
@ -307,7 +307,7 @@ static void LocalPlayer_die_injection(LocalPlayer_die_t original, LocalPlayer *e
static int32_t FurnaceScreen_handleAddItem_injection(FurnaceScreen_handleAddItem_t original, FurnaceScreen *furnace_screen, int32_t slot, ItemInstance *item) {
// Get Existing Item
FurnaceTileEntity *tile_entity = furnace_screen->tile_entity;
ItemInstance *existing_item = tile_entity->getItem(slot);
ItemInstance *existing_item = tile_entity->vtable->getItem(tile_entity, slot);
// Check Item
int valid;
@ -349,11 +349,11 @@ static void GameRenderer_render_injection(GameRenderer_render_t original, GameRe
// Fix GL Mode
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Get X And Y
float x = Mouse::getX() * Gui::InvGuiScale;
float y = Mouse::getY() * Gui::InvGuiScale;
float x = Mouse_getX() * Gui_InvGuiScale;
float y = Mouse_getY() * Gui_InvGuiScale;
// Render Cursor
Minecraft *minecraft = game_renderer->minecraft;
Common::renderCursor(x, y, minecraft);
Common_renderCursor(x, y, minecraft);
}
}
#endif
@ -410,14 +410,14 @@ HOOK(bind, int, (int sockfd, const struct sockaddr *addr, socklen_t addrlen)) {
// Change Grass Color
static int32_t get_color(LevelSource *level_source, int32_t x, int32_t z) {
Biome *biome = level_source->getBiome(x, z);
Biome *biome = level_source->vtable->getBiome(level_source, x, z);
if (biome == nullptr) {
return 0;
}
return biome->color;
}
#define BIOME_BLEND_SIZE 7
static int32_t GrassTile_getColor_injection(__attribute__((unused)) GrassTile *tile, LevelSource *level_source, int32_t x, __attribute__((unused)) int32_t y, int32_t z) {
static int32_t GrassTile_getColor_injection(__attribute__((unused)) Tile *tile, LevelSource *level_source, int32_t x, __attribute__((unused)) int32_t y, int32_t z) {
int r_sum = 0;
int g_sum = 0;
int b_sum = 0;
@ -441,7 +441,7 @@ static int32_t GrassTile_getColor_injection(__attribute__((unused)) GrassTile *t
static int32_t TallGrass_getColor_injection(TallGrass_getColor_t original, TallGrass *tile, LevelSource *level_source, int32_t x, int32_t y, int32_t z) {
int32_t original_color = original(tile, level_source, x, y, z);
if (original_color == 0x339933) {
return GrassTile_getColor_injection(nullptr, level_source, x, y, z);
return GrassTile_getColor_injection((Tile *) tile, level_source, x, y, z);
} else {
return original_color;
}
@ -459,27 +459,26 @@ static void RandomLevelSource_buildSurface_injection(RandomLevelSource_buildSurf
LargeCaveFeature *cave_feature = &random_level_source->cave_feature;
// Generate
cave_feature->apply((ChunkSource *) random_level_source, level, chunk_x, chunk_y, chunk_data, 0);
cave_feature->vtable->apply(cave_feature, (ChunkSource *) random_level_source, level, chunk_x, chunk_y, chunk_data, 0);
}
// No Block Tinting
template <typename T>
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();

View File

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