parent
b1d81e860f
commit
2b0d1d55ff
@ -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
|
||||
```
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
@ -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.
|
@ -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);
|
||||
}
|
Loading…
Reference in new issue