Improve Modding
This commit is contained in:
parent
a2d3941bd5
commit
a2b2d6e7c4
82
MODDING.md
Normal file
82
MODDING.md
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
# Modding
|
||||||
|
Modding Minecraft: Pi Edition is possible by patching the binary at runtime. To make this easier ``minecraft-pi-dcoker`` includes a libary called ``libcore.so`` which provides several functions to help you patch the game.
|
||||||
|
|
||||||
|
## Hex Addresses
|
||||||
|
Minecraft: Pi Edition has no symbols so you must patch the hex address of an instruction instead of using a function name. Hex addresses can be found using tools like [Ghidra](https://ghidra-sre.org) or [RetDec](https://retdec.com). To find out what a function does, you can find its equivalent in Minecraft: Pocket Edition 0.6.1 and use its name for reference because Minecraft: Pocket Edition 0.6.1 includes symbols.
|
||||||
|
|
||||||
|
## Loading Directories
|
||||||
|
``minecraft-pi-docker`` loads mods from two locations, ``/app/minecraft-pi/mods``, and ``~/.minecraft/mods``. The first location only exists in the Docker container and is immutable, so you should place your mods in ``~/.minecraft/mods`` which is mounted on the host as ``~/.minecraft-pi/mods``.
|
||||||
|
|
||||||
|
## ``libcore.so`` API
|
||||||
|
Header files and the shared library can be download from [Jenkins](https://jenkins.thebrokenrail.com/job/minecraft-pi-docker/job/master/lastSuccessfulBuild/artifact/out/lib).
|
||||||
|
|
||||||
|
### ``void *overwrite(void *start, void *target)``
|
||||||
|
This method replaces a function with another function.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
- **start:** The function you are replacing.
|
||||||
|
- **target:** The function you are replacing it with.
|
||||||
|
|
||||||
|
#### Return Value
|
||||||
|
The original contents of the function.
|
||||||
|
|
||||||
|
#### Warning
|
||||||
|
This should never be used on functions that are only 1 byte long because it overwrites 2 bytes.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```c
|
||||||
|
static int func_injection(int a, int b) {
|
||||||
|
return a + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((constructor)) static void init() {
|
||||||
|
overwrite((void *) 0xabcde, func_injection);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ``void revert_overwrite(void *start, void *original)``
|
||||||
|
This allows you to revert ``overwrite()``. This can be used to call the original version of a function.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
- **start:** The function that was overwritten.
|
||||||
|
- **original:** The return value of ``overwrite()``.
|
||||||
|
|
||||||
|
#### Return Value
|
||||||
|
None
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```c
|
||||||
|
typedef int (*func_t)(int a, int b);
|
||||||
|
static func_t func = (func_t) 0xabcde;
|
||||||
|
static void *func_original = NULL;
|
||||||
|
|
||||||
|
static int func_injection(int a, int b) {
|
||||||
|
revert_overwrite((void *) func, func_original);
|
||||||
|
(*func)(a, b);
|
||||||
|
revert_overwrite((void *) func, func_original);
|
||||||
|
|
||||||
|
return a + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((constructor)) static void init() {
|
||||||
|
func_original = overwrite((void *) func, func_injection);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ``void patch(void *start, unsigned char patch[])``
|
||||||
|
This allows you to replace a specific instruction.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
- **start:** The target instruction.
|
||||||
|
- **patch:** The new instruction (array length must be 4).
|
||||||
|
|
||||||
|
#### Return Value
|
||||||
|
None
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```c
|
||||||
|
__attribute__((constructor)) static void init() {
|
||||||
|
unsigned char patch_data[4] = {0x00, 0x00, 0x00, 0x00};
|
||||||
|
patch((void *) 0xabcde, patch_data);
|
||||||
|
}
|
||||||
|
```
|
@ -3,3 +3,6 @@ This is a project allowing Minecraft: Pi Edition to be run without a Raspberry P
|
|||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
[View Binaries](https://jenkins.thebrokenrail.com/job/minecraft-pi-docker/job/master/lastSuccessfulBuild/artifact/out/)
|
[View Binaries](https://jenkins.thebrokenrail.com/job/minecraft-pi-docker/job/master/lastSuccessfulBuild/artifact/out/)
|
||||||
|
|
||||||
|
## Modding
|
||||||
|
[View Modding](MODDING.md)
|
||||||
|
@ -4,11 +4,6 @@ project(core)
|
|||||||
|
|
||||||
add_compile_options(-Wall -Wextra -Werror)
|
add_compile_options(-Wall -Wextra -Werror)
|
||||||
|
|
||||||
include_directories(include)
|
|
||||||
|
|
||||||
add_library(core SHARED src/core.c)
|
|
||||||
target_link_libraries(core dl)
|
|
||||||
|
|
||||||
add_library(bcm_host SHARED src/bcm_host.c)
|
add_library(bcm_host SHARED src/bcm_host.c)
|
||||||
|
|
||||||
add_executable(launcher src/launcher.c)
|
add_executable(launcher src/launcher.c)
|
||||||
|
@ -19,22 +19,29 @@ static int ends_with(const char *s, const char *t) {
|
|||||||
return strcmp(s + slen - tlen, t) == 0;
|
return strcmp(s + slen - tlen, t) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_and_print_env(char *name, char *value) {
|
static void trim(char *value) {
|
||||||
|
// Remove Trailing Colon
|
||||||
int length = strlen(value);
|
int length = strlen(value);
|
||||||
if (value[length - 1] == ':') {
|
if (value[length - 1] == ':') {
|
||||||
value[length - 1] = '\0';
|
value[length - 1] = '\0';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_and_print_env(char *name, char *value) {
|
||||||
|
// Set Variable With Not Trailing Colon
|
||||||
|
trim(value);
|
||||||
|
|
||||||
fprintf(stderr, "Set %s = %s\n", name, value);
|
fprintf(stderr, "Set %s = %s\n", name, value);
|
||||||
setenv(name, value, 1);
|
setenv(name, value, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *get_env_safe(const char *name) {
|
static char *get_env_safe(const char *name) {
|
||||||
|
// Get Variable Or Blank String If Not Set
|
||||||
char *ret = getenv(name);
|
char *ret = getenv(name);
|
||||||
return ret != NULL ? ret : "";
|
return ret != NULL ? ret : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
static void load(char **ld_preload, char *folder) {
|
static void load(char **ld_path, char **ld_preload, char *folder) {
|
||||||
int folder_name_length = strlen(folder);
|
int folder_name_length = strlen(folder);
|
||||||
while (1) {
|
while (1) {
|
||||||
DIR *dp = opendir(folder);
|
DIR *dp = opendir(folder);
|
||||||
@ -44,7 +51,8 @@ static void load(char **ld_preload, char *folder) {
|
|||||||
while (1) {
|
while (1) {
|
||||||
entry = readdir(dp);
|
entry = readdir(dp);
|
||||||
if (entry != NULL) {
|
if (entry != NULL) {
|
||||||
if (starts_with(entry->d_name, "lib") && ends_with(entry->d_name, ".so")) {
|
// Check If File Is A Shared Library
|
||||||
|
if (entry->d_type == DT_REG && starts_with(entry->d_name, "lib") && ends_with(entry->d_name, ".so")) {
|
||||||
int name_length = strlen(entry->d_name);
|
int name_length = strlen(entry->d_name);
|
||||||
int total_length = folder_name_length + name_length;
|
int total_length = folder_name_length + name_length;
|
||||||
char name[total_length + 1];
|
char name[total_length + 1];
|
||||||
@ -58,9 +66,11 @@ static void load(char **ld_preload, char *folder) {
|
|||||||
|
|
||||||
name[total_length] = '\0';
|
name[total_length] = '\0';
|
||||||
|
|
||||||
|
// Add To LD_PRELOAD
|
||||||
asprintf(ld_preload, "%s:%s", name, *ld_preload);
|
asprintf(ld_preload, "%s:%s", name, *ld_preload);
|
||||||
}
|
}
|
||||||
} else if (errno != 0) {
|
} else if (errno != 0) {
|
||||||
|
// Error Reading Contents Of Folder
|
||||||
fprintf(stderr, "Error Reading Directory: %s\n", strerror(errno));
|
fprintf(stderr, "Error Reading Directory: %s\n", strerror(errno));
|
||||||
exit(1);
|
exit(1);
|
||||||
} else {
|
} else {
|
||||||
@ -68,8 +78,13 @@ static void load(char **ld_preload, char *folder) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
closedir(dp);
|
closedir(dp);
|
||||||
|
|
||||||
|
// Add To LD_LIBRARY_PATH
|
||||||
|
asprintf(ld_path, "%s:%s", *ld_path, folder);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} else if (errno == ENOENT) {
|
} else if (errno == ENOENT) {
|
||||||
|
// Folder Doesn't Exists, Attempt Creation
|
||||||
char *cmd = NULL;
|
char *cmd = NULL;
|
||||||
asprintf(&cmd, "mkdir -p %s", folder);
|
asprintf(&cmd, "mkdir -p %s", folder);
|
||||||
int ret = system(cmd);
|
int ret = system(cmd);
|
||||||
@ -77,6 +92,7 @@ static void load(char **ld_preload, char *folder) {
|
|||||||
exit(ret);
|
exit(ret);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Unable To Open Folder
|
||||||
fprintf(stderr, "Error Opening Directory: %s\n", strerror(errno));
|
fprintf(stderr, "Error Opening Directory: %s\n", strerror(errno));
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@ -88,26 +104,36 @@ int main(__attribute__((unused)) int argc, char *argv[]) {
|
|||||||
|
|
||||||
char *ld_path = NULL;
|
char *ld_path = NULL;
|
||||||
|
|
||||||
|
// Start Configuring LD_LIBRARY_PATH
|
||||||
char *cwd = getcwd(NULL, 0);
|
char *cwd = getcwd(NULL, 0);
|
||||||
asprintf(&ld_path, "%s:/usr/arm-linux-gnueabihf/lib:%s", cwd, get_env_safe("LD_LIBRARY_PATH"));
|
asprintf(&ld_path, "%s:/usr/arm-linux-gnueabihf/lib", cwd);
|
||||||
free(cwd);
|
free(cwd);
|
||||||
|
|
||||||
set_and_print_env("LD_LIBRARY_PATH", ld_path);
|
// Start Configuring LD_PRELOAD
|
||||||
free(ld_path);
|
|
||||||
|
|
||||||
char *ld_preload = NULL;
|
char *ld_preload = NULL;
|
||||||
asprintf(&ld_preload, "%s", get_env_safe("LD_PRELOAD"));
|
asprintf(&ld_preload, "%s", get_env_safe("LD_PRELOAD"));
|
||||||
|
|
||||||
load(&ld_preload, "./mods/");
|
// Load Mods From ./mods
|
||||||
|
load(&ld_path, &ld_preload, "./mods/");
|
||||||
|
|
||||||
|
// Loads Mods From ~/.minecraft/mods
|
||||||
char *home_mods = NULL;
|
char *home_mods = NULL;
|
||||||
asprintf(&home_mods, "%s/.minecraft/mods/", getenv("HOME"));
|
asprintf(&home_mods, "%s/.minecraft/mods/", getenv("HOME"));
|
||||||
load(&ld_preload, home_mods);
|
load(&ld_path, &ld_preload, home_mods);
|
||||||
free(home_mods);
|
free(home_mods);
|
||||||
|
|
||||||
|
// Add Existing LD_LIBRARY_PATH
|
||||||
|
asprintf(&ld_path, "%s:%s", ld_path, get_env_safe("LD_LIBRARY_PATH"));
|
||||||
|
|
||||||
|
// Set LD_LIBRARY_PATH
|
||||||
|
set_and_print_env("LD_LIBRARY_PATH", ld_path);
|
||||||
|
free(ld_path);
|
||||||
|
|
||||||
|
// Set LD_PRELOAD
|
||||||
set_and_print_env("LD_PRELOAD", ld_preload);
|
set_and_print_env("LD_PRELOAD", ld_preload);
|
||||||
free(ld_preload);
|
free(ld_preload);
|
||||||
|
|
||||||
|
// Start Game
|
||||||
fprintf(stderr, "Starting Game...\n");
|
fprintf(stderr, "Starting Game...\n");
|
||||||
return execve("./minecraft-pi", argv, environ);
|
return execve("./minecraft-pi", argv, environ);
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,11 @@ project(mods)
|
|||||||
add_compile_options(-Wall -Wextra -Werror)
|
add_compile_options(-Wall -Wextra -Werror)
|
||||||
|
|
||||||
add_subdirectory(../core core)
|
add_subdirectory(../core core)
|
||||||
include_directories(../core/include)
|
|
||||||
|
include_directories(include)
|
||||||
|
|
||||||
|
add_library(core SHARED src/core.c)
|
||||||
|
target_link_libraries(core dl)
|
||||||
|
|
||||||
add_library(compat SHARED src/compat.c)
|
add_library(compat SHARED src/compat.c)
|
||||||
target_link_libraries(compat core SDL EGL GLESv1_CM GLESv2 X11 dl)
|
target_link_libraries(compat core SDL EGL GLESv1_CM GLESv2 X11 dl)
|
||||||
|
@ -195,6 +195,7 @@ __attribute__((constructor)) static void init() {
|
|||||||
|
|
||||||
// Change Username
|
// Change Username
|
||||||
const char *username = get_username();
|
const char *username = get_username();
|
||||||
|
fprintf(stderr, "Setting Username: %s\n", username);
|
||||||
patch_address((void *) 0x18fd4, (void *) username);
|
patch_address((void *) 0x18fd4, (void *) username);
|
||||||
|
|
||||||
if (has_feature("Disable Autojump By Default")) {
|
if (has_feature("Disable Autojump By Default")) {
|
||||||
|
@ -7,7 +7,16 @@ chmod -R g-s debian
|
|||||||
|
|
||||||
# Clean out Directory
|
# Clean out Directory
|
||||||
rm -rf out
|
rm -rf out
|
||||||
mkdir out
|
mkdir -p out/deb
|
||||||
|
|
||||||
# Generate DEB
|
# Generate DEB
|
||||||
dpkg -b debian out
|
dpkg -b debian out/deb
|
||||||
|
|
||||||
|
# Export Libraries
|
||||||
|
mkdir -p out/lib
|
||||||
|
|
||||||
|
# Copy Headers
|
||||||
|
cp -r mods/include out/lib/include
|
||||||
|
|
||||||
|
# Copy Shared Library
|
||||||
|
docker run -v "$(pwd)/out/lib:/out" --entrypoint sh thebrokenrail/minecraft-pi -c 'cp ./mods/lib*.so /out'
|
||||||
|
Loading…
Reference in New Issue
Block a user