Compare commits
2 Commits
b1d81e860f
...
053bf4aa5f
Author | SHA1 | Date | |
---|---|---|---|
053bf4aa5f | |||
2b0d1d55ff |
@ -94,6 +94,10 @@ if(MCPI_SERVER_MODE)
|
||||
add_definitions(-DMCPI_SERVER_MODE)
|
||||
endif()
|
||||
|
||||
# Version
|
||||
file(STRINGS VERSION VERSION)
|
||||
add_definitions(-DVERSION="${VERSION}")
|
||||
|
||||
# Build libreborn
|
||||
add_subdirectory(libreborn)
|
||||
|
||||
|
@ -72,7 +72,7 @@ This sub-component implements stubs for various redundant libraries used by MCPI
|
||||
This is always compiled for ARM.
|
||||
|
||||
##### What To Stub And What To Patch?
|
||||
Most libraries (like ``bcm_host``) can just be replaced with stubs, because they don't need to do anything and aren't used by anything else. However, some libraries (like EGL) might be used by some of MCPI-Reborn's dependencies (like GLFW) so instead of being replaced by a stub, each call is manually patched out from MCPI. A stub is still generated just in case that library isn't present on the system to silence linker errors, but it is only loaded if no other version is available.
|
||||
Most libraries (like ``bcm_host``) can just be replaced with stubs, because they don't need to do anything and aren't used by anything else. However, some libraries (like EGL and X11) might be used by some of MCPI-Reborn's dependencies (like GLFW) so instead of being replaced by a stub, each call is manually patched out from MCPI. A stub is still generated just in case that library isn't present on the system to silence linker errors, but it is only loaded if no other version is available.
|
||||
|
||||
#### Headers
|
||||
This sub-component includes headers for SDL, GLES, and EGL allowing easy (cross-)compilation.
|
||||
|
@ -1,5 +1,9 @@
|
||||
# Changelog
|
||||
|
||||
**2.0.5**
|
||||
* Show Reborn Version In Start Screen
|
||||
* External Server Support
|
||||
|
||||
**2.0.4**
|
||||
* Optimize Media Layer Proxy
|
||||
|
||||
|
14
docs/MULTIPLAYER.md
Normal file
14
docs/MULTIPLAYER.md
Normal file
@ -0,0 +1,14 @@
|
||||
# Multiplayer
|
||||
MCPI-Reborn supports two ways to play multiplayer.
|
||||
|
||||
## Local Network (LAN)
|
||||
This is also supported by vanilla MCPI. Just load a world in MCPI and other devices on the network can join.
|
||||
|
||||
## External Servers
|
||||
Unlike vanilla MCPI, MCPI-Reborn allows you to natively join a server outside of the local network. Just modify ``~/.minecraft-pi/servers.txt`` and it should show up in MCPI's server list.
|
||||
|
||||
### Example ``~/.minecraft-pi/servers.txt``
|
||||
```
|
||||
# Comment
|
||||
example.com:19132
|
||||
```
|
@ -7,4 +7,5 @@
|
||||
* [View Building](BUILDING.md)
|
||||
* [View Architecture](ARCHITECTURE.md)
|
||||
* [View Command Line Arguments](COMMAND_LINE.md)
|
||||
* [View Multiplayer](MULTIPLAYER.md)
|
||||
* [View Changelog](CHANGELOG.md)
|
||||
|
@ -3,7 +3,7 @@ Name=Minecraft: Pi Edition: Reborn
|
||||
Comment=Fun with Blocks
|
||||
Icon=/usr/share/pixmaps/minecraft-pi-reborn-client.png
|
||||
StartupNotify=false
|
||||
StartupWMClass=Minecraft - Pi edition
|
||||
StartupWMClass=Minecraft: Pi Edition: Reborn
|
||||
Exec=/usr/bin/minecraft-pi-reborn-client
|
||||
Terminal=false
|
||||
Type=Application
|
||||
|
@ -106,7 +106,7 @@ static void run_zenity_and_set_env(const char *env_name, std::vector<std::string
|
||||
std::vector<std::string> full_command;
|
||||
full_command.push_back("zenity");
|
||||
full_command.push_back("--class");
|
||||
full_command.push_back("Minecraft - Pi edition");
|
||||
full_command.push_back("Minecraft: Pi Edition: Reborn");
|
||||
full_command.insert(full_command.end(), command.begin(), command.end());
|
||||
// Convert To C Array
|
||||
const char *full_command_array[full_command.size() + 1];
|
||||
|
@ -17,6 +17,7 @@ static renderCursor_t renderCursor = (renderCursor_t) 0x480c4;
|
||||
|
||||
static char **default_path = (char **) 0xe264; // /.minecraft/
|
||||
static char **default_username = (char **) 0x18fd4; // StevePi
|
||||
static char **minecraft_pi_version = (char **) 0x39d94; // v0.1.1 alpha
|
||||
|
||||
static unsigned char **Item_flintAndSteel = (unsigned char **) 0x17ba70;
|
||||
static unsigned char **Item_snowball = (unsigned char **) 0x17bbb0;
|
||||
@ -345,6 +346,11 @@ static uint32_t RakNetInstance_send_vtable_offset = 0x38;
|
||||
typedef uint32_t (*RakNetInstance_isServer_t)(unsigned char *rak_net_instance);
|
||||
static uint32_t RakNetInstance_isServer_vtable_offset = 0x48;
|
||||
|
||||
typedef void (*RakNetInstance_pingForHosts_t)(unsigned char *rak_net_instance, int32_t base_port);
|
||||
static RakNetInstance_pingForHosts_t RakNetInstance_pingForHosts = (RakNetInstance_pingForHosts_t) 0x73538;
|
||||
static uint32_t RakNetInstance_pingForHosts_vtable_offset = 0x14;
|
||||
static void *RakNetInstance_pingForHosts_vtable_addr = (void *) 0x109afc;
|
||||
|
||||
static uint32_t RakNetInstance_peer_property_offset = 0x4; // RakNet::RakPeer *
|
||||
|
||||
// RakNet::RakPeer
|
||||
@ -355,6 +361,9 @@ static uint32_t RakNet_RakPeer_GetSystemAddressFromGuid_vtable_offset = 0xd0;
|
||||
typedef bool (*RakNet_RakPeer_IsBanned_t)(unsigned char *rak_peer, const char *ip);
|
||||
static RakNet_RakPeer_IsBanned_t RakNet_RakPeer_IsBanned = (RakNet_RakPeer_IsBanned_t) 0xda3b4;
|
||||
|
||||
typedef bool (*RakNet_RakPeer_Ping_t)(unsigned char *rak_peer, const char *host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections, uint32_t connectionSocketIndex);
|
||||
static RakNet_RakPeer_Ping_t RakNet_RakPeer_Ping = (RakNet_RakPeer_Ping_t) 0xd9c2c;
|
||||
|
||||
// RakNet::SystemAddress
|
||||
|
||||
typedef char *(*RakNet_SystemAddress_ToString_t)(struct RakNet_SystemAddress *system_address, bool print_delimiter, char delimiter);
|
||||
@ -469,6 +478,11 @@ static SelectWorldScreen_getUniqueLevelName_t SelectWorldScreen_getUniqueLevelNa
|
||||
|
||||
static SelectWorldScreen_getUniqueLevelName_t Touch_SelectWorldScreen_getUniqueLevelName = (SelectWorldScreen_getUniqueLevelName_t) 0x3d82c;
|
||||
|
||||
// Common
|
||||
|
||||
typedef std::string (*Common_getGameVersionString_t)(std::string const& version_suffix);
|
||||
static Common_getGameVersionString_t Common_getGameVersionString = (Common_getGameVersionString_t) 0x15068;
|
||||
|
||||
#endif
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
@ -30,11 +30,5 @@ add_subdirectory(extras)
|
||||
|
||||
# Add Symlinks So MCPI Can Locate Libraries
|
||||
if(BUILD_ARM_COMPONENTS)
|
||||
if(MCPI_SERVER_MODE OR MCPI_USE_MEDIA_LAYER_PROXY)
|
||||
install_symlink("libmedia-layer-core.so" "${MCPI_LIB_DIR}/libX11.so.6")
|
||||
else()
|
||||
# When Loading In Client Mode On An ARM Host, Use Native X11 By Default
|
||||
install_symlink("../lib/libmedia-layer-core.so" "${MCPI_FALLBACK_LIB_DIR}/libX11.so.6")
|
||||
endif()
|
||||
install_symlink("libmedia-layer-core.so" "${MCPI_LIB_DIR}/libSDL-1.2.so.0")
|
||||
endif()
|
||||
|
@ -2,6 +2,5 @@ project(media-layer-extras)
|
||||
|
||||
if(BUILD_ARM_COMPONENTS)
|
||||
# Add Source To Media Core
|
||||
target_sources(media-layer-core PRIVATE src/SDL.c src/X11.cpp)
|
||||
target_link_libraries(media-layer-core dl)
|
||||
target_sources(media-layer-core PRIVATE src/SDL.c)
|
||||
endif()
|
||||
|
@ -1,76 +0,0 @@
|
||||
#include <elf.h>
|
||||
#include <vector>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
#include <libreborn/media-layer/core.h>
|
||||
|
||||
#ifndef MEDIA_LAYER_PROXY_SERVER
|
||||
// Store Adresses That Are Part Of MCPI
|
||||
struct text_section_data {
|
||||
void *section;
|
||||
Elf32_Word size;
|
||||
};
|
||||
static std::vector<text_section_data> &get_text_sections() {
|
||||
static std::vector<text_section_data> sections;
|
||||
return sections;
|
||||
}
|
||||
static void text_section_callback(void *section, Elf32_Word size, __attribute__((unused)) void *data) {
|
||||
text_section_data section_data;
|
||||
section_data.section = section;
|
||||
section_data.size = size;
|
||||
get_text_sections().push_back(section_data);
|
||||
}
|
||||
// Check If The Current Return Address Is Part Of MCPI Or An External Library
|
||||
static inline int _is_returning_to_external_library(void *return_addr) {
|
||||
// Load Text Sections If Not Loaded
|
||||
if (get_text_sections().size() < 1) {
|
||||
iterate_text_sections(text_section_callback, NULL);
|
||||
}
|
||||
// Iterate Text Sections
|
||||
for (std::vector<text_section_data>::size_type i = 0; i < get_text_sections().size(); i++) {
|
||||
text_section_data section_data = get_text_sections()[i];
|
||||
// Check Text Section
|
||||
if (return_addr >= section_data.section && return_addr < (((unsigned char *) section_data.section) + section_data.size)) {
|
||||
// Part Of MCPI
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// Part Of An External Library
|
||||
return 1;
|
||||
}
|
||||
#define is_returning_to_external_library() _is_returning_to_external_library(__builtin_extract_return_addr(__builtin_return_address(0)))
|
||||
#else
|
||||
// When Using The Media Layer Proxy, None Of These Functions Are Ever Called By An External Library
|
||||
#define is_returning_to_external_library() 0
|
||||
#endif
|
||||
|
||||
// Don't Directly Use XLib Unless Returning To An External library
|
||||
HOOK(XTranslateCoordinates, int, (void *display, XID src_w, XID dest_w, int src_x, int src_y, int *dest_x_return, int *dest_y_return, XID *child_return)) {
|
||||
if (is_returning_to_external_library()) {
|
||||
// Use Real Function
|
||||
ensure_XTranslateCoordinates();
|
||||
return (*real_XTranslateCoordinates)(display, src_w, dest_w, src_x, src_y, dest_x_return, dest_y_return, child_return);
|
||||
} else {
|
||||
// Use MCPI Replacemnt Function
|
||||
*dest_x_return = src_x;
|
||||
*dest_y_return = src_y;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
HOOK(XGetWindowAttributes, int, (void *display, XID w, XWindowAttributes *window_attributes_return)) {
|
||||
if (is_returning_to_external_library()) {
|
||||
// Use Real Function
|
||||
ensure_XGetWindowAttributes();
|
||||
return (*real_XGetWindowAttributes)(display, w, window_attributes_return);
|
||||
} else {
|
||||
// Use MCPI Replacemnt Function
|
||||
XWindowAttributes attributes;
|
||||
attributes.x = 0;
|
||||
attributes.y = 0;
|
||||
media_get_framebuffer_size(&attributes.width, &attributes.height);
|
||||
*window_attributes_return = attributes;
|
||||
return 1;
|
||||
}
|
||||
}
|
@ -5,15 +5,15 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#define GL_FALSE 0
|
||||
#define GL_FOG_COLOR 0x0B66
|
||||
#define GL_FOG_COLOR 0xb66
|
||||
#define GL_ARRAY_BUFFER_BINDING 0x8894
|
||||
#define GL_UNSIGNED_BYTE 0x1401
|
||||
#define GL_RGB 0x1907
|
||||
#define GL_RGBA 0x1908
|
||||
#define GL_MODELVIEW_MATRIX 0x0BA6
|
||||
#define GL_PROJECTION_MATRIX 0x0BA7
|
||||
#define GL_VIEWPORT 0x0BA2
|
||||
#define GL_DEPTH_TEST 0x0B71
|
||||
#define GL_MODELVIEW_MATRIX 0xba6
|
||||
#define GL_PROJECTION_MATRIX 0xba7
|
||||
#define GL_VIEWPORT 0xba2
|
||||
#define GL_DEPTH_TEST 0xb71
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
@ -14,11 +14,15 @@ if(BUILD_ARM_COMPONENTS)
|
||||
# Stub EGL
|
||||
add_library(EGL SHARED src/EGL.c)
|
||||
target_link_libraries(EGL reborn-headers media-layer-headers)
|
||||
# Stub X11
|
||||
add_library(X11 SHARED src/X11.c)
|
||||
target_link_libraries(X11 reborn-headers media-layer-headers)
|
||||
set_target_properties(X11 PROPERTIES SOVERSION "6")
|
||||
# Install
|
||||
if(MCPI_SERVER_MODE OR MCPI_USE_MEDIA_LAYER_PROXY)
|
||||
install(TARGETS EGL DESTINATION "${MCPI_LIB_DIR}")
|
||||
install(TARGETS EGL X11 DESTINATION "${MCPI_LIB_DIR}")
|
||||
else()
|
||||
install(TARGETS EGL DESTINATION "${MCPI_FALLBACK_LIB_DIR}") # Place At The End Of LD_LIBRARY_PATH
|
||||
install(TARGETS EGL X11 DESTINATION "${MCPI_FALLBACK_LIB_DIR}") # Place At The End Of LD_LIBRARY_PATH
|
||||
endif()
|
||||
|
||||
# Install GLESv1_CM Stubs In Server Mode
|
||||
|
@ -1,15 +1,3 @@
|
||||
/*
|
||||
* This is only loaded when no other EGL library is present on the system to silence linker errors.
|
||||
*
|
||||
* All EGL calls are manually patched out in mods/src/compat/egl.c.
|
||||
* Unlike with bcm_host, EGL can't just be always stubbed out with LD_PRELOAD, because in some situations GLFW has it as a dependency.
|
||||
*
|
||||
* So normally, MCPI just loads the real EGL and never uses it (because MCPI's EGL calls were patched out).
|
||||
* However, since the real EGL is still loaded, GLFW can use it if it needs to.
|
||||
*
|
||||
* This stub library is just in case the system has no EGL library present.
|
||||
*/
|
||||
|
||||
#include <EGL/egl.h>
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
|
14
media-layer/stubs/src/X11.c
Normal file
14
media-layer/stubs/src/X11.c
Normal file
@ -0,0 +1,14 @@
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
#define IMPOSSIBLE() ERR("(%s:%i) This Should Never Be Called", __FILE__, __LINE__)
|
||||
|
||||
// Raw X11 Is Replaced With GLFW
|
||||
|
||||
int XTranslateCoordinates(__attribute__((unused)) void *display, __attribute__((unused)) XID src_w, __attribute__((unused)) XID dest_w, __attribute__((unused)) int src_x, __attribute__((unused)) int src_y, __attribute__((unused)) int *dest_x_return, __attribute__((unused)) int *dest_y_return, __attribute__((unused)) XID *child_return) {
|
||||
IMPOSSIBLE();
|
||||
}
|
||||
int XGetWindowAttributes(__attribute__((unused)) void *display, __attribute__((unused)) XID w, __attribute__((unused)) XWindowAttributes *window_attributes_return) {
|
||||
IMPOSSIBLE();
|
||||
}
|
@ -7,7 +7,7 @@ add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
|
||||
|
||||
## Mods
|
||||
|
||||
add_library(compat SHARED src/compat/compat.c src/compat/egl.c)
|
||||
add_library(compat SHARED src/compat/compat.c src/compat/egl.c src/compat/x11.c)
|
||||
target_link_libraries(compat feature input media-layer-core dl)
|
||||
|
||||
add_library(readdir SHARED src/readdir/readdir.c)
|
||||
@ -15,9 +15,15 @@ add_library(readdir SHARED src/readdir/readdir.c)
|
||||
add_library(feature SHARED src/feature/feature.c)
|
||||
target_link_libraries(feature reborn)
|
||||
|
||||
add_library(version SHARED src/version/version.cpp)
|
||||
target_link_libraries(version reborn)
|
||||
|
||||
if(MCPI_SERVER_MODE)
|
||||
add_library(server SHARED src/server/server.cpp src/server/server_properties.cpp)
|
||||
target_link_libraries(server reborn feature home compat dl media-layer-core pthread)
|
||||
target_link_libraries(server reborn feature home compat version dl media-layer-core pthread)
|
||||
else()
|
||||
add_library(multiplayer SHARED src/multiplayer/multiplayer.cpp)
|
||||
target_link_libraries(multiplayer reborn home)
|
||||
endif()
|
||||
|
||||
add_library(camera SHARED src/camera/camera.cpp)
|
||||
@ -57,13 +63,17 @@ add_library(test SHARED src/test/test.c)
|
||||
target_link_libraries(test reborn home)
|
||||
|
||||
add_library(init SHARED src/init/init.c)
|
||||
target_link_libraries(init compat game_mode camera input misc death options touch textures chat home test)
|
||||
target_link_libraries(init compat game_mode camera input misc death options touch textures chat home version test)
|
||||
if(MCPI_SERVER_MODE)
|
||||
target_link_libraries(init server)
|
||||
else()
|
||||
target_link_libraries(init multiplayer)
|
||||
endif()
|
||||
|
||||
## Install Mods
|
||||
install(TARGETS init compat readdir feature override game_mode camera input misc death options touch textures chat home test DESTINATION "${MCPI_INSTALL_DIR}/mods")
|
||||
install(TARGETS init compat readdir feature override game_mode camera input misc death options touch textures chat home version test DESTINATION "${MCPI_INSTALL_DIR}/mods")
|
||||
if(MCPI_SERVER_MODE)
|
||||
install(TARGETS server DESTINATION "${MCPI_INSTALL_DIR}/mods")
|
||||
else()
|
||||
install(TARGETS multiplayer DESTINATION "${MCPI_INSTALL_DIR}/mods")
|
||||
endif()
|
||||
|
@ -42,7 +42,7 @@ unsigned int chat_get_counter() {
|
||||
static void *chat_thread(__attribute__((unused)) void *nop) {
|
||||
// Open
|
||||
int return_code;
|
||||
char *output = run_command("zenity --title \"Chat\" --class \"Minecraft - Pi edition\" --entry --text \"Enter Chat Message:\"", &return_code);
|
||||
char *output = run_command("zenity --title 'Chat' --class 'Minecraft: Pi Edition: Reborn' --entry --text 'Enter Chat Message:'", &return_code);
|
||||
// Handle Message
|
||||
if (output != NULL) {
|
||||
// Check Return Code
|
||||
|
@ -1,2 +1,2 @@
|
||||
# ``compat`` Mod
|
||||
This utility mod sends keyboard input to other mods. It also patches out all EGL calls.
|
||||
This utility mod sends keyboard input to other mods. It also patches out all EGL and X11 calls.
|
||||
|
@ -12,6 +12,12 @@
|
||||
#include "../init/init.h"
|
||||
#include "compat.h"
|
||||
|
||||
// Custom Title
|
||||
HOOK(SDL_WM_SetCaption, void, (__attribute__((unused)) const char *title, const char *icon)) {
|
||||
ensure_SDL_WM_SetCaption();
|
||||
(*real_SDL_WM_SetCaption)("Minecraft: Pi Edition: Reborn", icon);
|
||||
}
|
||||
|
||||
// Mouse Cursor Is Always Invisible In Vanilla MCPI
|
||||
// Because In Vanilla MCPI, The GPU Overlay Covered The Normal Mouse Cursor
|
||||
HOOK(SDL_ShowCursor, int, (int toggle)) {
|
||||
|
28
mods/src/compat/x11.c
Normal file
28
mods/src/compat/x11.c
Normal file
@ -0,0 +1,28 @@
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
#include <libreborn/media-layer/core.h>
|
||||
|
||||
// Functions That Have Their Return Values Used
|
||||
static int XTranslateCoordinates_injection(__attribute__((unused)) void *display, __attribute__((unused)) XID src_w, __attribute__((unused)) XID dest_w, int src_x, int src_y, int *dest_x_return, int *dest_y_return, __attribute__((unused)) XID *child_return) {
|
||||
// Use MCPI Replacemnt Function
|
||||
*dest_x_return = src_x;
|
||||
*dest_y_return = src_y;
|
||||
return 1;
|
||||
}
|
||||
static int XGetWindowAttributes_injection(__attribute__((unused)) void *display, __attribute__((unused)) XID w, XWindowAttributes *window_attributes_return) {
|
||||
// Use MCPI Replacemnt Function
|
||||
XWindowAttributes attributes;
|
||||
attributes.x = 0;
|
||||
attributes.y = 0;
|
||||
media_get_framebuffer_size(&attributes.width, &attributes.height);
|
||||
*window_attributes_return = attributes;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Patch X11 Calls
|
||||
__attribute__((constructor)) static void patch_x11_calls() {
|
||||
// Disable X11 Calls
|
||||
overwrite_call((void *) 0x132a4, (void *) XGetWindowAttributes_injection); // XGetWindowAttributes
|
||||
overwrite_call((void *) 0x132d4, (void *) XTranslateCoordinates_injection); // XTranslateCoordinates
|
||||
}
|
@ -12,14 +12,13 @@
|
||||
// Store Game Data In Launch Directory
|
||||
#define NEW_PATH ""
|
||||
|
||||
// Store Launch Directory
|
||||
static char *launch_directory = NULL;
|
||||
__attribute__((constructor)) static void init_launch_directory() {
|
||||
launch_directory = getcwd(NULL, 0);
|
||||
}
|
||||
char *home_get_launch_directory() {
|
||||
return launch_directory;
|
||||
}
|
||||
|
||||
// Pretend $HOME Is Launch Directory
|
||||
HOOK(getenv, char *, (const char *name)) {
|
||||
if (strcmp(name, "HOME") == 0) {
|
||||
return launch_directory;
|
||||
@ -32,10 +31,18 @@ HOOK(getenv, char *, (const char *name)) {
|
||||
|
||||
// Get MCPI Home Directory
|
||||
char *home_get() {
|
||||
char *dir = NULL;
|
||||
safe_asprintf(&dir, "%s/" NEW_PATH, getenv("HOME"));
|
||||
static char *dir = NULL;
|
||||
// Load
|
||||
if (dir == NULL) {
|
||||
safe_asprintf(&dir, "%s/" NEW_PATH, getenv("HOME"));
|
||||
}
|
||||
// Return
|
||||
return dir;
|
||||
}
|
||||
// Free
|
||||
__attribute__((destructor)) static void _free_home() {
|
||||
free(home_get());
|
||||
}
|
||||
|
||||
// Init
|
||||
void init_home() {
|
||||
|
@ -4,11 +4,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef MCPI_SERVER_MODE
|
||||
char *home_get_launch_directory();
|
||||
#endif
|
||||
|
||||
char *home_get(); // Remember To free()
|
||||
char *home_get();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ __attribute__((constructor)) static void init() {
|
||||
init_compat();
|
||||
#ifdef MCPI_SERVER_MODE
|
||||
init_server();
|
||||
#else
|
||||
init_multiplayer();
|
||||
#endif
|
||||
init_game_mode();
|
||||
init_input();
|
||||
@ -16,4 +18,5 @@ __attribute__((constructor)) static void init() {
|
||||
init_textures();
|
||||
init_chat();
|
||||
init_home();
|
||||
init_version();
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ void run_tests();
|
||||
void init_compat();
|
||||
#ifdef MCPI_SERVER_MODE
|
||||
void init_server();
|
||||
#else
|
||||
void init_multiplayer();
|
||||
#endif
|
||||
void init_game_mode();
|
||||
void init_input();
|
||||
@ -19,6 +21,7 @@ void init_touch();
|
||||
void init_textures();
|
||||
void init_chat();
|
||||
void init_home();
|
||||
void init_version();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
4
mods/src/multiplayer/README.md
Normal file
4
mods/src/multiplayer/README.md
Normal file
@ -0,0 +1,4 @@
|
||||
# ``multiplayer`` Mod
|
||||
This mod implements connecting to external (non-LAN) servers. This mod is client mode only.
|
||||
|
||||
It is controlled by the ``~/.minecraft-pi/servers.txt`` file.
|
126
mods/src/multiplayer/multiplayer.cpp
Normal file
126
mods/src/multiplayer/multiplayer.cpp
Normal file
@ -0,0 +1,126 @@
|
||||
#ifdef MCPI_SERVER_MODE
|
||||
#error "External Server Code Requires Client Mode"
|
||||
#endif
|
||||
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
#include <libreborn/minecraft.h>
|
||||
|
||||
#include "../home/home.h"
|
||||
#include "../init/init.h"
|
||||
|
||||
// Load Server List
|
||||
struct server_list_entry {
|
||||
std::string address;
|
||||
int port;
|
||||
};
|
||||
static std::vector<struct server_list_entry> server_list_entries;
|
||||
static bool server_list_loaded = false;
|
||||
static void load_servers() {
|
||||
// Prepare
|
||||
server_list_entries.clear();
|
||||
|
||||
// Open Servers File
|
||||
std::string file(home_get());
|
||||
file.append("/servers.txt");
|
||||
|
||||
// Create Stream
|
||||
std::ifstream server_list_file(file);
|
||||
|
||||
if (!server_list_file.good()) {
|
||||
// Write Defaults
|
||||
std::ofstream server_list_file_output(file);
|
||||
server_list_file_output << "# External Servers File\n";
|
||||
server_list_file_output << "thebrokenrail.com:19132\n";
|
||||
server_list_file_output.close();
|
||||
// Re-Open Stream
|
||||
server_list_file = std::ifstream(file);
|
||||
}
|
||||
|
||||
// Check Servers File
|
||||
if (!server_list_file.is_open()) {
|
||||
ERR("Unable To Open %s", file.c_str());
|
||||
}
|
||||
|
||||
// Iterate Lines
|
||||
{
|
||||
std::string line;
|
||||
while (std::getline(server_list_file, line)) {
|
||||
// Check Line
|
||||
if (line.length() > 0) {
|
||||
if (line[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
// Parse
|
||||
std::string address;
|
||||
std::string port_str;
|
||||
// Loop
|
||||
size_t last_colon = line.find_last_of(':');
|
||||
if (last_colon != std::string::npos) {
|
||||
for (std::string::size_type i = 0; i < line.length(); i++) {
|
||||
if (i > last_colon) {
|
||||
port_str.push_back(line[i]);
|
||||
} else if (i < last_colon) {
|
||||
address.push_back(line[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check Line
|
||||
if (address.length() < 1 || port_str.length() < 1 || port_str.find_first_not_of("0123456789") != std::string::npos) {
|
||||
// Invalid Line
|
||||
WARN("Invalid Server: %s", line.c_str());
|
||||
continue;
|
||||
}
|
||||
// Parse Port
|
||||
int port = std::stoi(port_str);
|
||||
|
||||
// Done
|
||||
struct server_list_entry entry;
|
||||
entry.address = address;
|
||||
entry.port = port;
|
||||
server_list_entries.push_back(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close
|
||||
server_list_file.close();
|
||||
}
|
||||
|
||||
// Iterare Server List
|
||||
static void iterate_servers(std::function<void(const char *address, int port)> callback) {
|
||||
// Load
|
||||
if (!server_list_loaded) {
|
||||
load_servers();
|
||||
server_list_loaded = true;
|
||||
}
|
||||
|
||||
// Loop
|
||||
for (std::vector<struct server_list_entry>::size_type i = 0; i < server_list_entries.size(); i++) {
|
||||
struct server_list_entry entry = server_list_entries[i];
|
||||
callback(entry.address.c_str(), entry.port);
|
||||
}
|
||||
}
|
||||
|
||||
static void RakNetInstance_pingForHosts_injection(unsigned char *rak_net_instance, int32_t base_port) {
|
||||
// Call Original
|
||||
(*RakNetInstance_pingForHosts)(rak_net_instance, base_port);
|
||||
|
||||
// Get RakNet::RakPeer
|
||||
unsigned char *rak_peer = *(unsigned char **) (rak_net_instance + RakNetInstance_peer_property_offset);
|
||||
|
||||
// Add External Servers
|
||||
iterate_servers([rak_peer](const char *address, int port) {
|
||||
(*RakNet_RakPeer_Ping)(rak_peer, address, port, 1, 0);
|
||||
});
|
||||
}
|
||||
|
||||
// Init
|
||||
void init_multiplayer() {
|
||||
// Inject Code
|
||||
patch_address(RakNetInstance_pingForHosts_vtable_addr, (void *) RakNetInstance_pingForHosts_injection);
|
||||
}
|
@ -20,8 +20,6 @@ static char *get_override_path(const char *filename) {
|
||||
// Get Asset Override Path
|
||||
char *overrides = NULL;
|
||||
safe_asprintf(&overrides, "%s/overrides", home_path);
|
||||
// Free Home Path
|
||||
free(home_path);
|
||||
// Get Data Path
|
||||
char *data = NULL;
|
||||
char *cwd = getcwd(NULL, 0);
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "../init/init.h"
|
||||
#include "../home/home.h"
|
||||
#include "../compat/compat.h"
|
||||
#include "../version/version.h"
|
||||
|
||||
#include <libreborn/minecraft.h>
|
||||
|
||||
@ -65,7 +66,7 @@ static std::string get_world_name() {
|
||||
|
||||
// Create/Start World
|
||||
static void start_world(unsigned char *minecraft) {
|
||||
INFO("%s", "Starting Minecraft: Pi Edition Dedicated Server");
|
||||
INFO("Starting Minecraft: Pi Edition: Dedicated Server (%s)", version_get());
|
||||
|
||||
// Specify Level Settings
|
||||
LevelSettings settings;
|
||||
@ -144,7 +145,7 @@ static bool is_whitelist() {
|
||||
}
|
||||
// Get Path Of Blacklist (Or Whitelist) File
|
||||
static std::string get_blacklist_file() {
|
||||
std::string file(home_get_launch_directory());
|
||||
std::string file(home_get());
|
||||
file.append(is_whitelist() ? "/whitelist.txt" : "/blacklist.txt");
|
||||
return file;
|
||||
}
|
||||
@ -457,12 +458,12 @@ static unsigned char get_max_players() {
|
||||
|
||||
static void server_init() {
|
||||
// Open Properties File
|
||||
std::string file(home_get_launch_directory());
|
||||
std::string file(home_get());
|
||||
file.append("/server.properties");
|
||||
|
||||
std::ifstream properties_file(file);
|
||||
|
||||
if (!properties_file || !properties_file.good()) {
|
||||
if (!properties_file.good()) {
|
||||
// Write Defaults
|
||||
std::ofstream properties_file_output(file);
|
||||
properties_file_output << "# Message Of The Day\n";
|
||||
@ -490,9 +491,9 @@ static void server_init() {
|
||||
properties_file = std::ifstream(file);
|
||||
}
|
||||
|
||||
// Open Properties File
|
||||
// Check Properties File
|
||||
if (!properties_file.is_open()) {
|
||||
ERR("%s", "Unable To Open server.properties");
|
||||
ERR("Unable To Open %s", file.c_str());
|
||||
}
|
||||
// Load Properties
|
||||
get_server_properties().load(properties_file);
|
||||
@ -502,7 +503,7 @@ static void server_init() {
|
||||
// Create Empty Blacklist/Whitelist File
|
||||
std::string blacklist_file_path = get_blacklist_file();
|
||||
std::ifstream blacklist_file(blacklist_file_path);
|
||||
if (!blacklist_file || !blacklist_file.good()) {
|
||||
if (!blacklist_file.good()) {
|
||||
// Write Default
|
||||
std::ofstream blacklist_output(blacklist_file_path);
|
||||
blacklist_output << "# Blacklist/Whitelist; Each Line Is One IP Address\n";
|
||||
|
@ -13,7 +13,6 @@ void run_tests() {
|
||||
char *path = home_get();
|
||||
int exists = access(path, F_OK) == 0;
|
||||
int can_write = exists ? access(path, R_OK | W_OK) == 0 : 1;
|
||||
free(path);
|
||||
|
||||
if (!can_write) {
|
||||
// Failure
|
||||
|
2
mods/src/version/README.md
Normal file
2
mods/src/version/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
# ``version`` Mod
|
||||
This mod adds the MCPI-Reborn version to the start screen.
|
34
mods/src/version/version.cpp
Normal file
34
mods/src/version/version.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
#include <libreborn/libreborn.h>
|
||||
#include <libreborn/minecraft.h>
|
||||
|
||||
#include "version.h"
|
||||
#include "../init/init.h"
|
||||
|
||||
// Get New Version
|
||||
char *version_get() {
|
||||
static char *version = NULL;
|
||||
// Load
|
||||
if (version == NULL) {
|
||||
safe_asprintf(&version, "%s / Reborn v" VERSION, *minecraft_pi_version);
|
||||
}
|
||||
// Return
|
||||
return version;
|
||||
}
|
||||
// Free
|
||||
__attribute__((destructor)) static void _free_version() {
|
||||
free(version_get());
|
||||
}
|
||||
|
||||
// Injection For Touch GUI Version
|
||||
static std::string Common_getGameVersionString_injection(__attribute__((unused)) std::string const& version_suffix) {
|
||||
// Set Version
|
||||
return version_get();
|
||||
}
|
||||
|
||||
// Init
|
||||
void init_version() {
|
||||
// Touch GUI
|
||||
overwrite((void *) Common_getGameVersionString, (void *) Common_getGameVersionString_injection);
|
||||
// Normal GUI
|
||||
patch_address((void *) minecraft_pi_version, version_get());
|
||||
}
|
11
mods/src/version/version.h
Normal file
11
mods/src/version/version.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
char *version_get();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user