minecraft-pi-reborn/mods/src/multiplayer/multiplayer.cpp

134 lines
4.0 KiB
C++

#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 <symbols/minecraft.h>
#include "../home/home.h"
#include "../init/init.h"
#include "../feature/feature.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\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;
// Add Default Port If Needed
size_t last_colon = line.find_last_of(':');
if (last_colon == std::string::npos) {
line.append(":19132");
last_colon = line.find_last_of(':');
}
// Loop
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);
}
}
// Ping External Servers
static void RakNetInstance_pingForHosts_injection(unsigned char *rak_net_instance, int32_t base_port) {
// Call Original Method
(*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
if (feature_has("External Server Support", server_disabled)) {
patch_address(RakNetInstance_pingForHosts_vtable_addr, (void *) RakNetInstance_pingForHosts_injection);
}
}