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
|
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
|
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}"
|
@ -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'
|
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
|
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)
|
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)
|
||||||
|
|
||||||
|
@ -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
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 <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")) {
|
||||||
|
@ -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 *);
|
||||||
|
@ -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
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
|
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
|
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}"
|
||||||
|
Loading…
Reference in New Issue
Block a user