From 58679939979538437a787a4f2bd3e8b9bd36d0f0 Mon Sep 17 00:00:00 2001 From: murdle Date: Mon, 19 Jan 2026 20:18:08 +0200 Subject: [PATCH] add dedicated server and readme --- .gitignore | 1 + CMakeLists.txt | 17 +- FindEGL.cmake | 53 ++++ README.md | 54 +++- mcpe/Api.h | 19 ++ mcpe/AppResourceLoader.h | 19 ++ mcpe/AutomationClient.h | 22 ++ mcpe/EntitlementManager.h | 16 + mcpe/FilePathManager.h | 70 +++++ mcpe/FilePickerSettings.h | 18 +- mcpe/IMinecraftApp.h | 21 ++ mcpe/ImagePickingCallback.h | 2 +- mcpe/Keyboard.h | 2 +- mcpe/LevelSettings.h | 25 ++ mcpe/MinecraftEventing.h | 12 + mcpe/MinecraftGame.h | 3 + mcpe/OpsList.h | 12 + mcpe/Resource.h | 18 ++ mcpe/ResourcePack.h | 59 ++++ mcpe/ResourcePackStack.h | 24 ++ mcpe/Scheduler.h | 17 + mcpe/ServerInstance.h | 64 ++++ mcpe/Social.h | 37 +++ mcpe/UUID.h | 14 + mcpe/Whitelist.h | 11 + mcpe/gl.h | 9 +- mcpe/string.cpp | 79 +++++ mcpe/string.h | 53 ++++ mcpe/types.cpp | 106 ++++++- server.properties | 12 + src/common.cpp | 24 +- src/common.h | 4 +- ...AppPlatform.cpp => linux_app_platform.cpp} | 25 +- ...inuxAppPlatform.h => linux_app_platform.h} | 10 +- src/main.cpp | 44 +-- src/server.cpp | 297 ++++++++++++++++++ src/server_minecraft_app.h | 17 + src/server_properties.cpp | 41 +++ src/server_properties.h | 29 ++ 39 files changed, 1277 insertions(+), 83 deletions(-) create mode 100644 FindEGL.cmake create mode 100644 mcpe/Api.h create mode 100644 mcpe/AppResourceLoader.h create mode 100644 mcpe/AutomationClient.h create mode 100644 mcpe/EntitlementManager.h create mode 100644 mcpe/FilePathManager.h create mode 100644 mcpe/IMinecraftApp.h create mode 100644 mcpe/LevelSettings.h create mode 100644 mcpe/MinecraftEventing.h create mode 100644 mcpe/OpsList.h create mode 100644 mcpe/Resource.h create mode 100644 mcpe/ResourcePack.h create mode 100644 mcpe/ResourcePackStack.h create mode 100644 mcpe/Scheduler.h create mode 100644 mcpe/ServerInstance.h create mode 100644 mcpe/Social.h create mode 100644 mcpe/UUID.h create mode 100644 mcpe/Whitelist.h create mode 100644 mcpe/string.cpp create mode 100644 mcpe/string.h create mode 100644 server.properties rename src/{LinuxAppPlatform.cpp => linux_app_platform.cpp} (94%) rename src/{LinuxAppPlatform.h => linux_app_platform.h} (95%) create mode 100644 src/server_minecraft_app.h create mode 100644 src/server_properties.cpp create mode 100644 src/server_properties.h diff --git a/.gitignore b/.gitignore index e71dd52..2df2d50 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.vscode .idea/ assets/ data/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 6810b45..d0af6fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,9 +13,6 @@ link_directories(/usr/lib32) include_directories(/usr/include) set(X11_X11_LIB /usr/lib32/libX11.so) -#SET(CMAKE_CXX_COMPILER "g++-4.9") -#SET(CMAKE_C_COMPILER "gcc-4.9") - find_package(Threads REQUIRED) find_package(ZLIB REQUIRED) find_package(X11 REQUIRED) @@ -27,11 +24,19 @@ include_directories(${EGL_INCLUDE_DIRS}) include_directories(eglut) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -D_GLIBCXX_USE_CXX11_ABI=0") +set(HYBRIS_DEFINES PRIVATE ANDROID_X86_LINKER _GNU_SOURCE LINKER_TEXT_BASE=0xB0000100 LINKER_AREA_SIZE=0x01000000 LINKER_DEBUG=1) set(HYBRIS_SOURCES hybris/src/cache.c hybris/src/dlfcn.c hybris/src/hooks.c hybris/src/hooks_shm.c hybris/src/logging.c hybris/src/properties.c hybris/src/strlcpy.c hybris/src/sysconf.c hybris/src/jb/dlfcn.c hybris/src/jb/linker.c hybris/src/jb/linker_environ.c hybris/src/jb/linker_format.c hybris/src/jb/rt.c) set(EGLUT_SOURCES eglut/eglut.c eglut/eglut_x11.c) -set(SOURCE_FILES src/main.cpp src/common.cpp src/hook.cpp src/amdfix.s src/LinuxAppPlatform.cpp mcpe/types.cpp mcpe/ImagePickingCallback.h mcpe/FilePickerSettings.h) -add_executable(mcpelauncher ${HYBRIS_SOURCES} ${EGLUT_SOURCES} ${SOURCE_FILES}) +set(COMMON_SOURCE_FILES src/common.cpp src/hook.cpp src/linux_app_platform.cpp mcpe/types.cpp mcpe/string.cpp) +set(CLIENT_SOURCE_FILES src/main.cpp src/amdfix.s) +set(SERVER_SOURCE_FILES src/server.cpp src/server_properties.cpp) + +add_executable(mcpelauncher ${HYBRIS_SOURCES} ${EGLUT_SOURCES} ${COMMON_SOURCE_FILES} ${CLIENT_SOURCE_FILES}) target_link_libraries(mcpelauncher ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT} rt libEGL.so libGLESv2.so ${X11_X11_LIB} uuid) -target_compile_definitions(mcpelauncher PRIVATE ANDROID_X86_LINKER _GNU_SOURCE LINKER_TEXT_BASE=0xB0000100 LINKER_AREA_SIZE=0x01000000 LINKER_DEBUG=1) +target_compile_definitions(mcpelauncher ${HYBRIS_DEFINES}) set_target_properties(mcpelauncher PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32") + +add_executable(server ${HYBRIS_SOURCES} ${COMMON_SOURCE_FILES} ${SERVER_SOURCE_FILES}) +target_link_libraries(server ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT} ${X11_X11_LIB} rt uuid z zip) +target_compile_definitions(server ${HYBRIS_DEFINES} SERVER) \ No newline at end of file diff --git a/FindEGL.cmake b/FindEGL.cmake new file mode 100644 index 0000000..f0c58c8 --- /dev/null +++ b/FindEGL.cmake @@ -0,0 +1,53 @@ +# - Try to Find EGL +# Once done, this will define +# +# EGL_FOUND - system has EGL installed. +# EGL_INCLUDE_DIRS - directories which contain the EGL headers. +# EGL_LIBRARIES - libraries required to link against EGL. +# EGL_DEFINITIONS - Compiler switches required for using EGL. +# +# Copyright (C) 2012 Intel Corporation. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS +# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +find_package(PkgConfig) + +pkg_check_modules(PC_EGL egl) + +if (PC_EGL_FOUND) + set(EGL_DEFINITIONS ${PC_EGL_CFLAGS_OTHER}) +endif () + +find_path(EGL_INCLUDE_DIRS NAMES EGL/egl.h + HINTS ${PC_EGL_INCLUDEDIR} ${PC_EGL_INCLUDE_DIRS} +) + +set(EGL_NAMES ${EGL_NAMES} egl EGL) +find_library(EGL_LIBRARIES NAMES ${EGL_NAMES} + HINTS ${PC_EGL_LIBDIR} ${PC_EGL_LIBRARY_DIRS} +) + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(EGL DEFAULT_MSG EGL_INCLUDE_DIRS EGL_LIBRARIES) + +mark_as_advanced(EGL_INCLUDE_DIRS EGL_LIBRARIES) \ No newline at end of file diff --git a/README.md b/README.md index 0ec7aca..f14cb65 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,62 @@ MCPE Linux Launcher =================== -## Required packages +A fork of [mcpelauncher-linux](https://github.com/MCMrARM/mcpelauncher-linux) (commit from 2017, MCPE 1.1) with additional features and improvements -``` - sudo apt-get install cmake zlib1g-dev:i386 libncurses5-dev:i386 libgles2-mesa-dev gcc-multilib g++-multilib zlib1g-dev:i386 libx11-dev:i386 linux-libc-dev:i386 uuid-dev:i386 libpng-dev:i386 libx11-dev:i386 libxext6:i386 +## Features +- Dedicated server support +- Compatibility fixes for building with the latest GCC +- Other quality-of-life improvements + +## Required Packages + +### Ubuntu / Debian +Install the necessary dependencies with: + +```bash +sudo dpkg --add-architecture i386 +sudo apt-get update +sudo apt-get install cmake zlib1g-dev:i386 libncurses5-dev:i386 \ +libgles2-mesa-dev libegl1-mesa-dev:i386 gcc-multilib g++-multilib \ +libx11-dev:i386 linux-libc-dev:i386 uuid-dev:i386 libpng-dev:i386 \ +libxext6:i386 ``` -If g++-4.9 fails to install and you're using Ubuntu 14.04 you may need to add the repository ppa:ubuntu-toolchain-r/test. +### Arch Linux +Install the necessary dependencies with: -You'll also need to install 32-bit version of the graphic drivers (nvidia drivers ask you about that at installation, so -you may need to reinstall/reconfigure them; if you use mesa you'll need to install the libgles2-mesa-dev:i386 and -libegl1-mesa-dev:i386 packages) +```bash +sudo pacman -Syu base-devel cmake zlib libpng libx11 libxext libglvnd \ +libxrandr libxi mesa lib32-gcc-libs lib32-glibc lib32-mesa lib32-libx11 \ +lib32-libxext lib32-libxrandr lib32-libxi +``` -You may also need to do `sudo dpkg --add-architecture i386` if you have never installed i386 packages before. +Make sure the multilib repo is enabled in `/etc/pacman.conf`. ## Compiling This app uses cmake so it is enough to do: ``` - cmake . - make +mkdir build +cmake .. +make ``` ## Running 1. Clone this repository 2. Compile the launcher -3. You'll need to obtain a x86 MCPE .apk. The easiest way to do so is to use the -[Google Play downloader tool](https://github.com/MCMrARM/Google-Play-API) (it is an console app; remember that -you need to type 'y' when you are asked if you want to use x86 as the architecture, and you must have purchased MCPE -on the Google account you are logging in with; the package name of MCPE is `com.mojang.minecraftpe`) +3. Obtain a x86 MCPE 1.1 apk file 4. After you have downloaded MCPE, place it in the directory where you'll be running this app, and run ./extract.sh _filename_ 5. Run the launcher! If the extract script fails with an error about the .apk not being x86, it means that you have provided it a bad .apk. -You'll need to purchase MCPE on Google Play and use the downloader tool. + +## Using the Dedicated Server +1. Create a server.properties file and fill in your settings. An example is located in the root of this repository. +2. Run the server binary that you built earlier ## License and thanks -Most of the code in this repo is licensed under BSD. This project uses libc, libstdc++, libz and libm - libraries +- Credit goes to [MCMrARM](https://github.com/MCMrARM) for the original source code +- Most of the code in this repo is licensed under BSD. This project uses libc, libstdc++, libz and libm - libraries extracted from the Android OS. A modified version of libhybris is also included, which is licensed under GPL. This project -also uses the EGLUT library and FMOD library (for sound). +also uses the EGLUT library and FMOD library (for sound). \ No newline at end of file diff --git a/mcpe/Api.h b/mcpe/Api.h new file mode 100644 index 0000000..378a8d4 --- /dev/null +++ b/mcpe/Api.h @@ -0,0 +1,19 @@ +#pragma once + +namespace minecraft { +namespace api { + +class Api { + +public: + + void** vtable; + mcpe::string envPath; + void** playerIfaceVtable; + void** entityIfaceVtable; + void** networkIfaceVtable; + void** playerInteractionsIfaceVtable; +}; + +} +} \ No newline at end of file diff --git a/mcpe/AppResourceLoader.h b/mcpe/AppResourceLoader.h new file mode 100644 index 0000000..842f422 --- /dev/null +++ b/mcpe/AppResourceLoader.h @@ -0,0 +1,19 @@ +#pragma once + +#include "string.h" +#include +#include "Resource.h" + +class AppResourceLoader : public ResourceLoader { + +private: + char filler[0x14]; + +public: + static void (*AppResourceLoader_construct)(AppResourceLoader*, std::function); + + AppResourceLoader(std::function f) { + AppResourceLoader_construct(this, f); + } + +}; \ No newline at end of file diff --git a/mcpe/AutomationClient.h b/mcpe/AutomationClient.h new file mode 100644 index 0000000..515c72b --- /dev/null +++ b/mcpe/AutomationClient.h @@ -0,0 +1,22 @@ +#pragma once + +#include "IMinecraftApp.h" + +namespace Automation { + +class AutomationClient { + +public: + + static void (*AutomationClient_construct)(AutomationClient*, IMinecraftApp&); + + char filler[0x300]; + + AutomationClient(IMinecraftApp& a) { + AutomationClient_construct(this, a); + } + + +}; + +} \ No newline at end of file diff --git a/mcpe/EntitlementManager.h b/mcpe/EntitlementManager.h new file mode 100644 index 0000000..9c3570d --- /dev/null +++ b/mcpe/EntitlementManager.h @@ -0,0 +1,16 @@ +#pragma once +#include + +namespace Social { class UserManager; } +class MinecraftEventing; + +class EntitlementManager { +public: + char filler[0x100]; + + static void (*EntitlementManager_construct)(EntitlementManager*, MinecraftEventing&, Social::UserManager&); + + EntitlementManager(MinecraftEventing &evt, Social::UserManager &um) { + EntitlementManager_construct(this, evt, um); + } +}; \ No newline at end of file diff --git a/mcpe/FilePathManager.h b/mcpe/FilePathManager.h new file mode 100644 index 0000000..f7d136a --- /dev/null +++ b/mcpe/FilePathManager.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include "string.h" + +class FilePathManager { + +public: + char filler[0x20]; + + static void (*FilePathManager_construct)( + FilePathManager*, + mcpe::string, + bool + ); + + static mcpe::string (*FilePathManager_getRootPath)( + FilePathManager const* + ); + + static mcpe::string (*FilePathManager_getUserDataPath)( + FilePathManager const* + ); + + static void (*FilePathManager_setPackagePath)( + FilePathManager*, + mcpe::string + ); + + static mcpe::string (*FilePathManager_getPackagePath)( + FilePathManager const* + ); + + static void (*FilePathManager_setSettingsPath)( + FilePathManager*, + mcpe::string + ); + + static mcpe::string (*FilePathManager_getSettingsPath)( + FilePathManager const* + ); + + FilePathManager(mcpe::string str, bool b) { + FilePathManager_construct(this, std::move(str), b); + } + + mcpe::string getRootPath() const { + return FilePathManager_getRootPath(this); + } + + mcpe::string getUserDataPath() const { + return FilePathManager_getUserDataPath(this); + } + + void setPackagePath(mcpe::string s) { + FilePathManager_setPackagePath(this, std::move(s)); + } + + mcpe::string getPackagePath() const { + return FilePathManager_getPackagePath(this); + } + + void setSettingsPath(mcpe::string s) { + FilePathManager_setSettingsPath(this, std::move(s)); + } + + mcpe::string getSettingsPath() const { + return FilePathManager_getSettingsPath(this); + } +}; \ No newline at end of file diff --git a/mcpe/FilePickerSettings.h b/mcpe/FilePickerSettings.h index 05a2f2b..4f537da 100644 --- a/mcpe/FilePickerSettings.h +++ b/mcpe/FilePickerSettings.h @@ -1,6 +1,6 @@ #pragma once -#include +#include "string.h" #include #include @@ -10,15 +10,15 @@ struct FilePickerSettings { NONE, OPEN, SAVE }; struct FileDescription { - std::string ext, desc; + mcpe::string ext, desc; }; - char filler [0x20]; // 20 - std::function pickedCallback; // 30 - std::vector fileDescriptions; // 3c - int filler3; // 40 - PickerType type; // 44 - std::string defaultFileName; // 48 - std::string pickerTitle; // 52 + char filler [0x20]; + std::function pickedCallback; + std::vector fileDescriptions; + int filler3; + PickerType type; + mcpe::string defaultFileName; + mcpe::string pickerTitle; }; \ No newline at end of file diff --git a/mcpe/IMinecraftApp.h b/mcpe/IMinecraftApp.h new file mode 100644 index 0000000..4e4a506 --- /dev/null +++ b/mcpe/IMinecraftApp.h @@ -0,0 +1,21 @@ +#pragma once + +class Minecraft; + +namespace Automation { + class AutomationClient; +} + +class IMinecraftApp { + +public: + + virtual ~IMinecraftApp() { } + virtual Minecraft* getPrimaryMinecraft() = 0; + virtual Automation::AutomationClient* getAutomationClient() = 0; + virtual bool isEduMode() = 0; + virtual bool isDedicatedServer() = 0; + virtual int getDefaultNetworkMaxPlayers() = 0; + virtual void onNetworkMaxPlayersChanged(unsigned int) = 0; + +}; \ No newline at end of file diff --git a/mcpe/ImagePickingCallback.h b/mcpe/ImagePickingCallback.h index 3a96c22..8a7247d 100644 --- a/mcpe/ImagePickingCallback.h +++ b/mcpe/ImagePickingCallback.h @@ -6,7 +6,7 @@ class ImagePickingCallback { public: virtual ~ImagePickingCallback(); - virtual void onImagePickingSuccess(const std::string&); + virtual void onImagePickingSuccess(const mcpe::string&); virtual void onImagePickingCanceled(); }; \ No newline at end of file diff --git a/mcpe/Keyboard.h b/mcpe/Keyboard.h index d8456e2..fea0be6 100644 --- a/mcpe/Keyboard.h +++ b/mcpe/Keyboard.h @@ -9,7 +9,7 @@ class Keyboard { public: - static void (*Keyboard_feedText)(const std::string&, bool, unsigned char); + static void (*Keyboard_feedText)(const mcpe::string&, bool, unsigned char); static std::vector* inputs; static int* states; diff --git a/mcpe/LevelSettings.h b/mcpe/LevelSettings.h new file mode 100644 index 0000000..f1fe089 --- /dev/null +++ b/mcpe/LevelSettings.h @@ -0,0 +1,25 @@ +#pragma once +#include +#include + +class LevelSettings { +public: + static void (*LevelSettings_construct)(LevelSettings*); + + int seed; // 4 + int gametype; // 8 + int difficulty; // c + int forceGameType; // 10 + int generator; // 14 + bool hasAchievementsDisabled; // 18 + int dimension; // 1c + int time; // 20 + bool edu; // 21 + float rainLevel, lightningLevel; // 28, 2c + bool mpGame, lanBroadcast, xblBroadcast, commandsEnabled, texturepacksRequired, overrideSavedSettings; // 2d, 2e, 2f, 30, 31, 32~34 + char filler[0x300]; + + LevelSettings() { + LevelSettings_construct(this); + } +}; diff --git a/mcpe/MinecraftEventing.h b/mcpe/MinecraftEventing.h new file mode 100644 index 0000000..9a36a13 --- /dev/null +++ b/mcpe/MinecraftEventing.h @@ -0,0 +1,12 @@ +#pragma once + +class MinecraftEventing { +public: + char filler[0x200]; + + static void (*MinecraftEventing_construct)(MinecraftEventing*, mcpe::string const&); + + MinecraftEventing(mcpe::string const& str) { + MinecraftEventing_construct(this, str); + } +}; \ No newline at end of file diff --git a/mcpe/MinecraftGame.h b/mcpe/MinecraftGame.h index 2901b22..e574c9d 100644 --- a/mcpe/MinecraftGame.h +++ b/mcpe/MinecraftGame.h @@ -3,6 +3,9 @@ #include "App.h" class Options; +class MinecraftEventing; +class ResourcePackRepository; +class ResourcePackManager; class MinecraftGame : public App { diff --git a/mcpe/OpsList.h b/mcpe/OpsList.h new file mode 100644 index 0000000..5ae5c13 --- /dev/null +++ b/mcpe/OpsList.h @@ -0,0 +1,12 @@ +#pragma once + +struct OpsList { + + struct Entry { + char filler[0x10]; + }; + + bool b; + std::vector entries; + +}; \ No newline at end of file diff --git a/mcpe/Resource.h b/mcpe/Resource.h new file mode 100644 index 0000000..fca0772 --- /dev/null +++ b/mcpe/Resource.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +enum class ResourceFileSystem; + +class ResourceLoader { +}; + +class Resource { + +public: + static void (*Resource_registerLoader)(ResourceFileSystem, std::unique_ptr); + + static void registerLoader(ResourceFileSystem fs, std::unique_ptr loader) { + Resource_registerLoader(fs, std::move(loader)); + } +}; \ No newline at end of file diff --git a/mcpe/ResourcePack.h b/mcpe/ResourcePack.h new file mode 100644 index 0000000..2a44827 --- /dev/null +++ b/mcpe/ResourcePack.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include + +class MinecraftEventing; +class FilePathManager; +class ResourcePack; +class ResourcePackStack; +class ResourceLoader; +enum class ResourcePackStackType; + +class PackManifestFactory { + +public: + + char filler[4]; + + static void (*PackManifestFactory_construct)(PackManifestFactory*, MinecraftEventing&); + + PackManifestFactory(MinecraftEventing& ev) { + PackManifestFactory_construct(this, ev); + } + +}; + +class ResourcePackRepository { + +public: + + char filler[0x1C]; + ResourcePack* vanillaPack; + char filler2[0x200]; + + static void (*ResourcePackRepository_construct)(ResourcePackRepository*, MinecraftEventing&, PackManifestFactory&, EntitlementManager*, FilePathManager*); + + ResourcePackRepository(MinecraftEventing& ev, PackManifestFactory& factory, EntitlementManager* em, FilePathManager* fpm) { + ResourcePackRepository_construct(this, ev, factory, em, fpm); + } + +}; + +class ResourcePackManager: public ResourceLoader { + +public: + + char filler[0x200]; + + static void (*ResourcePackManager_construct)(ResourcePackManager*, std::function const); + static void (*ResourcePackManager_setStack)(ResourcePackManager*, std::unique_ptr, ResourcePackStackType, bool); + + ResourcePackManager(std::function const& f) { + ResourcePackManager_construct(this, f); + } + + void setStack(std::unique_ptr&& stackPtr, ResourcePackStackType stackType, bool b) { + ResourcePackManager_setStack(this, std::move(stackPtr), stackType, b); + } +}; \ No newline at end of file diff --git a/mcpe/ResourcePackStack.h b/mcpe/ResourcePackStack.h new file mode 100644 index 0000000..6acabec --- /dev/null +++ b/mcpe/ResourcePackStack.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +class ResourcePack; +class ResourcePackRepository; + +struct ResourcePackStack { + + static void** vtable_sym; + static void (*ResourcePackStack_add)(ResourcePackStack*, ResourcePack*, ResourcePackRepository const&, bool); + + void** vtable; + char filler[0x30]; + + ResourcePackStack() { + vtable = vtable_sym + 2; + memset(filler, 0, sizeof(filler)); + } + + void add(ResourcePack* resPack, ResourcePackRepository const& repo, bool b) { + ResourcePackStack_add(this, resPack, repo, b); + } +}; \ No newline at end of file diff --git a/mcpe/Scheduler.h b/mcpe/Scheduler.h new file mode 100644 index 0000000..ba567d0 --- /dev/null +++ b/mcpe/Scheduler.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +struct Scheduler { + +public: + + static Scheduler* (*singleton)(); + static void (*Scheduler_processCoroutines)(Scheduler*, std::chrono::duration); + + void processCoroutines(std::chrono::duration d) { + Scheduler_processCoroutines(this, d); + } + + +}; \ No newline at end of file diff --git a/mcpe/ServerInstance.h b/mcpe/ServerInstance.h new file mode 100644 index 0000000..4c893bc --- /dev/null +++ b/mcpe/ServerInstance.h @@ -0,0 +1,64 @@ +#pragma once + +#include +#include +#include +#include + +#include "string.h" + +class IMinecraftApp; +class Minecraft; +class Whitelist; +class OpsList; +class LevelSettings; +class FilePathManager; + +namespace minecraft { namespace api { class Api; } } +namespace mce { class UUID; } + +class MinecraftEventing; +class ResourcePackRepository; +class ResourcePackManager; + +class ServerInstance { + +public: + char filler[0x300]; + + static void (*ServerInstance_construct)( + ServerInstance*, + IMinecraftApp&, + Whitelist const&, + OpsList const&, + FilePathManager*, + std::chrono::duration, + mcpe::string, + mcpe::string, + mcpe::string, + mcpe::string, + mcpe::string, + LevelSettings*, + minecraft::api::Api&, + int, + bool, + int, + int, + int, + bool, + std::vector const&, + mcpe::string, + bool, + mce::UUID const&, + MinecraftEventing&, + ResourcePackRepository&, + ResourcePackManager&, + ResourcePackManager* + ); + + static void (*ServerInstance_update)(ServerInstance*); + + void update() { + ServerInstance_update(this); + } +}; \ No newline at end of file diff --git a/mcpe/Social.h b/mcpe/Social.h new file mode 100644 index 0000000..d758fe6 --- /dev/null +++ b/mcpe/Social.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +class ScreenStack; + +namespace Social { + +class XboxLiveUserManager { +public: + char filler[0x100]; + + static void (*XboxLiveUserManager_construct)(XboxLiveUserManager*, ScreenStack*); + + XboxLiveUserManager() { + XboxLiveUserManager_construct(this, nullptr); + } +}; + +class UserManager { +public: + char filler[0x100]; + + static void (*UserManager_construct)(UserManager*); + static void (*UserManager_addUser)(UserManager*, std::unique_ptr, bool); + + UserManager() { + UserManager_construct(this); + } + + void addUser(std::unique_ptr manager) { + UserManager_addUser(this, std::move(manager), false); + } +}; + +} \ No newline at end of file diff --git a/mcpe/UUID.h b/mcpe/UUID.h new file mode 100644 index 0000000..5c2c7b2 --- /dev/null +++ b/mcpe/UUID.h @@ -0,0 +1,14 @@ +#pragma once + +namespace mce { + +class UUID { + +public: + + static UUID* EMPTY; + char filler[0x14]; + +}; + +} \ No newline at end of file diff --git a/mcpe/Whitelist.h b/mcpe/Whitelist.h new file mode 100644 index 0000000..2704f88 --- /dev/null +++ b/mcpe/Whitelist.h @@ -0,0 +1,11 @@ +#pragma once + +struct Whitelist { + + struct Entry { + char filler[0x20]; + }; + + std::vector entries; + +}; \ No newline at end of file diff --git a/mcpe/gl.h b/mcpe/gl.h index 1cc1217..d7bb4f8 100644 --- a/mcpe/gl.h +++ b/mcpe/gl.h @@ -1,10 +1,11 @@ #pragma once +#include "string.h" struct gl { - static std::string (*getOpenGLVendor)(); - static std::string (*getOpenGLRenderer)(); - static std::string (*getOpenGLVersion)(); - static std::string (*getOpenGLExtensions)(); + static mcpe::string (*getOpenGLVendor)(); + static mcpe::string (*getOpenGLRenderer)(); + static mcpe::string (*getOpenGLVersion)(); + static mcpe::string (*getOpenGLExtensions)(); }; namespace mce { diff --git a/mcpe/string.cpp b/mcpe/string.cpp new file mode 100644 index 0000000..c2b80d3 --- /dev/null +++ b/mcpe/string.cpp @@ -0,0 +1,79 @@ +#define _GLIBCXX_USE_CXX11_ABI 0 +#include "string.h" + +mcpe::string* mcpe::string::empty; + +mcpe::string::string() { + ptr = empty->ptr; +} + +mcpe::string::string(const char *str) { + if (str[0] == '\0') { + ptr = empty->ptr; + } else { + new (this) std::string(str); + } +} + +mcpe::string::string(const char *str, size_t len) { + if (len == 0) { + ptr = empty->ptr; + } else { + new (this) std::string(str, len); + } +} + +mcpe::string::string(const string &str) { + if (str.ptr == empty->ptr) { + ptr = empty->ptr; + } else { + new (this) std::string(*((const std::string *) &str)); + } +} + +mcpe::string::~string() { + if (ptr == empty->ptr) + return; + ((std::string*) this)->~basic_string(); +} + +size_t mcpe::string::length() const { + if (ptr == empty->ptr) + return 0; + return ((std::string*) this)->length(); +} + +mcpe::string mcpe::string::operator+(const string &str) { + return *((std::string*) this) + *((std::string*) &str); +} + +mcpe::string& mcpe::string::operator=(const mcpe::string &str) { + if (this == &str) + return *this; + + if (ptr != empty->ptr) { + if (str.ptr == empty->ptr) { + ((std::string*) this)->~basic_string(); + ptr = empty->ptr; + } else { + *((std::string*) this) = *((const std::string*) &str); + } + } else { + if (str.ptr != empty->ptr) { + new (this) std::string(*((const std::string*)&str)); + } + } + + return *this; +} + +const char *mcpe::string::c_str() const { + if (ptr == empty->ptr) + return ""; + return ((std::string*) this)->c_str(); +} + +std::ostream& operator<< (std::ostream& os, const mcpe::string& obj) { + os << *((std::string const*) &obj); + return os; +} \ No newline at end of file diff --git a/mcpe/string.h b/mcpe/string.h new file mode 100644 index 0000000..1ae6aa2 --- /dev/null +++ b/mcpe/string.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include + +namespace mcpe { + +struct string { + +private: + void* ptr; + +public: + static mcpe::string* empty; + + string(); + string(const char *str); + string(const char *str, size_t len); + string(const string &str); + inline string(const std::string &str) : string(str.c_str(), str.length()) {} + ~string(); + + string &operator=(const string &str); + + size_t length() const; + const char *c_str() const; + + string operator+(const string &str); + + bool operator==(const string &s) const { + if (s.ptr == ptr) + return true; + if (s.length() != length()) + return false; + return (memcmp(c_str(), s.c_str(), length()) == 0); + } + + bool operator<(const string& s) const { + int d = memcmp(c_str(), s.c_str(), std::min(length(), s.length())); + if (d < 0) return true; + if (d == 0) return length() < s.length(); + return false; + } + + inline std::string std() const { + return std::string(c_str(), length()); + } + +}; + +} + +std::ostream& operator<<(std::ostream&, const mcpe::string&); \ No newline at end of file diff --git a/mcpe/types.cpp b/mcpe/types.cpp index 8218b3e..966dbd4 100644 --- a/mcpe/types.cpp +++ b/mcpe/types.cpp @@ -28,10 +28,10 @@ void (*Options::Options_setFullscreen)(Options*, bool); #include "gl.h" -std::string (*gl::getOpenGLVendor)(); -std::string (*gl::getOpenGLRenderer)(); -std::string (*gl::getOpenGLVersion)(); -std::string (*gl::getOpenGLExtensions)(); +mcpe::string (*gl::getOpenGLVendor)(); +mcpe::string (*gl::getOpenGLRenderer)(); +mcpe::string (*gl::getOpenGLVersion)(); +mcpe::string (*gl::getOpenGLExtensions)(); void (*mce::Platform::OGL::OGL_initBindings)(); #include "Mouse.h" @@ -40,6 +40,100 @@ void (*Mouse::feed)(char, char, short, short, short, short); #include "Keyboard.h" -void (*Keyboard::Keyboard_feedText)(const std::string&, bool, unsigned char); +void (*Keyboard::Keyboard_feedText)(const mcpe::string&, bool, unsigned char); std::vector* Keyboard::inputs; -int* Keyboard::states; \ No newline at end of file +int* Keyboard::states; + +#include "FilePathManager.h" + +void (*FilePathManager::FilePathManager_construct)(FilePathManager*, mcpe::string, bool); +mcpe::string (*FilePathManager::FilePathManager_getRootPath)(FilePathManager const*); +mcpe::string (*FilePathManager::FilePathManager_getUserDataPath)(FilePathManager const*); +void (*FilePathManager::FilePathManager_setPackagePath)(FilePathManager*, mcpe::string); +mcpe::string (*FilePathManager::FilePathManager_getPackagePath)(FilePathManager const*); +void (*FilePathManager::FilePathManager_setSettingsPath)(FilePathManager*, mcpe::string); +mcpe::string (*FilePathManager::FilePathManager_getSettingsPath)(FilePathManager const*); + +#include "LevelSettings.h" + +void (*LevelSettings::LevelSettings_construct)(LevelSettings*); + +#include "Social.h" + +void (*Social::UserManager::UserManager_construct)(Social::UserManager*); +void (*Social::UserManager::UserManager_addUser)(UserManager*, std::unique_ptr, bool); +void (*Social::XboxLiveUserManager::XboxLiveUserManager_construct)(Social::XboxLiveUserManager*, ScreenStack*); + +#include "MinecraftEventing.h" + +void (*MinecraftEventing::MinecraftEventing_construct)(MinecraftEventing*, mcpe::string const&); + +#include "EntitlementManager.h" + +void (*EntitlementManager::EntitlementManager_construct)(EntitlementManager*, MinecraftEventing&, Social::UserManager&); + +#include "Resource.h" + +void (*Resource::Resource_registerLoader)(ResourceFileSystem, std::unique_ptr); + +#include "AppResourceLoader.h" + +void (*AppResourceLoader::AppResourceLoader_construct)(AppResourceLoader*, std::function); + +#include "ResourcePack.h" + +void (*PackManifestFactory::PackManifestFactory_construct)(PackManifestFactory*, MinecraftEventing&); +void (*ResourcePackRepository::ResourcePackRepository_construct)(ResourcePackRepository*, MinecraftEventing&, PackManifestFactory&, EntitlementManager*, FilePathManager*); +void (*ResourcePackManager::ResourcePackManager_construct)(ResourcePackManager*, std::function const); +void (*ResourcePackManager::ResourcePackManager_setStack)(ResourcePackManager*, std::unique_ptr, ResourcePackStackType, bool); + +#include "AutomationClient.h" + +void (*Automation::AutomationClient::AutomationClient_construct)(Automation::AutomationClient*, IMinecraftApp&); + +#include "ServerInstance.h" + +void (*ServerInstance::ServerInstance_construct)( + ServerInstance*, + IMinecraftApp&, + Whitelist const&, + OpsList const&, + FilePathManager*, + std::chrono::duration, + mcpe::string, + mcpe::string, + mcpe::string, + mcpe::string, + mcpe::string, + LevelSettings*, + minecraft::api::Api&, + int, + bool, + int, + int, + int, + bool, + std::vector const&, + mcpe::string, + bool, + mce::UUID const&, + MinecraftEventing&, + ResourcePackRepository&, + ResourcePackManager&, + ResourcePackManager* +); +void (*ServerInstance::ServerInstance_update)(ServerInstance*); + +#include "UUID.h" + +mce::UUID* mce::UUID::EMPTY; + +#include "ResourcePackStack.h" + +void (*ResourcePackStack::ResourcePackStack_add)(ResourcePackStack*, ResourcePack*, ResourcePackRepository const&, bool); +void** ResourcePackStack::vtable_sym; + +#include "Scheduler.h" + +Scheduler* (*Scheduler::singleton)(); +void (*Scheduler::Scheduler_processCoroutines)(Scheduler*, std::chrono::duration); \ No newline at end of file diff --git a/server.properties b/server.properties new file mode 100644 index 0000000..e782c86 --- /dev/null +++ b/server.properties @@ -0,0 +1,12 @@ +motd=Test +view-distance=22 +server-port=19132 +server-port-v6=19133 +max-players=20 +online-mode=false +difficulty=1 +gamemode=0 +level-dir=oI8CAKp7BQA= +level-name=My World +level-seed=3986400852 +level-type=1 \ No newline at end of file diff --git a/src/common.cpp b/src/common.cpp index 970c734..8abebcf 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -1,5 +1,6 @@ #include "common.h" +#include #include #include #include @@ -14,14 +15,25 @@ extern "C" { #include "../hybris/include/hybris/dlfcn.h" } -std::string getCWD() { - char _cwd[MAXPATHLEN]; - getcwd(_cwd, MAXPATHLEN); - return std::string(_cwd) + "/"; +std::string rootPath = ""; + +std::string getRootPath() { + char resolved[PATH_MAX]; + const char* input = rootPath.empty() ? "." : rootPath.c_str(); + + if (realpath(input, resolved) == nullptr) { + getcwd(resolved, PATH_MAX); + } + + std::string result(resolved); + if (!result.empty() && result.back() != '/') + result += '/'; + + return result; } bool loadLibrary(std::string path) { - void* handle = hybris_dlopen((getCWD() + "libs/" + path).c_str(), RTLD_LAZY); + void* handle = hybris_dlopen((getRootPath() + "libs/" + path).c_str(), RTLD_LAZY); if (handle == nullptr) { printf("failed to load library %s: %s\n", path.c_str(), hybris_dlerror()); return false; @@ -49,7 +61,7 @@ void* loadLibraryOS(std::string path, const char** symbols) { } void* loadMod(std::string path) { - void* handle = hybris_dlopen((getCWD() + "mods/" + path).c_str(), RTLD_LAZY); + void* handle = hybris_dlopen((getRootPath() + "mods/" + path).c_str(), RTLD_LAZY); if (handle == nullptr) { printf("failed to load mod: %s\n", path.c_str()); return nullptr; diff --git a/src/common.h b/src/common.h index 6e22845..0acb79b 100644 --- a/src/common.h +++ b/src/common.h @@ -2,7 +2,9 @@ #include -std::string getCWD(); +extern std::string rootPath; + +std::string getRootPath(); bool loadLibrary(std::string path); void* loadLibraryOS(std::string path, const char** symbols); void* loadMod(std::string path); diff --git a/src/LinuxAppPlatform.cpp b/src/linux_app_platform.cpp similarity index 94% rename from src/LinuxAppPlatform.cpp rename to src/linux_app_platform.cpp index 9210de5..a7a6a5b 100644 --- a/src/LinuxAppPlatform.cpp +++ b/src/linux_app_platform.cpp @@ -1,4 +1,4 @@ -#include "LinuxAppPlatform.h" +#include "linux_app_platform.h" #include #include #include @@ -14,24 +14,25 @@ #include "../hybris/src/jb/linker.h" extern "C" { +#ifndef SERVER #include +#endif #include "../hybris/include/hybris/dlfcn.h" } void** LinuxAppPlatform::myVtable = nullptr; bool LinuxAppPlatform::mousePointerHidden = false; bool enablePocketGuis = false; -bool serverMode = false; LinuxAppPlatform::LinuxAppPlatform() : AppPlatform() { this->vtable = myVtable; - internalStorage = "data/private/"; - externalStorage = "data/public/"; - currentStorage = "data/current/"; - userdata = "data/user/"; - userdataPathForLevels = "data/user/"; + internalStorage = getRootPath() + "data/private/"; + externalStorage = getRootPath() + "data/public/"; + currentStorage = getRootPath() + "data/current/"; + userdata = getRootPath() + "data/user/"; + userdataPathForLevels = getRootPath() + "data/user/"; region = "0xdeadbeef"; - tmpPath = "tmp/"; + tmpPath = getRootPath() + "tmp/"; } #include @@ -99,12 +100,16 @@ void LinuxAppPlatform::initVtable(void* lib) { void LinuxAppPlatform::hideMousePointer() { mousePointerHidden = true; +#ifndef SERVER moveMouseToCenter = true; eglutSetMousePointerVisiblity(EGLUT_POINTER_INVISIBLE); +#endif } void LinuxAppPlatform::showMousePointer() { mousePointerHidden = false; +#ifndef SERVER eglutSetMousePointerVisiblity(EGLUT_POINTER_VISIBLE); +#endif } std::string LinuxAppPlatform::_pickFile(std::string commandLine) { @@ -149,7 +154,7 @@ void LinuxAppPlatform::pickFile(FilePickerSettings &settings) { std::cout << " - " << d.ext << " " << d.desc << "\n"; } std::stringstream ss; - ss << "zenity --file-selection --title '" << replaceAll(settings.pickerTitle, "'", "\\'") << "'"; + //ss << "zenity --file-selection --title '" << replaceAll(settings.pickerTitle, "'", "\\'") << "'"; if (settings.type == FilePickerSettings::PickerType::SAVE) ss << " --save"; if (settings.fileDescriptions.size() > 0) { @@ -170,9 +175,11 @@ void LinuxAppPlatform::pickFile(FilePickerSettings &settings) { void LinuxAppPlatform::setFullscreenMode(int mode) { std::cout << "set fullscreen mode: " << mode << "\n"; +#ifndef SERVER int newMode = mode == 1 ? EGLUT_FULLSCREEN : EGLUT_WINDOWED; if (eglutGet(EGLUT_FULLSCREEN_MODE) != newMode) eglutToggleFullscreen(); +#endif } std::string LinuxAppPlatform::createUUID() { diff --git a/src/LinuxAppPlatform.h b/src/linux_app_platform.h similarity index 95% rename from src/LinuxAppPlatform.h rename to src/linux_app_platform.h index cd2c10a..15182b5 100644 --- a/src/LinuxAppPlatform.h +++ b/src/linux_app_platform.h @@ -6,6 +6,7 @@ #include "../mcpe/gl.h" #include "../mcpe/AppPlatform.h" #include "../mcpe/ImagePickingCallback.h" +#include "common.h" class ImageData; class ImagePickingCallback; @@ -13,7 +14,6 @@ class FilePickerSettings; extern bool enablePocketGuis; extern bool moveMouseToCenter; -extern bool serverMode; class LinuxAppPlatform : public AppPlatform { @@ -35,15 +35,15 @@ public: std::string getDataUrl() { // this is used only for sounds printf("get data url: assets/\n"); - return "assets/"; + return getRootPath() + "assets/"; } std::string getUserDataUrl() { // this is used only for sounds printf("get user data url: data/user/\n"); - return "data/user/"; + return getRootPath() + "data/user/"; } std::string getPackagePath() { - return "assets/"; + return getRootPath() + "assets/"; } void hideMousePointer(); @@ -88,7 +88,7 @@ public: } std::string getAssetFileFullPath(std::string const& s) { printf("get assert full path: %s\n", s.c_str()); - return "assets/" + s; + return getRootPath() + "assets/" + s; } int getScreenType() { if (enablePocketGuis) diff --git a/src/main.cpp b/src/main.cpp index 526c8e1..8dbcf8c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,16 +13,23 @@ #include "android_symbols.h" #include "egl_symbols.h" #include "fmod_symbols.h" +#include "linux_app_platform.h" +#include "LinuxStore.h" +#include "common.h" +#include "hook.h" + #include "../mcpe/gl.h" #include "../mcpe/AppPlatform.h" #include "../mcpe/MinecraftGame.h" -#include "LinuxAppPlatform.h" -#include "LinuxStore.h" #include "../mcpe/Mouse.h" #include "../mcpe/Keyboard.h" #include "../mcpe/Options.h" -#include "common.h" -#include "hook.h" +#include "../mcpe/Whitelist.h" +#include "../mcpe/OpsList.h" +#include "../mcpe/LevelSettings.h" +#include "../mcpe/UUID.h" +#include "../mcpe/ServerInstance.h" +#include "../mcpe/FilePathManager.h" extern "C" { @@ -33,8 +40,6 @@ extern "C" { } -void commonStub() {} - void androidStub() { std::cout << "warn: android call\n"; } @@ -218,28 +223,29 @@ int main(int argc, char *argv[]) { } else if (strcmp(argv[i], "-sw") == 0 || strcmp(argv[i], "--width") == 0) { i++; windowWidth = std::stoi(argv[i]); - } else if (strcmp(argv[i], "-sh") == 0 || strcmp(argv[i], "--height") == 0) { + } else if (strcmp(argv[i], "-sw") == 0 || strcmp(argv[i], "--width") == 0) { i++; - windowHeight = std::stoi(argv[i]); + windowWidth = std::stoi(argv[i]); + } else if (strcmp(argv[i], "-r") == 0 || strcmp(argv[i], "--root-path") == 0) { + i++; + rootPath = argv[i]; } else if (strcmp(argv[i], "-ns") == 0 || strcmp(argv[i], "--no-stacktrace") == 0) { enableStackTracePrinting = false; } else if (strcmp(argv[i], "--pocket-guis") == 0) { enablePocketGuis = true; - } else if (strcmp(argv[i], "--server") == 0) { - serverMode = true; } else if (strcmp(argv[i], "--amd-fix") == 0) { std::cout << "--amd-fix: Enabling AMD Workaround.\n"; workaroundAMD = true; } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { std::cout << "Help\n"; std::cout << "--help Shows this help information\n"; + std::cout << "--root-path Sets the root path for game data\n"; std::cout << "--scale Sets the pixel scale\n"; std::cout << "--width Sets the window width\n"; std::cout << "--height Sets the window height\n"; std::cout << "--pocket-guis Switches to Pocket Edition GUIs\n"; std::cout << "--no-stacktrace Disables stack trace printing\n"; std::cout << "--amd-workaround Fixes crashes on pre-i686 and AMD CPUs\n\n"; - std::cout << "--server Enable dedicated server mode\n\n"; std::cout << "EGL Options\n"; std::cout << "-display Sets the display\n"; std::cout << "-info Shows info about the display\n\n"; @@ -258,7 +264,7 @@ int main(int argc, char *argv[]) { std::cout << "loading native libraries\n"; void* glesLib = loadLibraryOS("libGLESv2.so", gles_symbols); - void* fmodLib = loadLibraryOS((getCWD() + "libs/native/libfmod.so.8.2").c_str(), fmod_symbols); + void* fmodLib = loadLibraryOS((getRootPath() + "libs/native/libfmod.so.8.2").c_str(), fmod_symbols); if (glesLib == nullptr || fmodLib == nullptr) return -1; @@ -277,7 +283,7 @@ int main(int argc, char *argv[]) { if (!loadLibrary("libmcpelauncher_mod.so")) return -1; std::cout << "loading MCPE\n"; - std::string mcpePath = getCWD() + "libs/libminecraftpe.so"; + std::string mcpePath = getRootPath() + "libs/libminecraftpe.so"; void* handle = hybris_dlopen(mcpePath.c_str(), RTLD_LAZY); if (handle == nullptr) { std::cout << "failed to load MCPE: " << hybris_dlerror() << "\n"; @@ -309,6 +315,8 @@ int main(int argc, char *argv[]) { std::cout << "loaded " << mods.size() << " mods\n"; } + mcpe::string::empty = (mcpe::string*) hybris_dlsym(handle, "_ZN4Util12EMPTY_STRINGE"); + std::cout << "apply patches\n"; /* @@ -373,10 +381,10 @@ int main(int argc, char *argv[]) { std::cout << "patches applied!\n"; // load symbols for gl - gl::getOpenGLVendor = (std::string (*)()) hybris_dlsym(handle, "_ZN2gl15getOpenGLVendorEv"); - gl::getOpenGLRenderer = (std::string (*)()) hybris_dlsym(handle, "_ZN2gl17getOpenGLRendererEv"); - gl::getOpenGLVersion = (std::string (*)()) hybris_dlsym(handle, "_ZN2gl16getOpenGLVersionEv"); - gl::getOpenGLExtensions = (std::string (*)()) hybris_dlsym(handle, "_ZN2gl19getOpenGLExtensionsEv"); + gl::getOpenGLVendor = (mcpe::string (*)()) hybris_dlsym(handle, "_ZN2gl15getOpenGLVendorEv"); + gl::getOpenGLRenderer = (mcpe::string (*)()) hybris_dlsym(handle, "_ZN2gl17getOpenGLRendererEv"); + gl::getOpenGLVersion = (mcpe::string (*)()) hybris_dlsym(handle, "_ZN2gl16getOpenGLVersionEv"); + gl::getOpenGLExtensions = (mcpe::string (*)()) hybris_dlsym(handle, "_ZN2gl19getOpenGLExtensionsEv"); mce::Platform::OGL::OGL_initBindings = (void (*)()) hybris_dlsym(handle, "_ZN3mce8Platform3OGL12InitBindingsEv"); // init linux app platform @@ -399,7 +407,7 @@ int main(int argc, char *argv[]) { Keyboard::inputs = (std::vector*) hybris_dlsym(handle, "_ZN8Keyboard7_inputsE"); Keyboard::states = (int*) hybris_dlsym(handle, "_ZN8Keyboard7_statesE"); - Keyboard::Keyboard_feedText = (void (*)(const std::string&, bool, unsigned char)) hybris_dlsym(handle, "_ZN8Keyboard8feedTextERKSsbh"); + Keyboard::Keyboard_feedText = (void (*)(const mcpe::string&, bool, unsigned char)) hybris_dlsym(handle, "_ZN8Keyboard8feedTextERKSsbh"); Options::Options_getFullscreen = (bool (*)(Options*)) hybris_dlsym(handle, "_ZNK7Options13getFullscreenEv"); Options::Options_setFullscreen = (void (*)(Options*, bool)) hybris_dlsym(handle, "_ZN7Options13setFullscreenEb"); diff --git a/src/server.cpp b/src/server.cpp index e69de29..b99adc5 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -0,0 +1,297 @@ +#include +#include +#include +#include +#include +#include +#include "gles_symbols.h" +#include "android_symbols.h" +#include "egl_symbols.h" +#include "fmod_symbols.h" +#include "linux_app_platform.h" +#include "common.h" +#include "hook.h" +#include "server_minecraft_app.h" +#include "server_properties.h" + +#include "../mcpe/Api.h" +#include "../mcpe/AppPlatform.h" +#include "../mcpe/FilePathManager.h" +#include "../mcpe/LevelSettings.h" +#include "../mcpe/Social.h" +#include "../mcpe/MinecraftEventing.h" +#include "../mcpe/EntitlementManager.h" +#include "../mcpe/AppResourceLoader.h" +#include "../mcpe/Resource.h" +#include "../mcpe/ResourcePack.h" +#include "../mcpe/AutomationClient.h" +#include "../mcpe/ServerInstance.h" +#include "../mcpe/Whitelist.h" +#include "../mcpe/OpsList.h" +#include "../mcpe/UUID.h" +#include "../mcpe/ResourcePackStack.h" +#include "../mcpe/Scheduler.h" + +extern "C" { + +#include "../hybris/include/hybris/dlfcn.h" +#include "../hybris/include/hybris/hook.h" +#include "../hybris/src/jb/linker.h" + +} + +void stubFunc() {} + +void detachFromJavaStub() { + std::cout << "detach from java\n"; +} + +void* getJVMEnvStub() { + std::cout << "getjvmenv\n"; + return nullptr; +} + +int getDifficultyStub() { + return ServerProperties::instance().getInt("difficulty", 0); +} + +int getGameTypeStub() { + return ServerProperties::instance().getInt("gamemode", 0); +} + +using namespace std; + +int main(int argc, char *argv[]) { + for (int i = 1; i < argc; i++) { + if ((strcmp(argv[i], "-r") == 0 || strcmp(argv[i], "--root-path") == 0) && i + 1 < argc) { + rootPath = argv[i + 1]; + i++; + } + } + if (rootPath.empty()) { + std::cout << "[*] No root path set, the current directory will be used to store game data\n"; + std::cout << "[*] It is recommended to set one by using the `--root-path` argument\n"; + } + + // load properties + std::ifstream propertiesFile(getRootPath() + "server.properties"); + if (propertiesFile) { + ServerProperties::instance().load(propertiesFile); + } else { + std::cerr << "[!] No server.properties file found, cannot continue\n"; + return 1; + } + + registerCrashHandler(); + setenv("LC_ALL", "C", 1); + + std::cout << "loading native libraries\n"; + void* glesLib = loadLibraryOS("libGLESv2.so", gles_symbols); + void* fmodLib = loadLibraryOS((getRootPath() + "libs/native/libfmod.so.8.2").c_str(), fmod_symbols); + if (glesLib == nullptr || fmodLib == nullptr) + return -1; + + std::cout << "loading hybris libraries\n"; + stubSymbols(android_symbols, (void*)stubFunc); + stubSymbols(egl_symbols, (void*)stubFunc); + stubSymbols(gles_symbols, (void*)stubFunc); + stubSymbols(fmod_symbols, (void*)stubFunc); + hybris_hook("eglGetProcAddress", (void*) stubFunc); + hookAndroidLog(); + + if (!loadLibrary("libc.so") || !loadLibrary("libstdc++.so") || !loadLibrary("libm.so") || !loadLibrary("libz.so")) + return -1; + // load stub libraries + if (!loadLibrary("libandroid.so") || !loadLibrary("liblog.so") || !loadLibrary("libEGL.so") || !loadLibrary("libGLESv2.so") || !loadLibrary("libOpenSLES.so") || !loadLibrary("libfmod.so") || !loadLibrary("libGLESv1_CM.so")) + return -1; + if (!loadLibrary("libmcpelauncher_mod.so")) + return -1; + std::cout << "loading MCPE\n"; + std::string mcpePath = getRootPath() + "libs/libminecraftpe.so"; + void* handle = hybris_dlopen(mcpePath.c_str(), RTLD_LAZY); + if (handle == nullptr) { + std::cout << "failed to load MCPE: " << hybris_dlerror() << "\n"; + return -1; + } + addHookLibrary(handle, mcpePath); + + unsigned int libBase = ((soinfo*) handle)->base; + std::cout << "loaded MCPE (at " << libBase << ")\n"; + + std::cout << "apply patches\n"; + + unsigned int patchOff = (unsigned int) hybris_dlsym(handle, "_ZN9crossplat10threadpool16detach_from_javaEPv"); + patchCallInstruction((void*) patchOff, (void*) &detachFromJavaStub, true); + + patchOff = (unsigned int) hybris_dlsym(handle, "_ZN9crossplat11get_jvm_envEv"); + patchCallInstruction((void*) patchOff, (void*) &getJVMEnvStub, true); + + // murdle: these functions seem to be returning garbage data and fucking stuff up so i will just stub them + // it's probably something related to levelsettings but it is 4 am and i want to sleep + // at this point it's easier to just do this than spend 3 more days debugging + + patchOff = (unsigned int) hybris_dlsym(handle, "_ZNK5Level13getDifficultyEv"); + patchCallInstruction((void*) patchOff, (void*) &getDifficultyStub, true); + + patchOff = (unsigned int) hybris_dlsym(handle, "_ZNK9LevelData11getGameTypeEv"); + patchCallInstruction((void*) patchOff, (void*) &getGameTypeStub, true); + + mcpe::string::empty = (mcpe::string*) hybris_dlsym(handle, "_ZN4Util12EMPTY_STRINGE"); + + std::cout << "patches applied!\n"; + + // init linux app platform + AppPlatform::myVtable = (void**) hybris_dlsym(handle, "_ZTV11AppPlatform"); + AppPlatform::_singleton = (AppPlatform**) hybris_dlsym(handle, "_ZN11AppPlatform10mSingletonE"); + AppPlatform::AppPlatform_construct = (void (*)(AppPlatform*)) hybris_dlsym(handle, "_ZN11AppPlatformC2Ev"); + AppPlatform::AppPlatform_initialize = (void (*)(AppPlatform*)) hybris_dlsym(handle, "_ZN11AppPlatform10initializeEv"); + AppPlatform::AppPlatform__fireAppFocusGained = (void (*)(AppPlatform*)) hybris_dlsym(handle, "_ZN11AppPlatform19_fireAppFocusGainedEv"); + + void** ptr = (void**) hybris_dlsym(handle, "_ZN9crossplat3JVME"); + *ptr = (void*) 1; // this just needs not to be null + + // server functions + ((void*&) mce::UUID::EMPTY) = hybris_dlsym(handle, "_ZN3mce4UUID5EMPTYE"); + + ((void*&) FilePathManager::FilePathManager_construct) = hybris_dlsym(handle, "_ZN15FilePathManagerC2ESsb"); + ((void*&) FilePathManager::FilePathManager_getRootPath) = hybris_dlsym(handle, "_ZNK15FilePathManager11getRootPathEv"); + ((void*&) FilePathManager::FilePathManager_getUserDataPath) = hybris_dlsym(handle, "_ZNK15FilePathManager15getUserDataPathEv"); + ((void*&) FilePathManager::FilePathManager_setSettingsPath) = hybris_dlsym(handle, "_ZN15FilePathManager15setSettingsPathESs"); + ((void*&) FilePathManager::FilePathManager_getSettingsPath) = hybris_dlsym(handle, "_ZNK15FilePathManager15getSettingsPathEv"); + ((void*&) FilePathManager::FilePathManager_setPackagePath) = hybris_dlsym(handle, "_ZN15FilePathManager14setPackagePathESs"); + ((void*&) FilePathManager::FilePathManager_getPackagePath) = hybris_dlsym(handle, "_ZNK15FilePathManager14getPackagePathEv"); + + ((void*&) Social::XboxLiveUserManager::XboxLiveUserManager_construct) = hybris_dlsym(handle, "_ZN6Social19XboxLiveUserManagerC2ER11ScreenStack"); + ((void*&) Social::UserManager::UserManager_construct) = hybris_dlsym(handle, "_ZN6Social11UserManagerC2Ev"); + ((void*&) Social::UserManager::UserManager_addUser) = hybris_dlsym(handle, "_ZN6Social11UserManager7addUserESt10unique_ptrINS_19XboxLiveUserManagerESt14default_deleteIS2_EEi"); + + ((void*&) MinecraftEventing::MinecraftEventing_construct) = hybris_dlsym(handle, "_ZN17MinecraftEventingC2ERKSs"); + ((void*&) EntitlementManager::EntitlementManager_construct) = hybris_dlsym(handle, "_ZN18EntitlementManagerC2ER17MinecraftEventingRN6Social11UserManagerE"); + ((void*&) Automation::AutomationClient::AutomationClient_construct) = hybris_dlsym(handle, "_ZN10Automation16AutomationClientC2ER13IMinecraftApp"); + ((void*&) LevelSettings::LevelSettings_construct) = hybris_dlsym(handle, "_ZN13LevelSettingsC2Ev"); + + ((void*&) Resource::Resource_registerLoader) = hybris_dlsym(handle, "_ZN8Resource14registerLoaderE18ResourceFileSystemSt10unique_ptrI14ResourceLoaderSt14default_deleteIS2_EE"); + ((void*&) AppResourceLoader::AppResourceLoader_construct) = hybris_dlsym(handle, "_ZN17AppResourceLoaderC2ESt8functionIFSsvEE"); + + ((void*&) PackManifestFactory::PackManifestFactory_construct) = hybris_dlsym(handle, "_ZN19PackManifestFactoryC2ER17MinecraftEventing"); + ((void*&) ResourcePackManager::ResourcePackManager_construct) = hybris_dlsym(handle, "_ZN19ResourcePackManagerC2ESt8functionIFSsvEE"); + ((void*&) ResourcePackManager::ResourcePackManager_setStack) = hybris_dlsym(handle, "_ZN19ResourcePackManager8setStackESt10unique_ptrI17ResourcePackStackSt14default_deleteIS1_EE21ResourcePackStackTypeb"); + + ((void*&) ResourcePackStack::vtable_sym) = hybris_dlsym(handle, "_ZTV17ResourcePackStack"); + ((void*&) ResourcePackStack::ResourcePackStack_add) = hybris_dlsym(handle, "_ZN17ResourcePackStack3addEP12ResourcePackRK22ResourcePackRepositoryb"); + ((void*&) ResourcePackRepository::ResourcePackRepository_construct) = hybris_dlsym(handle, "_ZN22ResourcePackRepositoryC2ER17MinecraftEventingR19PackManifestFactoryP18EntitlementManagerP15FilePathManager"); + + ((void*&) ServerInstance::ServerInstance_construct) = hybris_dlsym(handle, "_ZN14ServerInstanceC2ER13IMinecraftAppRK9WhitelistRK7OpsListP15FilePathManagerNSt6chrono8durationIxSt5ratioILx1ELx1EEEESsSsSsSsSs13LevelSettingsRN9minecraft3api3ApiEibiiibRKSt6vectorISsSaISsEESsbRKN3mce4UUIDER17MinecraftEventingR22ResourcePackRepositoryR19ResourcePackManagerPSX_"); + ((void*&) ServerInstance::ServerInstance_update) = hybris_dlsym(handle, "_ZN14ServerInstance6updateEv"); + + ((void*&) Scheduler::singleton) = hybris_dlsym(handle, "_ZN9Scheduler9singletonEv"); + ((void*&) Scheduler::Scheduler_processCoroutines) = hybris_dlsym(handle, "_ZN9Scheduler17processCoroutinesEd"); + + std::cout << "init app platform vtable\n"; + LinuxAppPlatform::initVtable(handle); + std::cout << "init app platform\n"; + LinuxAppPlatform* platform = new LinuxAppPlatform(); + std::cout << "app platform initialized\n"; + + platform->initialize(); + + Whitelist whitelist; + OpsList ops; + + minecraft::api::Api api; + api.vtable = (void**)hybris_dlsym(handle, "_ZTVN9minecraft3api3ApiE") + 2; + api.envPath = getRootPath(); + api.playerIfaceVtable = (void**) hybris_dlsym(handle, "_ZTVN9minecraft3api15PlayerInterfaceE") + 2; + api.entityIfaceVtable = (void**) hybris_dlsym(handle, "_ZTVN9minecraft3api15EntityInterfaceE") + 2; + api.networkIfaceVtable = (void**) hybris_dlsym(handle, "_ZTVN9minecraft3api16NetworkInterfaceE") + 2; + api.playerInteractionsIfaceVtable = (void**) hybris_dlsym(handle, "_ZTVN9minecraft3api26PlayerInteractionInterfaceE") + 2; + + FilePathManager pathmgr (platform->getCurrentStoragePath(), false); + pathmgr.setPackagePath(platform->getPackagePath()); + pathmgr.setSettingsPath(pathmgr.getRootPath()); + + LevelSettings levelSettings; + levelSettings.seed = ServerProperties::instance().getLong("level-seed", 0); + levelSettings.gametype = ServerProperties::instance().getInt("gamemode", 0); + levelSettings.forceGameType = ServerProperties::instance().getBool("force-gamemode", false); + levelSettings.difficulty = ServerProperties::instance().getInt("difficulty", 0); + levelSettings.dimension = 0; + levelSettings.generator = ServerProperties::instance().getInt("level-type", 1); + levelSettings.edu = false; + levelSettings.mpGame = true; + levelSettings.lanBroadcast = true; + levelSettings.commandsEnabled = true; + levelSettings.texturepacksRequired = false; + + std::unique_ptr xboxUserManager( + new Social::XboxLiveUserManager() + ); + Social::UserManager userManager; + userManager.addUser(std::move(xboxUserManager)); + + MinecraftEventing eventing(pathmgr.getRootPath()); + EntitlementManager entManager(eventing, userManager); + + Resource::registerLoader((ResourceFileSystem) 1, std::unique_ptr(new AppResourceLoader([&pathmgr] { return pathmgr.getPackagePath(); }))); + Resource::registerLoader((ResourceFileSystem) 8, std::unique_ptr(new AppResourceLoader([&pathmgr] { return pathmgr.getUserDataPath(); }))); + Resource::registerLoader((ResourceFileSystem) 4, std::unique_ptr(new AppResourceLoader([&pathmgr] { return pathmgr.getSettingsPath(); }))); + + std::function rootPathFn = + [&pathmgr]() -> std::string { + return pathmgr.getRootPath().c_str(); + }; + + ResourcePackManager* resourcePackManager = new ResourcePackManager(rootPathFn); + Resource::registerLoader((ResourceFileSystem) 0, std::unique_ptr(resourcePackManager)); + + PackManifestFactory packManifestFactory(eventing); + ResourcePackRepository resourcePackRepo(eventing, packManifestFactory, &entManager, &pathmgr); + + std::unique_ptr stack(new ResourcePackStack()); + stack->add(resourcePackRepo.vanillaPack, resourcePackRepo, false); + resourcePackManager->setStack(std::move(stack), (ResourcePackStackType) 3, false); + + DedicatedServerMinecraftApp app; + Automation::AutomationClient aclient (app); + app.automationClient = &aclient; + + ServerInstance server; + ServerInstance::ServerInstance_construct( + &server, + app, + whitelist, + ops, + &pathmgr, + std::chrono::duration(0), + ServerProperties::instance().getString("level-dir"), + ServerProperties::instance().getString("level-name"), + ServerProperties::instance().getString("motd"), + "", + "", + &levelSettings, + api, + ServerProperties::instance().getInt("view-distance", 22), + true, + ServerProperties::instance().getInt("server-port", 19132), + ServerProperties::instance().getInt("server-port-v6", 19133), + ServerProperties::instance().getInt("max-players", 20), + ServerProperties::instance().getBool("online-mode", false), + {}, + "normal", + false, + *mce::UUID::EMPTY, + eventing, + resourcePackRepo, + *resourcePackManager, + resourcePackManager + ); + + std::cout << "Server running\n"; + + while (true) { + server.update(); + Scheduler::singleton()->processCoroutines(std::chrono::duration_cast>(std::chrono::milliseconds(50))); + } + + return 0; +} diff --git a/src/server_minecraft_app.h b/src/server_minecraft_app.h new file mode 100644 index 0000000..8ff3dc1 --- /dev/null +++ b/src/server_minecraft_app.h @@ -0,0 +1,17 @@ +#pragma once + +#include "../mcpe/IMinecraftApp.h" + +class DedicatedServerMinecraftApp : public IMinecraftApp { + +public: + Automation::AutomationClient* automationClient; + + virtual Minecraft* getPrimaryMinecraft() { return nullptr; } + virtual Automation::AutomationClient* getAutomationClient() { return automationClient; } + virtual bool isEduMode() { return false; } + virtual bool isDedicatedServer() { return true; } + virtual int getDefaultNetworkMaxPlayers() { return 20; } + virtual void onNetworkMaxPlayersChanged(unsigned int max) {} + +}; \ No newline at end of file diff --git a/src/server_properties.cpp b/src/server_properties.cpp new file mode 100644 index 0000000..983151c --- /dev/null +++ b/src/server_properties.cpp @@ -0,0 +1,41 @@ +#include "server_properties.h" + +void ServerProperties::load(std::istream& stream) { + std::string line; + while (std::getline(stream, line)) { + if (!line.empty() && line[0] == '#') + continue; + + size_t i = line.find('='); + if (i == std::string::npos) + continue; + + properties[line.substr(0, i)] = line.substr(i + 1); + } +} + +std::string ServerProperties::getString(const std::string& name, const std::string& def) { + return properties.count(name) ? properties.at(name) : def; +} + +int ServerProperties::getInt(const std::string& name, int def) { + return properties.count(name) ? std::stoi(properties.at(name)) : def; +} + +long long ServerProperties::getLong(const std::string& name, long long def) { + auto it = properties.find(name); + if (it == properties.end() || it->second.empty()) return def; + return std::stoll(it->second); +} + +float ServerProperties::getFloat(const std::string& name, float def) { + return properties.count(name) ? std::stof(properties.at(name)) : def; +} + +bool ServerProperties::getBool(const std::string& name, bool def) { + if (properties.count(name)) { + const std::string& val = properties.at(name); + return val == "true" || val == "yes" || val == "1"; + } + return def; +} \ No newline at end of file diff --git a/src/server_properties.h b/src/server_properties.h new file mode 100644 index 0000000..87f95f7 --- /dev/null +++ b/src/server_properties.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include + +class ServerProperties { +private: + std::map properties; + + ServerProperties() = default; + +public: + ServerProperties(const ServerProperties&) = delete; + ServerProperties& operator=(const ServerProperties&) = delete; + + static ServerProperties& instance() { + static ServerProperties instance; + return instance; + } + + void load(std::istream& stream); + + std::string getString(const std::string& name, const std::string& def = ""); + int getInt(const std::string& name, int def = 0); + long long getLong(const std::string& name, long long def = 0); + float getFloat(const std::string& name, float def = 0.f); + bool getBool(const std::string& name, bool def = false); +}; \ No newline at end of file