External Server Support
This commit is contained in:
parent
b1d81e860f
commit
2b0d1d55ff
@ -72,7 +72,7 @@ This sub-component implements stubs for various redundant libraries used by MCPI
|
|||||||
This is always compiled for ARM.
|
This is always compiled for ARM.
|
||||||
|
|
||||||
##### What To Stub And What To Patch?
|
##### 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
|
#### Headers
|
||||||
This sub-component includes headers for SDL, GLES, and EGL allowing easy (cross-)compilation.
|
This sub-component includes headers for SDL, GLES, and EGL allowing easy (cross-)compilation.
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
**2.0.5**
|
||||||
|
* External Server Support
|
||||||
|
|
||||||
**2.0.4**
|
**2.0.4**
|
||||||
* Optimize Media Layer Proxy
|
* 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 Building](BUILDING.md)
|
||||||
* [View Architecture](ARCHITECTURE.md)
|
* [View Architecture](ARCHITECTURE.md)
|
||||||
* [View Command Line Arguments](COMMAND_LINE.md)
|
* [View Command Line Arguments](COMMAND_LINE.md)
|
||||||
|
* [View Multiplayer](MULTIPLAYER.md)
|
||||||
* [View Changelog](CHANGELOG.md)
|
* [View Changelog](CHANGELOG.md)
|
||||||
|
@ -345,6 +345,11 @@ static uint32_t RakNetInstance_send_vtable_offset = 0x38;
|
|||||||
typedef uint32_t (*RakNetInstance_isServer_t)(unsigned char *rak_net_instance);
|
typedef uint32_t (*RakNetInstance_isServer_t)(unsigned char *rak_net_instance);
|
||||||
static uint32_t RakNetInstance_isServer_vtable_offset = 0x48;
|
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 *
|
static uint32_t RakNetInstance_peer_property_offset = 0x4; // RakNet::RakPeer *
|
||||||
|
|
||||||
// RakNet::RakPeer
|
// RakNet::RakPeer
|
||||||
@ -355,6 +360,9 @@ static uint32_t RakNet_RakPeer_GetSystemAddressFromGuid_vtable_offset = 0xd0;
|
|||||||
typedef bool (*RakNet_RakPeer_IsBanned_t)(unsigned char *rak_peer, const char *ip);
|
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;
|
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
|
// RakNet::SystemAddress
|
||||||
|
|
||||||
typedef char *(*RakNet_SystemAddress_ToString_t)(struct RakNet_SystemAddress *system_address, bool print_delimiter, char delimiter);
|
typedef char *(*RakNet_SystemAddress_ToString_t)(struct RakNet_SystemAddress *system_address, bool print_delimiter, char delimiter);
|
||||||
|
@ -30,11 +30,5 @@ add_subdirectory(extras)
|
|||||||
|
|
||||||
# Add Symlinks So MCPI Can Locate Libraries
|
# Add Symlinks So MCPI Can Locate Libraries
|
||||||
if(BUILD_ARM_COMPONENTS)
|
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")
|
install_symlink("libmedia-layer-core.so" "${MCPI_LIB_DIR}/libSDL-1.2.so.0")
|
||||||
endif()
|
endif()
|
||||||
|
@ -2,6 +2,5 @@ project(media-layer-extras)
|
|||||||
|
|
||||||
if(BUILD_ARM_COMPONENTS)
|
if(BUILD_ARM_COMPONENTS)
|
||||||
# Add Source To Media Core
|
# Add Source To Media Core
|
||||||
target_sources(media-layer-core PRIVATE src/SDL.c src/X11.cpp)
|
target_sources(media-layer-core PRIVATE src/SDL.c)
|
||||||
target_link_libraries(media-layer-core dl)
|
|
||||||
endif()
|
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
|
#endif
|
||||||
|
|
||||||
#define GL_FALSE 0
|
#define GL_FALSE 0
|
||||||
#define GL_FOG_COLOR 0x0B66
|
#define GL_FOG_COLOR 0xb66
|
||||||
#define GL_ARRAY_BUFFER_BINDING 0x8894
|
#define GL_ARRAY_BUFFER_BINDING 0x8894
|
||||||
#define GL_UNSIGNED_BYTE 0x1401
|
#define GL_UNSIGNED_BYTE 0x1401
|
||||||
#define GL_RGB 0x1907
|
#define GL_RGB 0x1907
|
||||||
#define GL_RGBA 0x1908
|
#define GL_RGBA 0x1908
|
||||||
#define GL_MODELVIEW_MATRIX 0x0BA6
|
#define GL_MODELVIEW_MATRIX 0xba6
|
||||||
#define GL_PROJECTION_MATRIX 0x0BA7
|
#define GL_PROJECTION_MATRIX 0xba7
|
||||||
#define GL_VIEWPORT 0x0BA2
|
#define GL_VIEWPORT 0xba2
|
||||||
#define GL_DEPTH_TEST 0x0B71
|
#define GL_DEPTH_TEST 0xb71
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
@ -14,11 +14,15 @@ if(BUILD_ARM_COMPONENTS)
|
|||||||
# Stub EGL
|
# Stub EGL
|
||||||
add_library(EGL SHARED src/EGL.c)
|
add_library(EGL SHARED src/EGL.c)
|
||||||
target_link_libraries(EGL reborn-headers media-layer-headers)
|
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
|
# Install
|
||||||
if(MCPI_SERVER_MODE OR MCPI_USE_MEDIA_LAYER_PROXY)
|
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()
|
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()
|
endif()
|
||||||
|
|
||||||
# Install GLESv1_CM Stubs In Server Mode
|
# 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 <EGL/egl.h>
|
||||||
|
|
||||||
#include <libreborn/libreborn.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
|
## 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)
|
target_link_libraries(compat feature input media-layer-core dl)
|
||||||
|
|
||||||
add_library(readdir SHARED src/readdir/readdir.c)
|
add_library(readdir SHARED src/readdir/readdir.c)
|
||||||
@ -18,6 +18,9 @@ target_link_libraries(feature reborn)
|
|||||||
if(MCPI_SERVER_MODE)
|
if(MCPI_SERVER_MODE)
|
||||||
add_library(server SHARED src/server/server.cpp src/server/server_properties.cpp)
|
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 dl media-layer-core pthread)
|
||||||
|
else()
|
||||||
|
add_library(multiplayer SHARED src/multiplayer/multiplayer.cpp)
|
||||||
|
target_link_libraries(multiplayer reborn home)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(camera SHARED src/camera/camera.cpp)
|
add_library(camera SHARED src/camera/camera.cpp)
|
||||||
@ -60,10 +63,14 @@ 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 test)
|
||||||
if(MCPI_SERVER_MODE)
|
if(MCPI_SERVER_MODE)
|
||||||
target_link_libraries(init server)
|
target_link_libraries(init server)
|
||||||
|
else()
|
||||||
|
target_link_libraries(init multiplayer)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
## Install Mods
|
## 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 test DESTINATION "${MCPI_INSTALL_DIR}/mods")
|
||||||
if(MCPI_SERVER_MODE)
|
if(MCPI_SERVER_MODE)
|
||||||
install(TARGETS server DESTINATION "${MCPI_INSTALL_DIR}/mods")
|
install(TARGETS server DESTINATION "${MCPI_INSTALL_DIR}/mods")
|
||||||
|
else()
|
||||||
|
install(TARGETS multiplayer DESTINATION "${MCPI_INSTALL_DIR}/mods")
|
||||||
endif()
|
endif()
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
# ``compat`` Mod
|
# ``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.
|
||||||
|
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
|
// Store Game Data In Launch Directory
|
||||||
#define NEW_PATH ""
|
#define NEW_PATH ""
|
||||||
|
|
||||||
|
// Store Launch Directory
|
||||||
static char *launch_directory = NULL;
|
static char *launch_directory = NULL;
|
||||||
__attribute__((constructor)) static void init_launch_directory() {
|
__attribute__((constructor)) static void init_launch_directory() {
|
||||||
launch_directory = getcwd(NULL, 0);
|
launch_directory = getcwd(NULL, 0);
|
||||||
}
|
}
|
||||||
char *home_get_launch_directory() {
|
|
||||||
return launch_directory;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Pretend $HOME Is Launch Directory
|
||||||
HOOK(getenv, char *, (const char *name)) {
|
HOOK(getenv, char *, (const char *name)) {
|
||||||
if (strcmp(name, "HOME") == 0) {
|
if (strcmp(name, "HOME") == 0) {
|
||||||
return launch_directory;
|
return launch_directory;
|
||||||
|
@ -4,10 +4,6 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef MCPI_SERVER_MODE
|
|
||||||
char *home_get_launch_directory();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
char *home_get(); // Remember To free()
|
char *home_get(); // Remember To free()
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -5,6 +5,8 @@ __attribute__((constructor)) static void init() {
|
|||||||
init_compat();
|
init_compat();
|
||||||
#ifdef MCPI_SERVER_MODE
|
#ifdef MCPI_SERVER_MODE
|
||||||
init_server();
|
init_server();
|
||||||
|
#else
|
||||||
|
init_multiplayer();
|
||||||
#endif
|
#endif
|
||||||
init_game_mode();
|
init_game_mode();
|
||||||
init_input();
|
init_input();
|
||||||
|
@ -8,6 +8,8 @@ void run_tests();
|
|||||||
void init_compat();
|
void init_compat();
|
||||||
#ifdef MCPI_SERVER_MODE
|
#ifdef MCPI_SERVER_MODE
|
||||||
void init_server();
|
void init_server();
|
||||||
|
#else
|
||||||
|
void init_multiplayer();
|
||||||
#endif
|
#endif
|
||||||
void init_game_mode();
|
void init_game_mode();
|
||||||
void init_input();
|
void init_input();
|
||||||
|
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);
|
||||||
|
}
|
@ -144,7 +144,7 @@ static bool is_whitelist() {
|
|||||||
}
|
}
|
||||||
// Get Path Of Blacklist (Or Whitelist) File
|
// Get Path Of Blacklist (Or Whitelist) File
|
||||||
static std::string get_blacklist_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");
|
file.append(is_whitelist() ? "/whitelist.txt" : "/blacklist.txt");
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
@ -457,12 +457,12 @@ static unsigned char get_max_players() {
|
|||||||
|
|
||||||
static void server_init() {
|
static void server_init() {
|
||||||
// Open Properties File
|
// Open Properties File
|
||||||
std::string file(home_get_launch_directory());
|
std::string file(home_get());
|
||||||
file.append("/server.properties");
|
file.append("/server.properties");
|
||||||
|
|
||||||
std::ifstream properties_file(file);
|
std::ifstream properties_file(file);
|
||||||
|
|
||||||
if (!properties_file || !properties_file.good()) {
|
if (!properties_file.good()) {
|
||||||
// Write Defaults
|
// Write Defaults
|
||||||
std::ofstream properties_file_output(file);
|
std::ofstream properties_file_output(file);
|
||||||
properties_file_output << "# Message Of The Day\n";
|
properties_file_output << "# Message Of The Day\n";
|
||||||
@ -490,9 +490,9 @@ static void server_init() {
|
|||||||
properties_file = std::ifstream(file);
|
properties_file = std::ifstream(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open Properties File
|
// Check Properties File
|
||||||
if (!properties_file.is_open()) {
|
if (!properties_file.is_open()) {
|
||||||
ERR("%s", "Unable To Open server.properties");
|
ERR("Unable To Open %s", file.c_str());
|
||||||
}
|
}
|
||||||
// Load Properties
|
// Load Properties
|
||||||
get_server_properties().load(properties_file);
|
get_server_properties().load(properties_file);
|
||||||
@ -502,7 +502,7 @@ static void server_init() {
|
|||||||
// Create Empty Blacklist/Whitelist File
|
// Create Empty Blacklist/Whitelist File
|
||||||
std::string blacklist_file_path = get_blacklist_file();
|
std::string blacklist_file_path = get_blacklist_file();
|
||||||
std::ifstream blacklist_file(blacklist_file_path);
|
std::ifstream blacklist_file(blacklist_file_path);
|
||||||
if (!blacklist_file || !blacklist_file.good()) {
|
if (!blacklist_file.good()) {
|
||||||
// Write Default
|
// Write Default
|
||||||
std::ofstream blacklist_output(blacklist_file_path);
|
std::ofstream blacklist_output(blacklist_file_path);
|
||||||
blacklist_output << "# Blacklist/Whitelist; Each Line Is One IP Address\n";
|
blacklist_output << "# Blacklist/Whitelist; Each Line Is One IP Address\n";
|
||||||
|
Loading…
Reference in New Issue
Block a user