Add Dedicated Server
This commit is contained in:
parent
b753b8da92
commit
84bfd9506c
@ -1,4 +1,4 @@
|
||||
FROM arm64v8/debian:bullseye
|
||||
FROM debian:bullseye
|
||||
|
||||
RUN dpkg --add-architecture armhf
|
||||
|
7
Dockerfile.server
Normal file
7
Dockerfile.server
Normal 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
7
debian/client/DEBIAN/postinst
vendored
Executable 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
|
@ -26,10 +26,9 @@ fi
|
||||
xhost local:root
|
||||
|
||||
# Launch Minecraft
|
||||
DOCKER_COMPOSE="docker-compose -f /usr/share/minecraft-pi/docker-compose.yml"
|
||||
${DOCKER_COMPOSE} pull | zenity --class 'Minecraft - Pi edition' --progress --pulsate --no-cancel --auto-close --text 'Updating Minecraft...'
|
||||
${DOCKER_COMPOSE} up
|
||||
${DOCKER_COMPOSE} down
|
||||
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} run --rm minecraft-pi || :
|
||||
|
||||
# Kill VirGL
|
||||
kill "${VIRGL_PID}"
|
@ -1,7 +1,7 @@
|
||||
version: '3.7'
|
||||
services:
|
||||
minecraft-pi:
|
||||
image: thebrokenrail/minecraft-pi
|
||||
image: 'thebrokenrail/minecraft-pi:client'
|
||||
network_mode: 'host'
|
||||
volumes:
|
||||
- '/tmp/.X11-unix:/tmp/.X11-unix'
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
7
debian/server/DEBIAN/control
vendored
Normal file
7
debian/server/DEBIAN/control
vendored
Normal 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
|
@ -4,4 +4,4 @@ set -e
|
||||
|
||||
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
10
debian/server/usr/bin/minecraft-pi-server
vendored
Executable 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
|
7
debian/server/usr/share/minecraft-pi/server/docker-compose.yml
vendored
Normal file
7
debian/server/usr/share/minecraft-pi/server/docker-compose.yml
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
version: '3.7'
|
||||
services:
|
||||
minecraft-pi:
|
||||
image: 'thebrokenrail/minecraft-pi:server'
|
||||
network_mode: 'host'
|
||||
volumes:
|
||||
- '${MCPI_ROOT}:/root/.minecraft'
|
@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.1.0)
|
||||
project(mods)
|
||||
|
||||
add_compile_options(-Wall -Wextra -Werror)
|
||||
add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
|
||||
|
||||
add_subdirectory(../core core)
|
||||
|
||||
@ -11,8 +12,11 @@ include_directories(include)
|
||||
add_library(core SHARED src/core.c)
|
||||
target_link_libraries(core dl)
|
||||
|
||||
add_library(extra SHARED src/extra.c src/extra.cpp)
|
||||
target_link_libraries(extra core dl)
|
||||
add_library(server SHARED src/server/server.cpp src/server/server_properties.cpp)
|
||||
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)
|
||||
|
||||
|
@ -28,6 +28,8 @@ static Window x11_window;
|
||||
static Window x11_root_window;
|
||||
static int window_loaded = 0;
|
||||
|
||||
static int is_server = 0;
|
||||
|
||||
// Get Reference To X Window
|
||||
static void store_x11_window() {
|
||||
x11_display = glfwGetX11Display();
|
||||
@ -184,10 +186,17 @@ HOOK(SDL_WM_SetCaption, void, (const char *title, __attribute__((unused)) const
|
||||
exit(1);
|
||||
}
|
||||
|
||||
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);
|
||||
if (is_server) {
|
||||
// Don't Show Window In Server Mode
|
||||
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
||||
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);
|
||||
if (!glfw_window) {
|
||||
@ -195,19 +204,27 @@ HOOK(SDL_WM_SetCaption, void, (const char *title, __attribute__((unused)) const
|
||||
exit(1);
|
||||
}
|
||||
|
||||
glfwSetKeyCallback(glfw_window, glfw_key);
|
||||
glfwSetCharCallback(glfw_window, glfw_char);
|
||||
glfwSetCursorPosCallback(glfw_window, glfw_motion);
|
||||
glfwSetMouseButtonCallback(glfw_window, glfw_click);
|
||||
glfwSetScrollCallback(glfw_window, glfw_scroll);
|
||||
if (!is_server) {
|
||||
// Don't Process Events In Server Mode
|
||||
glfwSetKeyCallback(glfw_window, glfw_key);
|
||||
glfwSetCharCallback(glfw_window, glfw_char);
|
||||
glfwSetCursorPosCallback(glfw_window, glfw_motion);
|
||||
glfwSetMouseButtonCallback(glfw_window, glfw_click);
|
||||
glfwSetScrollCallback(glfw_window, glfw_scroll);
|
||||
}
|
||||
|
||||
store_x11_window();
|
||||
|
||||
glfwMakeContextCurrent(glfw_window);
|
||||
if (!is_server) {
|
||||
glfwMakeContextCurrent(glfw_window);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@ -345,28 +362,42 @@ HOOK(SDL_Quit, void, ()) {
|
||||
glfwTerminate();
|
||||
}
|
||||
|
||||
static SDL_GrabMode fake_grab_mode = SDL_GRAB_OFF;
|
||||
|
||||
// Fix SDL Cursor Visibility/Grabbing
|
||||
HOOK(SDL_WM_GrabInput, SDL_GrabMode, (SDL_GrabMode mode)) {
|
||||
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);
|
||||
if (is_server) {
|
||||
// Don't Grab Input In Server/Headless Mode
|
||||
if (mode != SDL_GRAB_QUERY) {
|
||||
fake_grab_mode = mode;
|
||||
}
|
||||
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
|
||||
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
|
||||
@ -430,6 +461,9 @@ HOOK(eglTerminate, EGLBoolean, (__attribute__((unused)) EGLDisplay display)) {
|
||||
|
||||
// Use VirGL
|
||||
__attribute__((constructor)) static void init() {
|
||||
is_server = get_is_server();
|
||||
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
18
mods/src/cxx11_util.cpp
Normal 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
21
mods/src/cxx11_util.h
Normal 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
|
@ -5,6 +5,7 @@
|
||||
#include <libcore/libcore.h>
|
||||
|
||||
#include "extra.h"
|
||||
#include "server/server.h"
|
||||
|
||||
static uint32_t getSpawnMobs_injection(__attribute__((unused)) int32_t obj) {
|
||||
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
|
||||
static void set_is_survival(int new_is_survival) {
|
||||
if (is_survival != new_is_survival) {
|
||||
@ -137,19 +121,62 @@ static void minecraft_init_injection(unsigned char *this) {
|
||||
*(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() {
|
||||
is_server = get_is_server();
|
||||
if (is_server) {
|
||||
server_init();
|
||||
}
|
||||
|
||||
if (has_feature("Touch GUI")) {
|
||||
// Use Touch UI
|
||||
unsigned char touch_gui_patch[4] = {0x01, 0x00, 0x50, 0xe3};
|
||||
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
|
||||
set_is_survival(0);
|
||||
set_is_survival(!default_game_mode);
|
||||
setIsCreativeMode_original = overwrite((void *) setIsCreativeMode, setIsCreativeMode_injection);
|
||||
|
||||
// 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);
|
||||
|
||||
// Disable Item Dropping When Cursor Is Hidden
|
||||
@ -170,7 +197,13 @@ __attribute__((constructor)) static void init() {
|
||||
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
|
||||
overwrite((void *) 0xbabec, getSpawnMobs_injection);
|
||||
}
|
||||
@ -193,8 +226,14 @@ __attribute__((constructor)) static void init() {
|
||||
patch((void *) 0x6dc70, patch_data_9);
|
||||
|
||||
// Change Username
|
||||
const char *username = get_username();
|
||||
fprintf(stderr, "Setting Username: %s\n", username);
|
||||
const char *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);
|
||||
|
||||
if (has_feature("Disable Autojump By Default")) {
|
||||
|
@ -8,14 +8,17 @@
|
||||
#include <libcore/libcore.h>
|
||||
|
||||
#include "extra.h"
|
||||
#include "cxx11_util.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
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/");
|
||||
full_path.append(path.c_str());
|
||||
full_path.append(path);
|
||||
std::ifstream stream(full_path);
|
||||
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 *);
|
||||
|
@ -7,6 +7,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
int has_feature(const char *name);
|
||||
int get_is_server();
|
||||
|
||||
void key_press(char key);
|
||||
void clear_input();
|
||||
|
237
mods/src/server/server.cpp
Normal file
237
mods/src/server/server.cpp
Normal 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
19
mods/src/server/server.h
Normal 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
|
37
mods/src/server/server_properties.cpp
Normal file
37
mods/src/server/server_properties.cpp
Normal 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);
|
||||
}
|
22
mods/src/server/server_properties.h
Normal file
22
mods/src/server/server_properties.h
Normal 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
|
@ -2,4 +2,5 @@
|
||||
|
||||
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 .
|
||||
|
@ -10,7 +10,8 @@ rm -rf out
|
||||
mkdir -p out/deb
|
||||
|
||||
# Generate DEB
|
||||
dpkg -b debian out/deb
|
||||
dpkg -b debian/client out/deb
|
||||
dpkg -b debian/server out/deb
|
||||
|
||||
# Export Libraries
|
||||
mkdir -p out/lib
|
||||
@ -19,6 +20,6 @@ mkdir -p out/lib
|
||||
cp -r mods/include out/lib/include
|
||||
|
||||
# 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 rm -v "${IMG_ID}"
|
||||
|
Loading…
Reference in New Issue
Block a user