Add Dedicated Server

This commit is contained in:
TheBrokenRail 2020-10-10 19:02:13 -04:00
parent b753b8da92
commit 84bfd9506c
25 changed files with 537 additions and 63 deletions

View File

@ -1,4 +1,4 @@
FROM arm64v8/debian:bullseye FROM debian:bullseye
RUN dpkg --add-architecture armhf RUN dpkg --add-architecture armhf

7
Dockerfile.server Normal file
View File

@ -0,0 +1,7 @@
FROM thebrokenrail/minecraft-pi:client
ENV MCPI_SERVER=1
RUN apt-get install -y xvfb
ENTRYPOINT xvfb-run ./launcher

7
debian/client/DEBIAN/postinst vendored Executable file
View File

@ -0,0 +1,7 @@
#!/bin/sh
set -e
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
MCPI_FEATURES='' MCPI_USERNAME='' docker-compose -f /usr/share/minecraft-pi/client/docker-compose.yml pull

View File

@ -26,10 +26,9 @@ fi
xhost local:root xhost local:root
# Launch Minecraft # Launch Minecraft
DOCKER_COMPOSE="docker-compose -f /usr/share/minecraft-pi/docker-compose.yml" DOCKER_COMPOSE="docker-compose -f /usr/share/minecraft-pi/client/docker-compose.yml"
${DOCKER_COMPOSE} pull | zenity --class 'Minecraft - Pi edition' --progress --pulsate --no-cancel --auto-close --text 'Updating Minecraft...' (${DOCKER_COMPOSE} pull || :) | zenity --class 'Minecraft - Pi edition' --progress --pulsate --no-cancel --auto-close --text 'Updating Minecraft...'
${DOCKER_COMPOSE} up ${DOCKER_COMPOSE} run --rm minecraft-pi || :
${DOCKER_COMPOSE} down
# Kill VirGL # Kill VirGL
kill "${VIRGL_PID}" kill "${VIRGL_PID}"

View File

@ -1,7 +1,7 @@
version: '3.7' version: '3.7'
services: services:
minecraft-pi: minecraft-pi:
image: thebrokenrail/minecraft-pi image: 'thebrokenrail/minecraft-pi:client'
network_mode: 'host' network_mode: 'host'
volumes: volumes:
- '/tmp/.X11-unix:/tmp/.X11-unix' - '/tmp/.X11-unix:/tmp/.X11-unix'

View File

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 100 KiB

7
debian/server/DEBIAN/control vendored Normal file
View File

@ -0,0 +1,7 @@
Package: minecraft-pi-server
Version: 1.0.0
Maintainer: TheBrokenRail <connor24nolan@live.com>
Description: Fun with Blocks
Homepage: https://www.minecraft.net/en-us/edition/pi
Architecture: amd64
Depends: docker.io, docker-compose

View File

@ -4,4 +4,4 @@ set -e
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
MCPI_FEATURES='' docker-compose -f /usr/share/minecraft-pi/docker-compose.yml pull MCPI_ROOT='' docker-compose -f /usr/share/minecraft-pi/server/docker-compose.yml pull

10
debian/server/usr/bin/minecraft-pi-server vendored Executable file
View File

@ -0,0 +1,10 @@
#!/bin/sh
set -e
export MCPI_ROOT="${PWD}"
# Launch Minecraft
DOCKER_COMPOSE="docker-compose -f /usr/share/minecraft-pi/server/docker-compose.yml"
${DOCKER_COMPOSE} pull || :
${DOCKER_COMPOSE} run --rm minecraft-pi

View File

@ -0,0 +1,7 @@
version: '3.7'
services:
minecraft-pi:
image: 'thebrokenrail/minecraft-pi:server'
network_mode: 'host'
volumes:
- '${MCPI_ROOT}:/root/.minecraft'

View File

@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.1.0)
project(mods) project(mods)
add_compile_options(-Wall -Wextra -Werror) add_compile_options(-Wall -Wextra -Werror)
add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
add_subdirectory(../core core) add_subdirectory(../core core)
@ -11,8 +12,11 @@ include_directories(include)
add_library(core SHARED src/core.c) add_library(core SHARED src/core.c)
target_link_libraries(core dl) target_link_libraries(core dl)
add_library(extra SHARED src/extra.c src/extra.cpp) add_library(server SHARED src/server/server.cpp src/server/server_properties.cpp)
target_link_libraries(extra core dl) target_link_libraries(server core dl SDL)
add_library(extra SHARED src/extra.c src/extra.cpp src/cxx11_util.cpp)
target_link_libraries(extra core dl server)
find_package(glfw3 3.3 REQUIRED) find_package(glfw3 3.3 REQUIRED)

View File

@ -28,6 +28,8 @@ static Window x11_window;
static Window x11_root_window; static Window x11_root_window;
static int window_loaded = 0; static int window_loaded = 0;
static int is_server = 0;
// Get Reference To X Window // Get Reference To X Window
static void store_x11_window() { static void store_x11_window() {
x11_display = glfwGetX11Display(); x11_display = glfwGetX11Display();
@ -184,10 +186,17 @@ HOOK(SDL_WM_SetCaption, void, (const char *title, __attribute__((unused)) const
exit(1); exit(1);
} }
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); if (is_server) {
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 1); // Don't Show Window In Server Mode
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
} else {
// Create OpenGL ES 1.1 Context
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 1);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
}
glfw_window = glfwCreateWindow(840, 480, title, NULL, NULL); glfw_window = glfwCreateWindow(840, 480, title, NULL, NULL);
if (!glfw_window) { if (!glfw_window) {
@ -195,19 +204,27 @@ HOOK(SDL_WM_SetCaption, void, (const char *title, __attribute__((unused)) const
exit(1); exit(1);
} }
glfwSetKeyCallback(glfw_window, glfw_key); if (!is_server) {
glfwSetCharCallback(glfw_window, glfw_char); // Don't Process Events In Server Mode
glfwSetCursorPosCallback(glfw_window, glfw_motion); glfwSetKeyCallback(glfw_window, glfw_key);
glfwSetMouseButtonCallback(glfw_window, glfw_click); glfwSetCharCallback(glfw_window, glfw_char);
glfwSetScrollCallback(glfw_window, glfw_scroll); glfwSetCursorPosCallback(glfw_window, glfw_motion);
glfwSetMouseButtonCallback(glfw_window, glfw_click);
glfwSetScrollCallback(glfw_window, glfw_scroll);
}
store_x11_window(); store_x11_window();
glfwMakeContextCurrent(glfw_window); if (!is_server) {
glfwMakeContextCurrent(glfw_window);
}
} }
HOOK(eglSwapBuffers, EGLBoolean, (__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLSurface surface)) { HOOK(eglSwapBuffers, EGLBoolean, (__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLSurface surface)) {
glfwSwapBuffers(glfw_window); if (!is_server) {
// Don't Swap Buffers In A Context-Less Window
glfwSwapBuffers(glfw_window);
}
return EGL_TRUE; return EGL_TRUE;
} }
@ -345,28 +362,42 @@ HOOK(SDL_Quit, void, ()) {
glfwTerminate(); glfwTerminate();
} }
static SDL_GrabMode fake_grab_mode = SDL_GRAB_OFF;
// Fix SDL Cursor Visibility/Grabbing // Fix SDL Cursor Visibility/Grabbing
HOOK(SDL_WM_GrabInput, SDL_GrabMode, (SDL_GrabMode mode)) { HOOK(SDL_WM_GrabInput, SDL_GrabMode, (SDL_GrabMode mode)) {
if (mode != SDL_GRAB_QUERY && mode != SDL_WM_GrabInput(SDL_GRAB_QUERY)) { if (is_server) {
glfwSetInputMode(glfw_window, GLFW_CURSOR, mode == SDL_GRAB_OFF ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_DISABLED); // Don't Grab Input In Server/Headless Mode
glfwSetInputMode(glfw_window, GLFW_RAW_MOUSE_MOTION, mode == SDL_GRAB_OFF ? GLFW_FALSE : GLFW_TRUE); if (mode != SDL_GRAB_QUERY) {
fake_grab_mode = mode;
// GLFW Cursor Hiding is Broken
if (window_loaded) {
if (mode == SDL_GRAB_OFF) {
XFixesShowCursor(x11_display, x11_window);
} else {
XFixesHideCursor(x11_display, x11_window);
}
XFlush(x11_display);
} }
return fake_grab_mode;
} else {
if (mode != SDL_GRAB_QUERY && mode != SDL_WM_GrabInput(SDL_GRAB_QUERY)) {
glfwSetInputMode(glfw_window, GLFW_CURSOR, mode == SDL_GRAB_OFF ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_DISABLED);
glfwSetInputMode(glfw_window, GLFW_RAW_MOUSE_MOTION, mode == SDL_GRAB_OFF ? GLFW_FALSE : GLFW_TRUE);
// GLFW Cursor Hiding is Broken
if (window_loaded) {
if (mode == SDL_GRAB_OFF) {
XFixesShowCursor(x11_display, x11_window);
} else {
XFixesHideCursor(x11_display, x11_window);
}
XFlush(x11_display);
}
}
return mode == SDL_GRAB_QUERY ? (glfwGetInputMode(glfw_window, GLFW_CURSOR) == GLFW_CURSOR_NORMAL ? SDL_GRAB_OFF : SDL_GRAB_ON) : mode;
} }
return mode == SDL_GRAB_QUERY ? (glfwGetInputMode(glfw_window, GLFW_CURSOR) == GLFW_CURSOR_NORMAL ? SDL_GRAB_OFF : SDL_GRAB_ON) : mode;
} }
// Stub SDL Cursor Visibility // Stub SDL Cursor Visibility
HOOK(SDL_ShowCursor, int, (int toggle)) { HOOK(SDL_ShowCursor, int, (int toggle)) {
return toggle == SDL_QUERY ? (glfwGetInputMode(glfw_window, GLFW_CURSOR) == GLFW_CURSOR_NORMAL ? SDL_ENABLE : SDL_DISABLE) : toggle; if (is_server) {
return toggle == SDL_QUERY ? (fake_grab_mode == SDL_GRAB_OFF ? SDL_ENABLE : SDL_DISABLE) : toggle;
} else {
return toggle == SDL_QUERY ? (glfwGetInputMode(glfw_window, GLFW_CURSOR) == GLFW_CURSOR_NORMAL ? SDL_ENABLE : SDL_DISABLE) : toggle;
}
} }
// SDL Stub // SDL Stub
@ -430,6 +461,9 @@ HOOK(eglTerminate, EGLBoolean, (__attribute__((unused)) EGLDisplay display)) {
// Use VirGL // Use VirGL
__attribute__((constructor)) static void init() { __attribute__((constructor)) static void init() {
is_server = get_is_server();
setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1); setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1);
setenv("GALLIUM_DRIVER", "virpipe", 1); if (!is_server) {
setenv("GALLIUM_DRIVER", "virpipe", 1);
}
} }

18
mods/src/cxx11_util.cpp Normal file
View File

@ -0,0 +1,18 @@
// Use C++11 ABI
#undef _GLIBCXX_USE_CXX11_ABI
#define _GLIBCXX_USE_CXX11_ABI 1
#include <string>
#include "cxx11_util.h"
// Convert A C-String into A C++11 String That Can be Acessed In C++03 Code
cxx11_string create_cxx11_string(const char *str) {
std::string *new_str = new std::string(str);
int32_t new_size = sizeof (cxx11_string);
int32_t old_size = sizeof *new_str;
if (new_size != old_size) {
fprintf(stderr, "Mismatched String Size: Expected: %i Real: %i\n", new_size, old_size);
}
return *reinterpret_cast<cxx11_string *>(new_str);
}

21
mods/src/cxx11_util.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef CXX_11_UTIL_H
#define CXX_11_UTIL
#ifdef __cplusplus
extern "C" {
#endif
#define CXX11_STRING_SIZE 24
struct cxx11_string {
unsigned char data[CXX11_STRING_SIZE];
};
cxx11_string create_cxx11_string(const char *str);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -5,6 +5,7 @@
#include <libcore/libcore.h> #include <libcore/libcore.h>
#include "extra.h" #include "extra.h"
#include "server/server.h"
static uint32_t getSpawnMobs_injection(__attribute__((unused)) int32_t obj) { static uint32_t getSpawnMobs_injection(__attribute__((unused)) int32_t obj) {
return 1; return 1;
@ -66,23 +67,6 @@ static void handleClick_injection(unsigned char *this, unsigned char *param_2, u
} }
} }
int has_feature(const char *name) {
char *env = getenv("MCPI_FEATURES");
char *features = strdup(env != NULL ? env : "");
char *tok = strtok(features, "|");
int ret = 0;
while (tok != NULL) {
if (strcmp(tok, name) == 0) {
ret = 1;
break;
}
tok = strtok(NULL, "|");
}
free(features);
fprintf(stderr, "Feature: %s: %s\n", name, ret ? "Enabled" : "Disabled");
return ret;
}
// Patch Game Mode // Patch Game Mode
static void set_is_survival(int new_is_survival) { static void set_is_survival(int new_is_survival) {
if (is_survival != new_is_survival) { if (is_survival != new_is_survival) {
@ -137,19 +121,62 @@ static void minecraft_init_injection(unsigned char *this) {
*(this + 83) = 1; *(this + 83) = 1;
} }
// Is Dedicated Server
static int is_server = 0;
// Check For Feature
int has_feature(const char *name) {
if (is_server) {
// Enable All Features In Server
return 1;
} else {
char *env = getenv("MCPI_FEATURES");
char *features = strdup(env != NULL ? env : "");
char *tok = strtok(features, "|");
int ret = 0;
while (tok != NULL) {
if (strcmp(tok, name) == 0) {
ret = 1;
break;
}
tok = strtok(NULL, "|");
}
free(features);
fprintf(stderr, "Feature: %s: %s\n", name, ret ? "Enabled" : "Disabled");
return ret;
}
}
int get_is_server() {
return getenv("MCPI_SERVER") != NULL;
}
__attribute__((constructor)) static void init() { __attribute__((constructor)) static void init() {
is_server = get_is_server();
if (is_server) {
server_init();
}
if (has_feature("Touch GUI")) { if (has_feature("Touch GUI")) {
// Use Touch UI // Use Touch UI
unsigned char touch_gui_patch[4] = {0x01, 0x00, 0x50, 0xe3}; unsigned char touch_gui_patch[4] = {0x01, 0x00, 0x50, 0xe3};
patch((void *) 0x292fc, touch_gui_patch); patch((void *) 0x292fc, touch_gui_patch);
} }
// Get Default Game Mode
int default_game_mode;
if (is_server) {
default_game_mode = server_get_default_game_mode();
} else {
default_game_mode = !has_feature("Survival Mode");
}
// Dyanmic Game Mode Switching // Dyanmic Game Mode Switching
set_is_survival(0); set_is_survival(!default_game_mode);
setIsCreativeMode_original = overwrite((void *) setIsCreativeMode, setIsCreativeMode_injection); setIsCreativeMode_original = overwrite((void *) setIsCreativeMode, setIsCreativeMode_injection);
// Set Default Game Mode // Set Default Game Mode
unsigned char default_game_mode_patch[4] = {has_feature("Survival Mode") ? 0x00 : 0x01, 0x30, 0xa0, 0xe3}; unsigned char default_game_mode_patch[4] = {default_game_mode ? 0x01 : 0x00, 0x30, 0xa0, 0xe3};
patch((void *) 0xba744, default_game_mode_patch); patch((void *) 0xba744, default_game_mode_patch);
// Disable Item Dropping When Cursor Is Hidden // Disable Item Dropping When Cursor Is Hidden
@ -170,7 +197,13 @@ __attribute__((constructor)) static void init() {
patch((void *) 0x15b0c, instamine_patch); patch((void *) 0x15b0c, instamine_patch);
} }
if (has_feature("Mob Spawning")) { int mob_spawning;
if (is_server) {
mob_spawning = server_get_mob_spawning();
} else {
mob_spawning = has_feature("Mob Spawning");
}
if (mob_spawning) {
// Enable Mob Spawning // Enable Mob Spawning
overwrite((void *) 0xbabec, getSpawnMobs_injection); overwrite((void *) 0xbabec, getSpawnMobs_injection);
} }
@ -193,8 +226,14 @@ __attribute__((constructor)) static void init() {
patch((void *) 0x6dc70, patch_data_9); patch((void *) 0x6dc70, patch_data_9);
// Change Username // Change Username
const char *username = get_username(); const char *username;
fprintf(stderr, "Setting Username: %s\n", username); if (is_server) {
// MOTD is Username
username = server_get_motd();
} else {
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")) {

View File

@ -8,14 +8,17 @@
#include <libcore/libcore.h> #include <libcore/libcore.h>
#include "extra.h" #include "extra.h"
#include "cxx11_util.h"
#include <cstdio>
extern "C" { extern "C" {
static std::string readAssetFile(__attribute__((unused)) unsigned char *obj, const std::string& path) { static cxx11_string readAssetFile(__attribute__((unused)) unsigned char *obj, std::string const& path) {
std::string full_path("./data/"); std::string full_path("./data/");
full_path.append(path.c_str()); full_path.append(path);
std::ifstream stream(full_path); std::ifstream stream(full_path);
std::string str((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>()); std::string str((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());
return str; return create_cxx11_string(str.c_str());
} }
typedef unsigned char *(*TextEditScreen_t)(unsigned char *, unsigned char *); typedef unsigned char *(*TextEditScreen_t)(unsigned char *, unsigned char *);

View File

@ -7,6 +7,7 @@ extern "C" {
#endif #endif
int has_feature(const char *name); int has_feature(const char *name);
int get_is_server();
void key_press(char key); void key_press(char key);
void clear_input(); void clear_input();

237
mods/src/server/server.cpp Normal file
View File

@ -0,0 +1,237 @@
#include <string>
#include <stdint.h>
#include <ctime>
#include <cstdio>
#include <csignal>
#include <fstream>
#include <unistd.h>
#include <SDL/SDL_events.h>
#include <libcore/libcore.h>
#include "server.h"
#include "server_properties.h"
typedef void (*Minecraft_update_t)(unsigned char *minecraft);
static Minecraft_update_t Minecraft_update = (Minecraft_update_t) 0x16b74;
static void *Minecraft_update_original = NULL;
struct LevelSettings {
unsigned long seed;
int32_t game_type;
};
typedef void (*Minecraft_selectLevel_t)(unsigned char *minecraft, std::string const& level_dir, std::string const& level_name, LevelSettings const& vsettings);
static Minecraft_selectLevel_t Minecraft_selectLevel = (Minecraft_selectLevel_t) 0x16f38;
typedef void (*Minecraft_hostMultiplayer_t)(unsigned char *minecraft, int32_t port);
static Minecraft_hostMultiplayer_t Minecraft_hostMultiplayer = (Minecraft_hostMultiplayer_t) 0x16664;
typedef void *(*ProgressScreen_t)(unsigned char *obj);
static ProgressScreen_t ProgressScreen = (ProgressScreen_t) 0x37044;
typedef void (*Minecraft_setScreen_t)(unsigned char *minecraft, unsigned char *screen);
static Minecraft_setScreen_t Minecraft_setScreen = (Minecraft_setScreen_t) 0x15d6c;
#define INFO(msg, ...) printf("[INFO]: " msg "\n", __VA_ARGS__);
// Store Minecraft For Exit
static unsigned char *stored_minecraft = NULL;
// Server Properties
static ServerProperties &get_server_properties() {
static ServerProperties properties;
return properties;
}
// Default Server Properties
#define DEFAULT_MOTD "Minecraft Server"
#define DEFAULT_GAME_MODE "0"
#define DEFAULT_PORT "19132"
#define DEFAULT_SEED ""
#define DEFAULT_MOB_SPAWNING "true"
#define DEFAULT_WORLD_NAME "world"
typedef const char *(*Minecraft_getProgressMessage_t)(unsigned char *minecraft);
static Minecraft_getProgressMessage_t Minecraft_getProgressMessage = (Minecraft_getProgressMessage_t) 0x16e58;
typedef int32_t (*Minecraft_isLevelGenerated_t)(unsigned char *minecraft);
static Minecraft_isLevelGenerated_t Minecraft_isLevelGenerated = (Minecraft_isLevelGenerated_t) 0x16e6c;
#define SIGNIFICANT_PROGRESS 5
// Check If Two Percentages Are Different Enough To Be Logged
static bool is_progress_difference_significant(int32_t new_val, int32_t old_val) {
if (new_val != old_val) {
if (new_val == -1 || old_val == -1) {
return true;
} else if (new_val == 0 || new_val == 100) {
return true;
} else {
return new_val - old_val >= SIGNIFICANT_PROGRESS;
}
} else {
return false;
}
}
// Runs Every Tick
static int last_progress = -1;
static const char *last_message = NULL;
static bool loaded = false;
static void Minecraft_update_injection(unsigned char *minecraft) {
// Create/Start World
if (!loaded) {
INFO("%s", "Starting Minecraft: Pi Edition Dedicated Server");
LevelSettings settings;
settings.game_type = 0; // Patched By MCPI-Docker
std::string seed_str = get_server_properties().get_string("seed", DEFAULT_SEED);
int32_t seed = seed_str.length() > 0 ? std::stoi(seed_str) : time(NULL);
settings.seed = seed;
std::string world_name = get_server_properties().get_string("world-name", DEFAULT_WORLD_NAME);
(*Minecraft_selectLevel)(minecraft, world_name, world_name, settings);
int port = get_server_properties().get_int("port", DEFAULT_PORT);
(*Minecraft_hostMultiplayer)(minecraft, port);
INFO("Listening On: %i", port);
void *screen = ::operator new(0x4c);
screen = (*ProgressScreen)((unsigned char *) screen);
(*Minecraft_setScreen)(minecraft, (unsigned char *) screen);
stored_minecraft = minecraft;
loaded = true;
}
// Print Progress Message
const char *message = (*Minecraft_getProgressMessage)(minecraft);
int32_t progress = *(int32_t *) (minecraft + 0xc60);
if ((*Minecraft_isLevelGenerated)(minecraft)) {
message = "Ready";
progress = -1;
}
if (message != NULL) {
bool message_different = message != last_message;
bool progress_significant = is_progress_difference_significant(progress, last_progress);
if (message_different || progress_significant) {
if (progress != -1) {
INFO("Status: %s: %i%%", message, progress);
} else {
INFO("Status: %s", message);
}
if (message_different) {
last_message = message;
}
if (progress_significant) {
last_progress = progress;
}
}
}
// Call Original Method
revert_overwrite((void *) Minecraft_update, Minecraft_update_original);
(*Minecraft_update)(minecraft);
revert_overwrite((void *) Minecraft_update, Minecraft_update_original);
}
typedef void (*Level_saveLevelData_t)(unsigned char *level);
static Level_saveLevelData_t Level_saveLevelData = (Level_saveLevelData_t) 0xa2e94;
static void *Level_saveLevelData_original = NULL;
static void Level_saveLevelData_injection(unsigned char *level) {
// Print Log Message
INFO("%s", "Saving Game");
// Call Original Method
revert_overwrite((void *) Level_saveLevelData, Level_saveLevelData_original);
(*Level_saveLevelData)(level);
revert_overwrite((void *) Level_saveLevelData, Level_saveLevelData_original);
}
typedef void (*Gui_addMessage_t)(unsigned char *gui, std::string const& text);
static Gui_addMessage_t Gui_addMessage = (Gui_addMessage_t) 0x27820;
static void *Gui_addMessage_original = NULL;
static void Gui_addMessage_injection(unsigned char *gui, std::string const& text) {
// Print Log Message
printf("[CHAT]: %s\n", text.c_str());
// Call Original Method
revert_overwrite((void *) Gui_addMessage, Gui_addMessage_original);
(*Gui_addMessage)(gui, text);
revert_overwrite((void *) Gui_addMessage, Gui_addMessage_original);
}
static void exit_handler(__attribute__((unused)) int data) {
INFO("%s", "Stopping Server");
if (stored_minecraft != NULL) {
unsigned char *level = *(unsigned char **) (stored_minecraft + 0x188);
if (level != NULL) {
// Save Game
(*Level_saveLevelData)(level);
}
}
// Stop Game
SDL_Event event;
event.type = SDL_QUIT;
SDL_PushEvent(&event);
}
const char *server_get_motd() {
return get_server_properties().get_string("motd", DEFAULT_MOTD).c_str();
}
int server_get_default_game_mode() {
return get_server_properties().get_int("game-mode", DEFAULT_GAME_MODE);
}
int server_get_mob_spawning() {
return get_server_properties().get_bool("spawn-mobs", DEFAULT_MOB_SPAWNING);
}
void server_init() {
// Open Properties File
std::string file(getenv("HOME"));
file.append("/.minecraft/server.properties");
std::ifstream properties_file(file);
if (!properties_file || !properties_file.is_open()) {
// Write Defaults
std::ofstream properties_file_output(file);
properties_file_output << "motd=" DEFAULT_MOTD "\n";
properties_file_output << "game-mode=" DEFAULT_GAME_MODE "\n";
properties_file_output << "port=" DEFAULT_PORT "\n";
properties_file_output << "seed=" DEFAULT_SEED "\n";
properties_file_output << "spawn-mobs=" DEFAULT_MOB_SPAWNING "\n";
properties_file_output << "world-name=" DEFAULT_WORLD_NAME "\n";
properties_file_output.close();
// Re-Open File
properties_file = std::ifstream(file);
}
if (!properties_file.is_open()) {
printf("[ERR]: Unable To Open server.properties\n");
exit(1);
}
// Load Properties
get_server_properties().load(properties_file);
properties_file.close();
// Prevent Main Player From Loading
unsigned char player_patch[4] = {0x00, 0x20, 0xa0, 0xe3};
patch((void *) 0x1685c, player_patch);
// Start World On Launch
Minecraft_update_original = overwrite((void *) Minecraft_update, (void *) Minecraft_update_injection);
// Print Log On Game Save
Level_saveLevelData_original = overwrite((void *) Level_saveLevelData, (void *) Level_saveLevelData_injection);
// Exit handler
signal(SIGINT, exit_handler);
// Print Chat To Log
Gui_addMessage_original = overwrite((void *) Gui_addMessage, (void *) Gui_addMessage_injection);
}

19
mods/src/server/server.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef SERVER_H
#define SERVER_H
#ifdef __cplusplus
extern "C" {
#endif
void server_init();
const char *server_get_motd();
int server_get_default_game_mode();
int server_get_mob_spawning();
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,37 @@
#include "server_properties.h"
static bool is_true(std::string const& val) {
return (val == "true" || val == "yes" || val == "1");
}
void ServerProperties::load(std::istream& stream) {
std::string line;
while (std::getline(stream, line)) {
if (line.length() > 0) {
if (line[0] == '#') {
continue;
}
size_t i = line.find('=');
if (i == std::string::npos) {
continue;
}
properties.insert(std::pair<std::string, std::string>(line.substr(0, i), line.substr(i + 1)));
}
}
}
std::string ServerProperties::get_string(std::string const& name, std::string const& def) {
return properties.count(name) > 0 ? properties.at(name) : def;
}
int ServerProperties::get_int(std::string const& name, std::string const& def) {
return properties.count(name) > 0 ? std::stoi(properties.at(name)) : std::stoi(def);
}
bool ServerProperties::get_bool(std::string const& name, std::string const& def) {
if (properties.count(name) > 0) {
std::string const& val = properties.at(name);
return is_true(val);
}
return is_true(def);
}

View File

@ -0,0 +1,22 @@
#ifndef SERVER_PROPERTIES_H
#define SERVER_PROPERTIES_H
#include <string>
#include <istream>
#include <map>
class ServerProperties {
private:
std::map<std::string, std::string> properties;
public:
void load(std::istream& fstream);
std::string get_string(std::string const& name, std::string const& def);
int get_int(std::string const& name, std::string const& def);
bool get_bool(std::string const& name, std::string const& def);
};
#endif

View File

@ -2,4 +2,5 @@
set -e set -e
docker build ${DOCKER_BUILD_OPTIONS} --tag thebrokenrail/minecraft-pi:latest . docker build ${DOCKER_BUILD_OPTIONS} --tag thebrokenrail/minecraft-pi:client -f Dockerfile.client .
docker build ${DOCKER_BUILD_OPTIONS} --tag thebrokenrail/minecraft-pi:server -f Dockerfile.server .

View File

@ -10,7 +10,8 @@ rm -rf out
mkdir -p out/deb mkdir -p out/deb
# Generate DEB # Generate DEB
dpkg -b debian out/deb dpkg -b debian/client out/deb
dpkg -b debian/server out/deb
# Export Libraries # Export Libraries
mkdir -p out/lib mkdir -p out/lib
@ -19,6 +20,6 @@ mkdir -p out/lib
cp -r mods/include out/lib/include cp -r mods/include out/lib/include
# Copy Shared Library # Copy Shared Library
IMG_ID="$(docker create thebrokenrail/minecraft-pi)" IMG_ID="$(docker create thebrokenrail/minecraft-pi:client)"
docker cp "${IMG_ID}":/app/minecraft-pi/mods/. ./out/lib/. docker cp "${IMG_ID}":/app/minecraft-pi/mods/. ./out/lib/.
docker rm -v "${IMG_ID}" docker rm -v "${IMG_ID}"