diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml
index 02b07e8f..45362623 100644
--- a/.gitea/workflows/build.yml
+++ b/.gitea/workflows/build.yml
@@ -14,9 +14,6 @@ jobs:
strategy:
fail-fast: false
matrix:
- mode:
- - Client
- - Server
arch:
- AMD64
- ARM64
@@ -31,40 +28,19 @@ jobs:
submodules: true
# Dependencies
- name: Install Dependencies
- run: ./scripts/install-dependencies.sh ${{ matrix.arch }}
+ run: ./scripts/install-dependencies.sh build ${{ matrix.arch }}
# Build
- name: Build
- run: ./scripts/build.mjs appimage ${{ matrix.mode }} ${{ matrix.arch }}
+ run: ./scripts/build.mjs appimage ${{ matrix.arch }}
- name: Upload Artifacts
uses: christopherhx/gitea-upload-artifact@v4
with:
- name: ${{ matrix.mode }} (${{ matrix.arch }})
+ name: ${{ matrix.arch }}
path: ./out/*.AppImage*
if-no-files-found: error
# Test Project
test:
- strategy:
- fail-fast: false
- matrix:
- mode:
- - Client
- - Server
- name: Test
- runs-on: ubuntu-latest
- container: node:lts-bullseye
- steps:
- - name: Checkout Repository
- uses: actions/checkout@v4
- with:
- submodules: true
- # Dependencies
- - name: Install Dependencies
- run: ./scripts/install-dependencies.sh
- # Test
- - name: Test
- run: ./scripts/test.sh ${{ matrix.mode }}
- # Test Project On ARM
- rpi-test:
+ needs: build
strategy:
fail-fast: false
matrix:
@@ -72,10 +48,11 @@ jobs:
- Client
- Server
arch:
+ - AMD64
- ARM64
- ARMHF
- name: Raspberry Pi Test
- runs-on: raspberry-pi
+ name: Test
+ runs-on: ${{ startsWith(matrix.arch, 'ARM') && 'raspberry-pi' || 'ubuntu-latest' }}
container: node:lts-bullseye
steps:
- name: Checkout Repository
@@ -84,12 +61,19 @@ jobs:
submodules: true
# Dependencies
- name: Install Dependencies
- run: ./scripts/install-dependencies.sh ${{ matrix.arch }}
+ run: ./scripts/install-dependencies.sh test ${{ matrix.arch }}
+ # Download Artifact
+ - name: Download Artifact
+ uses: christopherhx/gitea-download-artifact@v4
+ with:
+ name: ${{ matrix.arch }}
+ path: out
# Test
- name: Test
- run: ./scripts/test.sh ${{ matrix.mode }}
+ run: ./scripts/test.sh ${{ matrix.mode }} ${{ matrix.arch }}
# Example Mods
example-mods:
+ needs: build
name: Build Example Mods
runs-on: ubuntu-latest
container: node:lts-bullseye
@@ -100,16 +84,18 @@ jobs:
submodules: true
# Dependencies
- name: Install Dependencies
- run: ./scripts/install-dependencies.sh
- - name: Install ARM Toolchain
- run: apt-get install --no-install-recommends -y g++-arm-linux-gnueabihf gcc-arm-linux-gnueabihf
- # Build SDK
- - name: Build SDK
+ run: ./scripts/install-dependencies.sh example_mods amd64
+ # SDK
+ - name: Download SDK
+ uses: christopherhx/gitea-download-artifact@v4
+ with:
+ name: AMD64
+ path: out
+ - name: Extract SDK
run: |
- ./scripts/build.mjs none client host
- export _MCPI_SKIP_ROOT_CHECK=1
- export DISPLAY=
- ./out/client/host/usr/bin/minecraft-pi-reborn-client --copy-sdk
+ ./scripts/fix-appimage-for-docker.sh ./out/*.AppImage
+ chmod +x ./out/*.AppImage
+ ./out/*.AppImage --copy-sdk
# Build Example Mods
- name: Build Example Mods
run: ./example-mods/build.sh
@@ -122,7 +108,10 @@ jobs:
# Create Release
release:
if: startsWith(github.ref, 'refs/tags/')
- needs: build
+ needs:
+ - build
+ - test
+ - example-mods
name: Release
runs-on: ubuntu-latest
container: node:lts-bullseye
diff --git a/cmake/options/extra-options.cmake b/cmake/options/extra-options.cmake
index fc83462a..2f50538e 100644
--- a/cmake/options/extra-options.cmake
+++ b/cmake/options/extra-options.cmake
@@ -6,10 +6,6 @@ if(MCPI_IS_APPIMAGE_BUILD AND MCPI_IS_FLATPAK_BUILD)
message(FATAL_ERROR "Invalid Build Configuration")
endif()
-# Server/Headless Builds
-mcpi_option(SERVER_MODE "Server Mode" BOOL FALSE)
-mcpi_option(HEADLESS_MODE "Headless Mode" BOOL "${MCPI_SERVER_MODE}")
-
# Prebuilt ARMHF Toolchain
if(BUILD_NATIVE_COMPONENTS)
set(MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN FALSE)
@@ -22,16 +18,12 @@ if(BUILD_NATIVE_COMPONENTS)
endif()
# Media Layer
-if(NOT MCPI_HEADLESS_MODE)
- set(DEFAULT_USE_MEDIA_LAYER_TRAMPOLINE FALSE)
- if(BUILD_NATIVE_COMPONENTS AND NOT IS_ARM_TARGETING)
- set(DEFAULT_USE_MEDIA_LAYER_TRAMPOLINE TRUE)
- endif()
- mcpi_option(USE_MEDIA_LAYER_TRAMPOLINE "Whether To Enable The Media Layer Trampoline" BOOL "${DEFAULT_USE_MEDIA_LAYER_TRAMPOLINE}")
- mcpi_option(USE_GLES1_COMPATIBILITY_LAYER "Whether To Enable The GLESv1_CM Compatibility Layer" BOOL TRUE)
-else()
- set(MCPI_USE_MEDIA_LAYER_TRAMPOLINE FALSE)
+set(DEFAULT_USE_MEDIA_LAYER_TRAMPOLINE FALSE)
+if(BUILD_NATIVE_COMPONENTS AND NOT IS_ARM_TARGETING)
+ set(DEFAULT_USE_MEDIA_LAYER_TRAMPOLINE TRUE)
endif()
+mcpi_option(USE_MEDIA_LAYER_TRAMPOLINE "Whether To Enable The Media Layer Trampoline" BOOL "${DEFAULT_USE_MEDIA_LAYER_TRAMPOLINE}")
+mcpi_option(USE_GLES1_COMPATIBILITY_LAYER "Whether To Enable The GLESv1_CM Compatibility Layer" BOOL TRUE)
if(MCPI_USE_MEDIA_LAYER_TRAMPOLINE)
set(BUILD_MEDIA_LAYER_CORE "${BUILD_NATIVE_COMPONENTS}")
else()
@@ -40,30 +32,12 @@ endif()
# Specify Variant Name
set(MCPI_VARIANT_NAME "minecraft-pi-reborn")
-if(MCPI_SERVER_MODE)
- string(APPEND MCPI_VARIANT_NAME "-server")
-else()
- string(APPEND MCPI_VARIANT_NAME "-client")
-endif()
# App ID
-set(DEFAULT_APP_ID "com.thebrokenrail.MCPIReborn")
-if(MCPI_SERVER_MODE)
- string(APPEND DEFAULT_APP_ID "Server")
-else()
- string(APPEND DEFAULT_APP_ID "Client")
-endif()
-mcpi_option(APP_ID "App ID" STRING "${DEFAULT_APP_ID}")
+mcpi_option(APP_ID "App ID" STRING "com.thebrokenrail.MCPIReborn")
# App Title
-mcpi_option(APP_BASE_TITLE "Base App Title" STRING "Minecraft: Pi Edition: Reborn")
-set(DEFAULT_APP_TITLE "${MCPI_APP_BASE_TITLE}")
-if(MCPI_SERVER_MODE)
- string(APPEND DEFAULT_APP_TITLE " (Server)")
-else()
- string(APPEND DEFAULT_APP_TITLE " (Client)")
-endif()
-mcpi_option(APP_TITLE "App Title" STRING "${DEFAULT_APP_TITLE}")
+mcpi_option(APP_TITLE "App Title" STRING "Minecraft: Pi Edition: Reborn")
# Skin Server
mcpi_option(SKIN_SERVER "Skin Server" STRING "https://raw.githubusercontent.com/MCPI-Revival/Skins/data")
diff --git a/dependencies/CMakeLists.txt b/dependencies/CMakeLists.txt
index 2045929a..94d2b22b 100644
--- a/dependencies/CMakeLists.txt
+++ b/dependencies/CMakeLists.txt
@@ -1,7 +1,7 @@
project(dependencies)
# stb_image
-if(BUILD_ARM_COMPONENTS AND NOT MCPI_HEADLESS_MODE)
+if(BUILD_ARM_COMPONENTS)
add_subdirectory(stb_image)
endif()
# Minecraft: Pi Edition
@@ -9,21 +9,21 @@ if(BUILD_ARM_COMPONENTS AND NOT MCPI_OPEN_SOURCE_ONLY)
add_subdirectory(minecraft-pi)
endif()
# Zenity (Minimal Build)
-if(BUILD_NATIVE_COMPONENTS AND NOT MCPI_SERVER_MODE)
+if(BUILD_NATIVE_COMPONENTS)
add_subdirectory(zenity)
endif()
# LIEF
-if(BUILD_NATIVE_COMPONENTS OR (BUILD_MEDIA_LAYER_CORE AND NOT MCPI_HEADLESS_MODE))
+if(BUILD_NATIVE_COMPONENTS OR BUILD_MEDIA_LAYER_CORE)
add_subdirectory(LIEF)
endif()
# Extra Runtime
add_subdirectory(runtime)
# GLFW
-if(BUILD_MEDIA_LAYER_CORE AND NOT MCPI_HEADLESS_MODE)
+if(BUILD_MEDIA_LAYER_CORE)
add_subdirectory(glfw)
endif()
# GLES Compatibility Layer
-if(BUILD_MEDIA_LAYER_CORE AND NOT MCPI_HEADLESS_MODE AND MCPI_USE_GLES1_COMPATIBILITY_LAYER)
+if(BUILD_MEDIA_LAYER_CORE AND MCPI_USE_GLES1_COMPATIBILITY_LAYER)
add_subdirectory(gles-compatibility-layer)
endif()
# UTF8-CPP
diff --git a/dependencies/minecraft-pi/CMakeLists.txt b/dependencies/minecraft-pi/CMakeLists.txt
index 76017fb4..b7c4f724 100644
--- a/dependencies/minecraft-pi/CMakeLists.txt
+++ b/dependencies/minecraft-pi/CMakeLists.txt
@@ -17,13 +17,5 @@ install(
DESTINATION "${MCPI_INSTALL_DIR}/game"
USE_SOURCE_PERMISSIONS
REGEX "api" EXCLUDE
- REGEX "data" EXCLUDE
)
-if(NOT MCPI_HEADLESS_MODE)
- install(
- DIRECTORY "${minecraft-pi_SOURCE_DIR}/data/"
- DESTINATION "${MCPI_INSTALL_DIR}/game/data"
- USE_SOURCE_PERMISSIONS
- )
-endif()
install_symlink("game/minecraft-pi" "${MCPI_INSTALL_DIR}/minecraft-pi")
diff --git a/dependencies/runtime/src b/dependencies/runtime/src
index 790e7918..aa874884 160000
--- a/dependencies/runtime/src
+++ b/dependencies/runtime/src
@@ -1 +1 @@
-Subproject commit 790e7918b1d63102b7a7f39dc1db006b2a5abf48
+Subproject commit aa874884072a700750956e241c7a1ce91dbfa74c
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index 6e876006..e6ce50d2 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -32,6 +32,8 @@
* Fix Furnace Visual Bug When Using Lava Bucket As Fuel
* Add Splash Text To Start Screen
* `overwrite_calls` Now Scans VTables
+* Unify Server/Client Builds
+* Use `|` For Separator In `servers.txt` Instead Of `:`
**2.5.3**
* Add `Replace Block Highlight With Outline` Feature Flag (Enabled By Default)
diff --git a/docs/DEDICATED_SERVER.md b/docs/DEDICATED_SERVER.md
index 5b703458..c7218515 100644
--- a/docs/DEDICATED_SERVER.md
+++ b/docs/DEDICATED_SERVER.md
@@ -4,11 +4,11 @@ The dedicated server is a version of Minecraft: Pi Edition modified to run in a
This server is also compatible with MCPE Alpha v0.6.1[^1].
## Setup
-To use, install and run the `minecraft-pi-reborn-server` AppImage. It will generate the world and `server.properties` in the current directory.
+To use, run the normal AppImage with the `--server` argument. It will generate the world and `server.properties` in the current directory.
## Server Limitations
* Player data is not saved because of limitations with MCPE LAN worlds
* An easy workaround is to place your inventory in a chest before logging off
-* Survival Mode servers are incompatible with unmodded MCPI
+* Survival Mode servers are incompatible with un-modded MCPI
-[^1]: The exception to this is buckets, those will crash MCPE players.
+[^1]: The exception to this is buckets and other modded items, those will crash MCPE players.
diff --git a/docs/GETTING_STARTED.md b/docs/GETTING_STARTED.md
index 0c931378..b3e18ac4 100644
--- a/docs/GETTING_STARTED.md
+++ b/docs/GETTING_STARTED.md
@@ -27,7 +27,7 @@ The AppImage requires Debian Bullseye or higher. This is equivalent to Ubuntu 20
It also requires some additional packages. To install them, run:
```sh
-sudo apt install -y libfuse2 libgtk-3-0 libopenal1
+sudo apt install -y libfuse2 libgtk-3-0 libopenal1 libglib2.0-0
```
diff --git a/example-mods/README.md b/example-mods/README.md
index 223da960..11e1c79a 100644
--- a/example-mods/README.md
+++ b/example-mods/README.md
@@ -1,19 +1,11 @@
# Example Mods
-This is an example of a mod that can be built using the modding SDK.
+These are example mods that can be built using the modding SDK.
* **Expanded Creative Mod**: This specific mod adds even more items and blocks to the Creative Inventory. It was originally by [@Bigjango13](https://github.com/bigjango13).
-* **Chat Commands Mod**: This specific mod makes an chat message starting with a ``/`` handled by the MCPI API.
+* **Chat Commands Mod**: This specific mod makes a chat message starting with `/` handled by the MCPI API.
* **Recipes Mod**: This specific mod demos custom recipes.
## The SDK
The modding SDK is a collection of exported CMake targets that allows anyone to create their own MCPI mod!
-The SDK is copied to ``~/.minecraft-pi/sdk/lib/minecraft-pi-reborn-client/sdk/sdk.cmake`` whenever MCPI-Reborn is started.
-
-## How do I use this?
-```sh
-mkdir build
-cd build
-cmake ..
-cp libexpanded-creative.so ~/.minecraft-pi/mods
-```
+The SDK is copied to `~/.minecraft-pi/sdk` whenever MCPI-Reborn is started.
diff --git a/example-mods/chat-commands/CMakeLists.txt b/example-mods/chat-commands/CMakeLists.txt
index 90a8e939..17f2939a 100644
--- a/example-mods/chat-commands/CMakeLists.txt
+++ b/example-mods/chat-commands/CMakeLists.txt
@@ -10,7 +10,7 @@ set(CMAKE_SYSTEM_PROCESSOR "arm")
project(chat-commands)
# Include SDK
-include("$ENV{HOME}/.minecraft-pi/sdk/lib/minecraft-pi-reborn-client/sdk/sdk.cmake")
+include("$ENV{HOME}/.minecraft-pi/sdk/lib/minecraft-pi-reborn/sdk/sdk.cmake")
# Build
add_library(chat-commands SHARED chat-commands.cpp)
diff --git a/example-mods/expanded-creative/CMakeLists.txt b/example-mods/expanded-creative/CMakeLists.txt
index 524b11da..0f21c3fe 100644
--- a/example-mods/expanded-creative/CMakeLists.txt
+++ b/example-mods/expanded-creative/CMakeLists.txt
@@ -10,7 +10,7 @@ set(CMAKE_SYSTEM_PROCESSOR "arm")
project(expanded-creative)
# Include SDK
-include("$ENV{HOME}/.minecraft-pi/sdk/lib/minecraft-pi-reborn-client/sdk/sdk.cmake")
+include("$ENV{HOME}/.minecraft-pi/sdk/lib/minecraft-pi-reborn/sdk/sdk.cmake")
# Build
add_library(expanded-creative SHARED expanded-creative.cpp)
diff --git a/example-mods/recipes/CMakeLists.txt b/example-mods/recipes/CMakeLists.txt
index b1be8ad9..4daf77e7 100644
--- a/example-mods/recipes/CMakeLists.txt
+++ b/example-mods/recipes/CMakeLists.txt
@@ -10,7 +10,7 @@ set(CMAKE_SYSTEM_PROCESSOR "arm")
project(recipes)
# Include SDK
-include("$ENV{HOME}/.minecraft-pi/sdk/lib/minecraft-pi-reborn-client/sdk/sdk.cmake")
+include("$ENV{HOME}/.minecraft-pi/sdk/lib/minecraft-pi-reborn/sdk/sdk.cmake")
# Build
add_library(recipes SHARED recipes.cpp)
diff --git a/images/CMakeLists.txt b/images/CMakeLists.txt
index c5dc5c71..efd6902b 100644
--- a/images/CMakeLists.txt
+++ b/images/CMakeLists.txt
@@ -1,21 +1,17 @@
project(images)
# Title Background
-if(NOT MCPI_HEADLESS_MODE)
- install(
- FILES "background.png"
- DESTINATION "${MCPI_INSTALL_DIR}/data/images/gui"
- RENAME "titleBG.png"
- )
-endif()
+install(
+ FILES "background.png"
+ DESTINATION "${MCPI_INSTALL_DIR}/data/images/gui"
+ RENAME "titleBG.png"
+)
# Chest Model
-if(NOT MCPI_HEADLESS_MODE)
- install(
- FILES "chest.png"
- DESTINATION "${MCPI_INSTALL_DIR}/data/images/item"
- )
-endif()
+install(
+ FILES "chest.png"
+ DESTINATION "${MCPI_INSTALL_DIR}/data/images/item"
+)
# Icon
install(
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index 49c4360b..f5d8afed 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -10,15 +10,11 @@ add_executable(launcher
src/mods.cpp
src/options/parser.cpp
src/main.cpp
+ src/client/configuration.cpp
+ src/client/cache.cpp
+ src/client/available-feature-flags # Show In IDE
)
-if(NOT MCPI_SERVER_MODE)
- embed_resource(launcher src/client/available-feature-flags)
- target_sources(launcher PRIVATE
- src/client/configuration.cpp
- src/client/cache.cpp
- src/client/available-feature-flags # Show In IDE
- )
-endif()
+embed_resource(launcher src/client/available-feature-flags)
target_link_libraries(launcher reborn-util LIB_LIEF trampoline-headers)
# RPath
set_target_properties(launcher PROPERTIES INSTALL_RPATH "$ORIGIN/lib/native")
@@ -38,18 +34,10 @@ file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/launcher.desktop"
"Type=Application\n"
"Categories=Game;\n"
)
-if(MCPI_HEADLESS_MODE)
- file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/launcher.desktop"
- "Terminal=true\n"
- "NoDisplay=true\n"
- )
-else()
- file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/launcher.desktop"
- "Terminal=false\n"
- "StartupNotify=false\n"
- "StartupWMClass=${MCPI_APP_ID}\n"
- )
-endif()
+file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/launcher.desktop"
+ "Terminal=true\n"
+ "NoDisplay=true\n"
+)
install(
FILES "${CMAKE_CURRENT_BINARY_DIR}/launcher.desktop"
DESTINATION "${MCPI_SHARE_DIR}/applications"
@@ -100,17 +88,11 @@ file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/appstream.xml"
" \n"
" \n"
" \n"
-)
-if(NOT MCPI_HEADLESS_MODE)
- file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/appstream.xml"
- " \n"
- " \n"
- " https://gitea.thebrokenrail.com/TheBrokenRail/minecraft-pi-reborn/raw/branch/master/images/start.png\n"
- " \n"
- " \n"
- )
-endif()
-file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/appstream.xml"
+ " \n"
+ " \n"
+ " https://gitea.thebrokenrail.com/TheBrokenRail/minecraft-pi-reborn/raw/branch/master/images/start.png\n"
+ " \n"
+ " \n"
"\n"
)
install(
diff --git a/launcher/src/client/cache.cpp b/launcher/src/client/cache.cpp
index ad116810..a81259fa 100644
--- a/launcher/src/client/cache.cpp
+++ b/launcher/src/client/cache.cpp
@@ -17,7 +17,7 @@ static std::string get_cache_path() {
if (home == nullptr) {
IMPOSSIBLE();
}
- return std::string(home) + HOME_SUBDIRECTORY_FOR_GAME_DATA "/.launcher-cache";
+ return std::string(home) + get_home_subdirectory_for_game_data() + "/.launcher-cache";
}
// Load
@@ -165,7 +165,8 @@ void wipe_cache() {
INFO("Wiping Launcher Cache...");
// Unlink File
- if (unlink(get_cache_path().c_str()) != 0) {
+ const int ret = unlink(get_cache_path().c_str());
+ if (ret != 0 && errno != ENOENT) {
WARN("Failure While Wiping Cache: %s", strerror(errno));
}
}
diff --git a/launcher/src/client/configuration.cpp b/launcher/src/client/configuration.cpp
index 4b9416cc..45c1007c 100644
--- a/launcher/src/client/configuration.cpp
+++ b/launcher/src/client/configuration.cpp
@@ -108,6 +108,7 @@ static void run_command_and_set_env(const char *env_name, const char *command[])
// Use Zenity To Set Environmental Variable
#define DIALOG_TITLE "Launcher"
static void run_zenity_and_set_env(const char *env_name, std::vector command) {
+ reborn_check_display();
// Create Full Command
std::vector full_command;
full_command.push_back("zenity");
@@ -146,31 +147,16 @@ void handle_non_launch_client_only_commands(const options_t &options) {
});
exit(EXIT_SUCCESS);
}
-}
-
-// Check Environment
-void check_environment_client() {
- // Don't Run As Root
- if (getenv("_MCPI_SKIP_ROOT_CHECK") == nullptr && (getuid() == 0 || geteuid() == 0)) {
- ERR("Don't Run As Root");
+ // Wipe Cache If Needed
+ if (options.wipe_cache) {
+ wipe_cache();
+ exit(EXIT_SUCCESS);
}
-
- // Check For Display
-#ifndef MCPI_HEADLESS_MODE
- if (getenv("DISPLAY") == nullptr && getenv("WAYLAND_DISPLAY") == nullptr) {
- ERR("No display attached! Make sure $DISPLAY or $WAYLAND_DISPLAY is set.");
- }
-#endif
}
// Configure Client Options
#define LIST_DIALOG_SIZE "400"
void configure_client(const options_t &options) {
- // Wipe Cache If Needed
- if (options.wipe_cache) {
- wipe_cache();
- }
-
// Load Cache
launcher_cache cache = options.no_cache ? empty_cache : load_cache();
diff --git a/launcher/src/client/configuration.h b/launcher/src/client/configuration.h
index eaa5afe7..e7c20cea 100644
--- a/launcher/src/client/configuration.h
+++ b/launcher/src/client/configuration.h
@@ -16,8 +16,5 @@ void load_available_feature_flags(const std::function &callba
// Handle Non-Launch Commands
void handle_non_launch_client_only_commands(const options_t &options);
-// Check Environment
-void check_environment_client();
-
// Configure Client Options
void configure_client(const options_t &options);
\ No newline at end of file
diff --git a/launcher/src/crash-report.c b/launcher/src/crash-report.c
index 04ba79ec..fe5fd6fb 100644
--- a/launcher/src/crash-report.c
+++ b/launcher/src/crash-report.c
@@ -15,7 +15,6 @@
#include "crash-report.h"
// Show Crash Report Dialog
-#ifndef MCPI_HEADLESS_MODE
#define DIALOG_TITLE "Crash Report"
#define CRASH_REPORT_DIALOG_WIDTH "640"
#define CRASH_REPORT_DIALOG_HEIGHT "480"
@@ -35,7 +34,7 @@ static void show_report(const char *log_filename) {
"--width", CRASH_REPORT_DIALOG_WIDTH,
"--height", CRASH_REPORT_DIALOG_HEIGHT,
"--text-info",
- "--text", MCPI_APP_BASE_TITLE " has crashed!\n\nNeed help? Consider asking on the Discord server! If you believe this is a problem with " MCPI_APP_BASE_TITLE " itself, please upload this crash report to the #bugs Discord channel.",
+ "--text", MCPI_APP_TITLE " has crashed!\n\nNeed help? Consider asking on the Discord server! If you believe this is a problem with " MCPI_APP_TITLE " itself, please upload this crash report to the #bugs Discord channel.",
"--filename", log_filename,
"--no-wrap",
"--font", "Monospace",
@@ -46,7 +45,6 @@ static void show_report(const char *log_filename) {
safe_execvpe(command, (const char *const *) environ);
}
}
-#endif
// Exit Handler
static void exit_handler(__attribute__((unused)) int signal) {
@@ -248,11 +246,9 @@ void setup_crash_report() {
unsetenv(MCPI_LOG_ENV);
// Show Crash Log
-#ifndef MCPI_HEADLESS_MODE
- if (is_crash) {
+ if (is_crash && !reborn_is_headless()) {
show_report(log_filename);
}
-#endif
// Exit
exit(WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE);
diff --git a/launcher/src/main.cpp b/launcher/src/main.cpp
index b48eb8ad..084488a0 100644
--- a/launcher/src/main.cpp
+++ b/launcher/src/main.cpp
@@ -6,9 +6,7 @@
#include "options/parser.h"
#include "crash-report.h"
#include "util.h"
-#ifndef MCPI_SERVER_MODE
#include "client/configuration.h"
-#endif
// Bind Options To Environmental Variable
static void bind_to_env(const char *env, const bool value) {
@@ -19,16 +17,14 @@ static void bind_to_env(const char *env, const bool value) {
}
static void setup_environment(const options_t &options) {
// Passthrough Options To Game
-#ifndef MCPI_SERVER_MODE
+ bind_to_env(MCPI_SERVER_MODE_ENV, options.server_mode);
bind_to_env("_MCPI_BENCHMARK", options.benchmark);
-#else
bind_to_env("_MCPI_ONLY_GENERATE", options.only_generate);
-#endif
+ bind_to_env(MCPI_FORCE_HEADLESS_ENV, options.force_headless);
+ bind_to_env(MCPI_FORCE_NON_HEADLESS_ENV, options.force_non_headless);
// GTK Dark Mode
-#ifndef MCPI_HEADLESS_MODE
set_and_print_env("GTK_THEME", "Adwaita:dark");
-#endif
// Configure PATH
{
@@ -72,9 +68,6 @@ static void start_game(const options_t &options) {
// Disable stdout Buffering
setvbuf(stdout, nullptr, _IONBF, 0);
- // Environemntal Variable Options
- setup_environment(options);
-
// Setup Crash Reporting
if (!options.disable_crash_report) {
setup_log_file();
@@ -92,35 +85,35 @@ static void start_game(const options_t &options) {
sigaction(SIGTERM, &act_sigterm, nullptr);
// Setup Home
-#ifndef MCPI_SERVER_MODE
- // Ensure $HOME
- const char *home = getenv("HOME");
- if (home == nullptr) {
- ERR("$HOME Isn't Set");
- }
- // Create If Needed
- {
- std::string minecraft_folder = std::string(home) + HOME_SUBDIRECTORY_FOR_GAME_DATA;
- struct stat tmp_stat = {};
- bool exists = stat(minecraft_folder.c_str(), &tmp_stat) != 0 ? false : S_ISDIR(tmp_stat.st_mode);
- if (!exists) {
- // Doesn't Exist
- if (mkdir(minecraft_folder.c_str(), S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
- ERR("Unable To Create Data Directory: %s", strerror(errno));
+ if (!reborn_is_server()) {
+ // Ensure $HOME
+ const char *home = getenv("HOME");
+ if (home == nullptr) {
+ ERR("$HOME Is Not Set");
+ }
+ // Create If Needed
+ {
+ std::string minecraft_folder = std::string(home) + get_home_subdirectory_for_game_data();
+ struct stat tmp_stat = {};
+ bool exists = stat(minecraft_folder.c_str(), &tmp_stat) != 0 ? false : S_ISDIR(tmp_stat.st_mode);
+ if (!exists) {
+ // Doesn't Exist
+ if (mkdir(minecraft_folder.c_str(), S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
+ ERR("Unable To Create Data Directory: %s", strerror(errno));
+ }
}
}
+ } else {
+ // Set Home To Current Directory, So World Data Is Stored There
+ char *launch_directory = getcwd(nullptr, 0);
+ set_and_print_env("HOME", launch_directory);
+ free(launch_directory);
}
-#else
- // Set Home To Current Directory, So World Data Is Stored There
- char *launch_directory = getcwd(NULL, 0);
- set_and_print_env("HOME", launch_directory);
- free(launch_directory);
-#endif
// Configure Client Options
-#ifndef MCPI_SERVER_MODE
- configure_client(options);
-#endif
+ if (!reborn_is_server()) {
+ configure_client(options);
+ }
// Bootstrap
bootstrap();
@@ -138,17 +131,12 @@ int main(int argc, char *argv[]) {
unsetenv(MCPI_LOG_ENV);
bind_to_env(MCPI_DEBUG_ENV, options.debug);
+ // Setup Environment
+ setup_environment(options);
+
// Handle Non-Launch Commands (Copy SDK, Print Feature Flags, Etc)
handle_non_launch_commands(options);
-#ifndef MCPI_SERVER_MODE
handle_non_launch_client_only_commands(options);
-#endif
-
- // Check Environment
-#ifndef MCPI_SERVER_MODE
- // Code After This Can Safely Open A Window
- check_environment_client();
-#endif
// Start The Game
start_game(options);
diff --git a/launcher/src/mods.cpp b/launcher/src/mods.cpp
index 2ad3d396..11932ca1 100644
--- a/launcher/src/mods.cpp
+++ b/launcher/src/mods.cpp
@@ -60,7 +60,7 @@ std::string bootstrap_mods(const std::string &binary_directory) {
// ~/.minecraft-pi/mods
{
// Get Mods Folder
- std::string mods_folder = std::string(getenv("HOME")) + HOME_SUBDIRECTORY_FOR_GAME_DATA SUBDIRECTORY_FOR_MODS;
+ std::string mods_folder = std::string(getenv("HOME")) + get_home_subdirectory_for_game_data() + SUBDIRECTORY_FOR_MODS;
// Load Mods From ./mods
load(preload, mods_folder);
}
diff --git a/launcher/src/options/option-list.h b/launcher/src/options/option-list.h
index b69d64e1..315688bc 100644
--- a/launcher/src/options/option-list.h
+++ b/launcher/src/options/option-list.h
@@ -1,12 +1,12 @@
OPTION(debug, "debug", 'd', "Enable Debug Logging (" MCPI_DEBUG_ENV ")")
OPTION(copy_sdk, "copy-sdk", -2, "Extract Modding SDK And Exit")
OPTION(disable_crash_report, "disable-crash-report", -1, "Disable Crash Report Dialog")
-#ifndef MCPI_SERVER_MODE
-OPTION(use_default, "default", -3, "Skip Configuration Dialogs")
-OPTION(no_cache, "no-cache", -4, "Disable Configuration Cache")
-OPTION(wipe_cache, "wipe-cache", -5, "Wipe Cached Configuration")
-OPTION(print_available_feature_flags, "print-available-feature-flags", -6, "Print Available Feature Flags")
-OPTION(benchmark, "benchmark", -7, "Run Benchmark")
-#else
-OPTION(only_generate, "only-generate", -8, "Generate World And Exit")
-#endif
\ No newline at end of file
+OPTION(use_default, "default", -3, "Skip Client-Mode Configuration Dialogs")
+OPTION(no_cache, "no-cache", -4, "Disable Client-Mode Configuration Cache")
+OPTION(wipe_cache, "wipe-cache", -5, "Wipe Cached Client-Mode Configuration And Exit")
+OPTION(print_available_feature_flags, "print-available-feature-flags", -6, "Print Available Client-Mode Feature Flags")
+OPTION(benchmark, "benchmark", -7, "Run Client-Mode Benchmark")
+OPTION(only_generate, "only-generate", -8, "Generate World And Exit (Server-Mode Only)")
+OPTION(force_headless, "force-headless", -9, "Force Disable Game Rendering")
+OPTION(force_non_headless, "force-non-headless", -10, "Force Enable Game Rendering")
+OPTION(server_mode, "server", -11, "Run In Server-Mode")
\ No newline at end of file
diff --git a/launcher/src/sdk.cpp b/launcher/src/sdk.cpp
index cd3b1bf4..2dde7e99 100644
--- a/launcher/src/sdk.cpp
+++ b/launcher/src/sdk.cpp
@@ -14,7 +14,7 @@
}
// Copy SDK Into ~/.minecraft-pi
-#define HOME_SUBDIRECTORY_FOR_SDK HOME_SUBDIRECTORY_FOR_GAME_DATA "/sdk"
+#define HOME_SUBDIRECTORY_FOR_SDK (std::string(get_home_subdirectory_for_game_data()) + "/sdk")
void copy_sdk(const std::string &binary_directory, const bool log_with_debug) {
// Ensure SDK Directory
std::string sdk_path;
diff --git a/libreborn/include/libreborn/config.h.in b/libreborn/include/libreborn/config.h.in
index dab2a3ee..519c3f21 100644
--- a/libreborn/include/libreborn/config.h.in
+++ b/libreborn/include/libreborn/config.h.in
@@ -1,7 +1,5 @@
#pragma once
-#cmakedefine MCPI_SERVER_MODE
-#cmakedefine MCPI_HEADLESS_MODE
#cmakedefine MCPI_IS_APPIMAGE_BUILD
#cmakedefine MCPI_IS_FLATPAK_BUILD
#cmakedefine MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
diff --git a/libreborn/include/libreborn/home.h b/libreborn/include/libreborn/home.h
deleted file mode 100644
index 65da96bc..00000000
--- a/libreborn/include/libreborn/home.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#pragma once
-
-// Minecraft Pi Game Data Root
-#ifndef MCPI_SERVER_MODE
-// Store Game Data In "~/.minecraft-pi" Instead Of "~/.minecraft" To Avoid Conflicts
-#define HOME_SUBDIRECTORY_FOR_GAME_DATA "/.minecraft-pi"
-#else
-// Store Game Data In $HOME Root (In Server Mode, $HOME Is Changed To The Launch Directory)
-#define HOME_SUBDIRECTORY_FOR_GAME_DATA ""
-#endif
diff --git a/libreborn/include/libreborn/libreborn.h b/libreborn/include/libreborn/libreborn.h
index 4ef7cba7..c9068b4b 100644
--- a/libreborn/include/libreborn/libreborn.h
+++ b/libreborn/include/libreborn/libreborn.h
@@ -5,5 +5,4 @@
#include "util.h"
#include "string.h"
#include "exec.h"
-#include "home.h"
#include "patch.h"
diff --git a/libreborn/include/libreborn/util.h b/libreborn/include/libreborn/util.h
index d4960a7d..c387a7d1 100644
--- a/libreborn/include/libreborn/util.h
+++ b/libreborn/include/libreborn/util.h
@@ -51,10 +51,19 @@ int lock_file(const char *file);
void unlock_file(const char *file, int fd);
// Access Configuration At Runtime
+#define MCPI_SERVER_MODE_ENV "_MCPI_SERVER_MODE"
+#define MCPI_FORCE_HEADLESS_ENV "_MCPI_FORCE_HEADLESS"
+#define MCPI_FORCE_NON_HEADLESS_ENV "_MCPI_FORCE_NON_HEADLESS"
const char *reborn_get_version();
int reborn_is_headless();
int reborn_is_server();
+// Check $DISPLAY
+void reborn_check_display();
+
+// Get Home Subdirectory
+const char *get_home_subdirectory_for_game_data();
+
// Customize VTable
#define CUSTOM_VTABLE(name, parent) \
void _setup_##name##_vtable(parent##_vtable *vtable); \
diff --git a/libreborn/src/util/util.c b/libreborn/src/util/util.c
index 6a37b7bb..e45e2507 100644
--- a/libreborn/src/util/util.c
+++ b/libreborn/src/util/util.c
@@ -50,16 +50,43 @@ const char *reborn_get_version() {
return MCPI_VERSION;
}
int reborn_is_headless() {
-#ifdef MCPI_HEADLESS_MODE
- return 1;
-#else
- return 0;
-#endif
+ static int ret;
+ static int is_set = 0;
+ if (!is_set) {
+ ret = reborn_is_server();
+ if (getenv(MCPI_FORCE_HEADLESS_ENV)) {
+ ret = 1;
+ } else if (getenv(MCPI_FORCE_NON_HEADLESS_ENV)) {
+ ret = 0;
+ }
+ is_set = 1;
+ }
+ return ret;
}
int reborn_is_server() {
-#ifdef MCPI_SERVER_MODE
- return 1;
-#else
- return 0;
-#endif
+ static int ret;
+ static int is_set = 0;
+ if (!is_set) {
+ ret = getenv(MCPI_SERVER_MODE_ENV) != NULL;
+ is_set = 1;
+ }
+ return ret;
}
+
+// Check $DISPLAY
+void reborn_check_display() {
+ if (!getenv("DISPLAY") && !getenv("WAYLAND_DISPLAY")) {
+ ERR("No display attached! Make sure $DISPLAY or $WAYLAND_DISPLAY is set.");
+ }
+}
+
+// Home Subdirectory
+const char *get_home_subdirectory_for_game_data() {
+ if (!reborn_is_server()) {
+ // Store Game Data In "~/.minecraft-pi" Instead Of "~/.minecraft" To Avoid Conflicts
+ return "/.minecraft-pi";
+ } else {
+ // Store Game Data In $HOME Root (In Server Mode, $HOME Is Changed To The Launch Directory)
+ return "";
+ }
+}
\ No newline at end of file
diff --git a/media-layer/core/CMakeLists.txt b/media-layer/core/CMakeLists.txt
index 6d66d9b7..c6c2faf8 100644
--- a/media-layer/core/CMakeLists.txt
+++ b/media-layer/core/CMakeLists.txt
@@ -1,17 +1,17 @@
project(media-layer-core)
# OpenGL
-if(NOT MCPI_HEADLESS_MODE)
- add_subdirectory(gles)
-endif()
+add_subdirectory(gles)
-# Configuration
-set(CORE_SRC src/base.cpp src/media.c $) # SDL Re-Implementation Using GLFW
-if(NOT MCPI_HEADLESS_MODE)
- list(APPEND CORE_SRC src/audio/api.cpp src/audio/engine.c src/audio/file.cpp)
-else()
- list(APPEND CORE_SRC src/audio/stubs.c)
-endif()
+# SDL Re-Implementation Using GLFW
+set(CORE_SRC
+ src/base.cpp
+ src/media.cpp
+ src/audio/api.cpp
+ src/audio/engine.c
+ src/audio/file.cpp
+ $
+)
# Build
add_library(media-layer-core-real SHARED ${CORE_SRC}) # Dependencies Are Setup Later
@@ -23,10 +23,14 @@ endif()
install(TARGETS media-layer-core-real DESTINATION "${MCPI_LIB_DIR}")
# Link
-target_link_libraries(media-layer-core-real PUBLIC media-layer-headers PUBLIC reborn-util PUBLIC dl)
-if(NOT MCPI_HEADLESS_MODE)
- # OpenAL
- find_library(OPENAL_LIBRARY NAMES openal REQUIRED)
- # Link
- target_link_libraries(media-layer-core-real PRIVATE "${OPENAL_LIBRARY}" PRIVATE m PRIVATE glfw PUBLIC GLESv1_CM PRIVATE LIB_LIEF)
-endif()
+find_library(OPENAL_LIBRARY NAMES openal REQUIRED)
+target_link_libraries(media-layer-core-real
+ PUBLIC media-layer-headers
+ PUBLIC reborn-util
+ PRIVATE "${OPENAL_LIBRARY}"
+ PRIVATE m
+ PRIVATE glfw
+ PUBLIC GLESv1_CM
+ PRIVATE LIB_LIEF
+ PUBLIC dl
+)
diff --git a/media-layer/core/src/audio/stubs.c b/media-layer/core/src/audio/stubs.c
deleted file mode 100644
index 2db851b6..00000000
--- a/media-layer/core/src/audio/stubs.c
+++ /dev/null
@@ -1,6 +0,0 @@
-#include
-
-void media_audio_update(__attribute__((unused)) float volume, __attribute__((unused)) float x, __attribute__((unused)) float y, __attribute__((unused)) float z, __attribute__((unused)) float yaw) {
-}
-void media_audio_play(__attribute__((unused)) const char *source, __attribute__((unused)) const char *name, __attribute__((unused)) float x, __attribute__((unused)) float y, __attribute__((unused)) float z, __attribute__((unused)) float pitch, __attribute__((unused)) float volume, __attribute__((unused)) int is_ui) {
-}
diff --git a/media-layer/core/src/media.c b/media-layer/core/src/media.c
deleted file mode 100644
index 6adb3761..00000000
--- a/media-layer/core/src/media.c
+++ /dev/null
@@ -1,861 +0,0 @@
-#include
-
-#include
-#include
-
-#ifndef MCPI_HEADLESS_MODE
-#include
-
-#define GLFW_INCLUDE_NONE
-#include
-#endif
-
-#include
-#include
-
-#ifndef MCPI_HEADLESS_MODE
-#include "audio/engine.h"
-#endif
-
-// Allow Disabling Interaction
-static void update_cursor();
-#ifndef MCPI_HEADLESS_MODE
-static void emit_events_after_is_interactable_change();
-#endif
-static int is_interactable = 1;
-void media_set_interactable(int toggle) {
- if (toggle != is_interactable) {
- is_interactable = toggle;
- update_cursor();
-#ifndef MCPI_HEADLESS_MODE
- emit_events_after_is_interactable_change();
-#endif
- }
-}
-
-// Track Media Layer State
-static volatile int is_running = 0;
-
-// Store Cursor State
-static int cursor_grabbed = 0;
-static int cursor_visible = 1;
-
-// Track If Raw Mouse Motion Is Enabled
-static int raw_mouse_motion_enabled = 1;
-
-// GLFW Code Not Needed In Headless Mode
-#ifndef MCPI_HEADLESS_MODE
-
-static GLFWwindow *glfw_window = NULL;
-
-// Handle GLFW Error
-static void glfw_error(__attribute__((unused)) int error, const char *description) {
- WARN("GLFW Error: %s", description);
-}
-
-// Pass Character Event
-static void character_event(char c) {
- // SDL_UserEvent Is Never Used In MCPI, So It Is Repurposed For Character Events
- SDL_Event event;
- event.type = SDL_USEREVENT;
- event.user.code = USER_EVENT_CHARACTER;
- event.user.data1 = (int) c;
- SDL_PushEvent(&event);
-}
-
-// Convert GLFW Key To SDL Key
-#define IMAGINARY_GLFW_CRAFTING_KEY GLFW_KEY_LAST
-static SDLKey glfw_key_to_sdl_key(int key) {
- switch (key) {
- // Movement
- case GLFW_KEY_W:
- return SDLK_w;
- case GLFW_KEY_A:
- return SDLK_a;
- case GLFW_KEY_S:
- return SDLK_s;
- case GLFW_KEY_D:
- return SDLK_d;
- case GLFW_KEY_SPACE:
- return SDLK_SPACE;
- case GLFW_KEY_LEFT_SHIFT:
- return SDLK_LSHIFT;
- case GLFW_KEY_RIGHT_SHIFT:
- return SDLK_RSHIFT;
- // Inventory
- case GLFW_KEY_E:
- return SDLK_e;
- // Drop Item
- case GLFW_KEY_Q:
- return SDLK_q;
- // Toolbar
- case GLFW_KEY_1:
- return SDLK_1;
- case GLFW_KEY_2:
- return SDLK_2;
- case GLFW_KEY_3:
- return SDLK_3;
- case GLFW_KEY_4:
- return SDLK_4;
- case GLFW_KEY_5:
- return SDLK_5;
- case GLFW_KEY_6:
- return SDLK_6;
- case GLFW_KEY_7:
- return SDLK_7;
- case GLFW_KEY_8:
- return SDLK_8;
- case GLFW_KEY_9:
- return SDLK_9;
- case GLFW_KEY_0:
- return SDLK_0;
- // UI Control
- case GLFW_KEY_ESCAPE:
- return SDLK_ESCAPE;
- case GLFW_KEY_UP:
- return SDLK_UP;
- case GLFW_KEY_DOWN:
- return SDLK_DOWN;
- case GLFW_KEY_LEFT:
- return SDLK_LEFT;
- case GLFW_KEY_RIGHT:
- return SDLK_RIGHT;
- case GLFW_KEY_TAB:
- return SDLK_TAB;
- case GLFW_KEY_ENTER:
- return SDLK_RETURN;
- case GLFW_KEY_BACKSPACE:
- return SDLK_BACKSPACE;
- case GLFW_KEY_DELETE:
- return SDLK_DELETE;
- // Fullscreen
- case GLFW_KEY_F11:
- return SDLK_F11;
- // Screenshot
- case GLFW_KEY_F2:
- return SDLK_F2;
- // Hide GUI
- case GLFW_KEY_F1:
- return SDLK_F1;
- // Third Person
- case GLFW_KEY_F5:
- return SDLK_F5;
- // Chat
- case GLFW_KEY_T:
- return SDLK_t;
- // Crafting
- case IMAGINARY_GLFW_CRAFTING_KEY:
- return SDLK_WORLD_0;
- // Unknown
- default:
- return SDLK_UNKNOWN;
- }
-}
-
-// Convert GLFW Key Modifier To SDL Key Modifier
-static SDLMod glfw_modifier_to_sdl_modifier(int mods) {
- SDLMod ret = KMOD_NONE;
- // Control
- if ((mods & GLFW_MOD_CONTROL) != 0) {
- ret |= KMOD_CTRL;
- }
- // Shift
- if ((mods & GLFW_MOD_SHIFT) != 0) {
- ret |= KMOD_SHIFT;
- }
- // Alt
- if ((mods & GLFW_MOD_ALT) != 0) {
- ret |= KMOD_ALT;
- }
- // Return
- return ret;
-}
-
-// Pass Key Presses To SDL
-static void glfw_key_raw(int key, int scancode, int action, int mods) {
- SDL_Event event1;
- int up = action == GLFW_RELEASE;
- event1.type = up ? SDL_KEYUP : SDL_KEYDOWN;
- event1.key.state = up ? SDL_RELEASED : SDL_PRESSED;
- event1.key.keysym.scancode = scancode;
- event1.key.keysym.mod = glfw_modifier_to_sdl_modifier(mods);
- event1.key.keysym.sym = glfw_key_to_sdl_key(key);
- SDL_PushEvent(&event1);
- // Allow MCPI To Access Original GLFW Keycode
- SDL_Event event2;
- event2.type = SDL_USEREVENT;
- event2.user.code = USER_EVENT_REAL_KEY;
- event2.user.data1 = event1.key.state;
- event2.user.data2 = key;
- SDL_PushEvent(&event2);
-}
-static void glfw_key(__attribute__((unused)) GLFWwindow *window, int key, int scancode, int action, int mods) {
- if (is_interactable) {
- glfw_key_raw(key, scancode, action, mods);
- }
-}
-
-// Pass Text To Minecraft
-static void codepoint_to_utf8(unsigned char *const buffer, const unsigned int code) {
- // https://stackoverflow.com/a/42013433/16198887
- if (code <= 0x7f) {
- buffer[0] = code;
- } else if (code <= 0x7ff) {
- buffer[0] = 0xc0 | (code >> 6); // 110xxxxx
- buffer[1] = 0x80 | (code & 0x3f); // 10xxxxxx
- } else if (code <= 0xffff) {
- buffer[0] = 0xe0 | (code >> 12); // 1110xxxx
- buffer[1] = 0x80 | ((code >> 6) & 0x3f); // 10xxxxxx
- buffer[2] = 0x80 | (code & 0x3f); // 10xxxxxx
- } else if (code <= 0x10ffff) {
- buffer[0] = 0xf0 | (code >> 18); // 11110xxx
- buffer[1] = 0x80 | ((code >> 12) & 0x3f); // 10xxxxxx
- buffer[2] = 0x80 | ((code >> 6) & 0x3f); // 10xxxxxx
- buffer[3] = 0x80 | (code & 0x3f); // 10xxxxxx
- }
-}
-static void glfw_char(__attribute__((unused)) GLFWwindow *window, unsigned int codepoint) {
- if (is_interactable) {
- // Convert
- size_t str_size = 4 /* Maximum UTF-8 character size */ + 1 /* NULL-terminator */;
- char str[str_size];
- memset(str, 0, str_size);
- codepoint_to_utf8((unsigned char *) str, codepoint);
- char *cp437_str = to_cp437(str);
- // Send Event
- for (int i = 0; cp437_str[i] != '\0'; i++) {
- character_event(cp437_str[i]);
- }
- // Free
- free(cp437_str);
- }
-}
-
-// Last Mouse Location
-static double last_mouse_x = 0;
-static double last_mouse_y = 0;
-// Ignore Relative Cursor Motion
-static int ignore_relative_motion = 0;
-
-// Convert Screen Coordinates To Pixels
-static void convert_to_pixels(GLFWwindow *window, double *xpos, double *ypos) {
- // Skip If Cursor Is Grabbed
- if (cursor_grabbed && raw_mouse_motion_enabled) {
- return;
- }
- // Get Window Size
- int window_width;
- int window_height;
- glfwGetWindowSize(window, &window_width, &window_height);
- // Get Framebuffer Size
- int framebuffer_width;
- int framebuffer_height;
- glfwGetFramebufferSize(window, &framebuffer_width, &framebuffer_height);
- // Calculate Ratios
- double width_ratio = ((double) framebuffer_width) / ((double) window_width);
- double height_ratio = ((double) framebuffer_height) / ((double) window_height);
- // Multiply
- *xpos *= width_ratio;
- *ypos *= height_ratio;
-}
-
-// Pass Mouse Movement To SDL
-static void glfw_motion(__attribute__((unused)) GLFWwindow *window, double xpos, double ypos) {
- convert_to_pixels(window, &xpos, &ypos);
- if (is_interactable) {
- SDL_Event event;
- event.type = SDL_MOUSEMOTION;
- event.motion.x = xpos;
- event.motion.y = ypos;
- event.motion.xrel = !ignore_relative_motion ? (xpos - last_mouse_x) : 0;
- event.motion.yrel = !ignore_relative_motion ? (ypos - last_mouse_y) : 0;
- SDL_PushEvent(&event);
- }
- ignore_relative_motion = 0;
- last_mouse_x = xpos;
- last_mouse_y = ypos;
-}
-
-// Create And Push SDL Mouse Click Event
-static void click_event(int button, int up) {
- SDL_Event event;
- event.type = up ? SDL_MOUSEBUTTONUP : SDL_MOUSEBUTTONDOWN;
- event.button.x = last_mouse_x;
- event.button.y = last_mouse_y;
- event.button.state = up ? SDL_RELEASED : SDL_PRESSED;
- event.button.button = button;
- SDL_PushEvent(&event);
-}
-
-// Pass Mouse Click To SDL
-static void glfw_click_raw(int button, int action) {
- int up = action == GLFW_RELEASE;
- int sdl_button = button == GLFW_MOUSE_BUTTON_RIGHT ? SDL_BUTTON_RIGHT : (button == GLFW_MOUSE_BUTTON_LEFT ? SDL_BUTTON_LEFT : SDL_BUTTON_MIDDLE);
- click_event(sdl_button, up);
-}
-static void glfw_click(__attribute__((unused)) GLFWwindow *window, int button, int action, __attribute__((unused)) int mods) {
- if (is_interactable) {
- glfw_click_raw(button, action);
- }
-}
-
-// Pass Mouse Scroll To SDL
-static void glfw_scroll(__attribute__((unused)) GLFWwindow *window, __attribute__((unused)) double xoffset, double yoffset) {
- if (is_interactable && yoffset != 0) {
- int sdl_button = yoffset > 0 ? SDL_BUTTON_WHEELUP : SDL_BUTTON_WHEELDOWN;
- click_event(sdl_button, 0);
- click_event(sdl_button, 1);
- }
-}
-
-// Controller Events
-static SDLKey glfw_controller_button_to_key(int button) {
- switch (button) {
- // Jump
- case GLFW_GAMEPAD_BUTTON_A:
- return GLFW_KEY_SPACE;
- // Drop Item
- case GLFW_GAMEPAD_BUTTON_DPAD_DOWN:
- return GLFW_KEY_Q;
- // Inventory
- case GLFW_GAMEPAD_BUTTON_Y:
- return GLFW_KEY_E;
- // Third-Person
- case GLFW_GAMEPAD_BUTTON_DPAD_UP:
- return GLFW_KEY_F5;
- // Sneak
- case GLFW_GAMEPAD_BUTTON_B:
- return GLFW_KEY_LEFT_SHIFT;
- // Chat
- case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT:
- return GLFW_KEY_T;
- // Pause
- case GLFW_GAMEPAD_BUTTON_START:
- case GLFW_GAMEPAD_BUTTON_BACK:
- return GLFW_KEY_ESCAPE;
- // Crafting
- case GLFW_GAMEPAD_BUTTON_X:
- return IMAGINARY_GLFW_CRAFTING_KEY;
- // Unknown
- default:
- return GLFW_KEY_UNKNOWN;
- }
-}
-static void glfw_controller_button(int button, int action) {
- int key = glfw_controller_button_to_key(button);
- if (key != GLFW_KEY_UNKNOWN) {
- // Press Key
- glfw_key_raw(key, glfwGetKeyScancode(key), action, 0);
- } else {
- // Scrolling
- if (button == GLFW_GAMEPAD_BUTTON_LEFT_BUMPER) {
- key = SDL_BUTTON_WHEELUP;
- } else if (button == GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER) {
- key = SDL_BUTTON_WHEELDOWN;
- }
- if (key != GLFW_KEY_UNKNOWN) {
- click_event(key, action == GLFW_PRESS);
- }
- }
-}
-
-// Controller Movement Axis
-static int controller_horizontal_key = GLFW_KEY_UNKNOWN;
-static int controller_vertical_key = GLFW_KEY_UNKNOWN;
-static void release_and_press_key(int *old_key, int new_key) {
- if (*old_key != new_key) {
- if (*old_key != GLFW_KEY_UNKNOWN) {
- glfw_key_raw(*old_key, glfwGetKeyScancode(*old_key), GLFW_RELEASE, 0);
- }
- if (new_key != GLFW_KEY_UNKNOWN) {
- glfw_key_raw(new_key, glfwGetKeyScancode(new_key), GLFW_PRESS, 0);
- }
- }
- *old_key = new_key;
-}
-#define verify_controller_axis_value(value, threshold) \
- if ((value < (threshold) && value > 0) || (value > -(threshold) && value < 0)) { \
- value = 0; \
- }
-#define CONTROLLER_MOVEMENT_AXIS_THRESHOLD 0.5f
-static void glfw_controller_movement(float x, float y) {
- // Verify
- verify_controller_axis_value(x, CONTROLLER_MOVEMENT_AXIS_THRESHOLD);
- verify_controller_axis_value(y, CONTROLLER_MOVEMENT_AXIS_THRESHOLD);
- // Horizontal Movement
- if (x > 0) {
- release_and_press_key(&controller_horizontal_key, GLFW_KEY_D);
- } else if (x < 0) {
- release_and_press_key(&controller_horizontal_key, GLFW_KEY_A);
- } else {
- release_and_press_key(&controller_horizontal_key, GLFW_KEY_UNKNOWN);
- }
- // Vertical Movement
- if (y < 0) {
- release_and_press_key(&controller_vertical_key, GLFW_KEY_W);
- } else if (y > 0) {
- release_and_press_key(&controller_vertical_key, GLFW_KEY_S);
- } else {
- release_and_press_key(&controller_vertical_key, GLFW_KEY_UNKNOWN);
- }
-}
-
-// Get Time
-#define NANOSECONDS_IN_SECOND 1000000000ll
-static long long int get_time() {
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
- long long int a = (long long int) ts.tv_nsec;
- long long int b = ((long long int) ts.tv_sec) * NANOSECONDS_IN_SECOND;
- return a + b;
-}
-
-// Controller Look Axis
-#define CONTROLLER_LOOK_EVENT_PERIOD 50000000ll // 1/20 Seconds
-#define CONTROLLER_LOOK_AXIS_THRESHOLD 0.2f
-#define CONTROLLER_LOOK_AXIS_SENSITIVITY 70
-static void glfw_controller_look(float x, float y) {
- // Current Time
- long long int current_time = get_time();
- // Last Time
- static long long int last_time = 0;
- static int is_last_time_set = 0;
- if (!is_last_time_set) {
- is_last_time_set = 1;
- last_time = current_time;
- }
-
- // Check If Period Has Passed
- if ((current_time - last_time) > CONTROLLER_LOOK_EVENT_PERIOD) {
- // Reset Last Time
- last_time = current_time;
-
- // Verify
- verify_controller_axis_value(x, CONTROLLER_LOOK_AXIS_THRESHOLD);
- verify_controller_axis_value(y, CONTROLLER_LOOK_AXIS_THRESHOLD);
-
- // Send Event
- if (is_interactable) {
- SDL_Event event;
- event.type = SDL_MOUSEMOTION;
- event.motion.x = last_mouse_x;
- event.motion.y = last_mouse_y;
- event.motion.xrel = x * CONTROLLER_LOOK_AXIS_SENSITIVITY;
- event.motion.yrel = y * CONTROLLER_LOOK_AXIS_SENSITIVITY;
- SDL_PushEvent(&event);
- }
- }
-}
-
-// Controller Place/Mine Triggers
-#define CONTROLLER_TRIGGER_THRESHOLD 0
-#define CONTROLLER_TRIGGER_COUNT 2
-static void glfw_controller_trigger(int trigger, int action) {
- glfw_click_raw(trigger == GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER ? GLFW_MOUSE_BUTTON_LEFT : GLFW_MOUSE_BUTTON_RIGHT, action);
-}
-
-// Current Controller
-static int current_controller = -1;
-
-// Track Controller State
-static void update_controller_state() {
- // Store Button/Trigger State
- static int controller_buttons[GLFW_GAMEPAD_BUTTON_LAST + 1];
- static int controller_triggers[CONTROLLER_TRIGGER_COUNT];
-
- // Get State
- GLFWgamepadstate state;
- int controller_enabled = cursor_grabbed && is_interactable;
- int controller_valid = controller_enabled && current_controller != -1 && glfwGetGamepadState(current_controller, &state);
- if (!controller_valid) {
- // Invalid Controller
-
- // Generate Blank State
- for (int i = GLFW_GAMEPAD_BUTTON_A; i <= GLFW_GAMEPAD_BUTTON_LAST; i++) {
- state.buttons[i] = GLFW_RELEASE;
- }
- for (int i = GLFW_GAMEPAD_AXIS_LEFT_X; i <= GLFW_GAMEPAD_AXIS_LAST; i++) {
- int is_trigger = i == GLFW_GAMEPAD_AXIS_LEFT_TRIGGER || i == GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER;
- state.axes[i] = is_trigger ? -1 : 0;
- }
- }
-
- // Check Buttons
- for (int i = GLFW_GAMEPAD_BUTTON_A; i <= GLFW_GAMEPAD_BUTTON_LAST; i++) {
- int old_state = controller_buttons[i];
- controller_buttons[i] = state.buttons[i];
- if (old_state != controller_buttons[i]) {
- // State Changed
- glfw_controller_button(i, controller_buttons[i]);
- }
- }
-
- // Handle Movement & Look
- glfw_controller_movement(state.axes[GLFW_GAMEPAD_AXIS_LEFT_X], state.axes[GLFW_GAMEPAD_AXIS_LEFT_Y]);
- glfw_controller_look(state.axes[GLFW_GAMEPAD_AXIS_RIGHT_X], state.axes[GLFW_GAMEPAD_AXIS_RIGHT_Y]);
-
- // Check Triggers
- for (int i = 0; i < CONTROLLER_TRIGGER_COUNT; i++) {
- int old_state = controller_triggers[i];
- int trigger_id = i == 0 ? GLFW_GAMEPAD_AXIS_LEFT_TRIGGER : GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER;
- controller_triggers[i] = state.axes[trigger_id] < CONTROLLER_TRIGGER_THRESHOLD ? GLFW_RELEASE : GLFW_PRESS;
- if (old_state != controller_triggers[i]) {
- // State Changed
- glfw_controller_trigger(trigger_id, controller_triggers[i]);
- }
- }
-}
-
-// Pick Controller
-static int joysticks[GLFW_JOYSTICK_LAST + 1];
-static void pick_new_controller() {
- current_controller = -1;
- for (int i = GLFW_JOYSTICK_1; i <= GLFW_JOYSTICK_LAST; i++) {
- if (joysticks[i] == 1) {
- current_controller = i;
- DEBUG("Using Controller: %s (%s)", glfwGetGamepadName(i), glfwGetJoystickName(i));
- break;
- }
- }
-}
-static void find_controllers() {
- for (int i = GLFW_JOYSTICK_1; i <= GLFW_JOYSTICK_LAST; i++) {
- joysticks[i] = glfwJoystickIsGamepad(i);
- }
- pick_new_controller();
-}
-static void glfw_joystick(int jid, int event) {
- if (event == GLFW_CONNECTED && glfwJoystickIsGamepad(jid)) {
- joysticks[jid] = 1;
- pick_new_controller();
- } else if (event == GLFW_DISCONNECTED) {
- joysticks[jid] = 0;
- if (jid == current_controller) {
- DEBUG("Controller Disconnected");
- pick_new_controller();
- }
- }
-}
-
-// Release all keys/buttons when interaction is disabled and vice versa.
-static void emit_events_after_is_interactable_change() {
- if (is_running) {
- for (int i = GLFW_KEY_SPACE; i <= GLFW_KEY_LAST; i++) {
- int state = glfwGetKey(glfw_window, i);
- if (state == GLFW_PRESS) {
- glfw_key_raw(i, glfwGetKeyScancode(i), is_interactable ? GLFW_PRESS : GLFW_RELEASE, 0);
- }
- }
- for (int i = GLFW_MOUSE_BUTTON_1; i <= GLFW_MOUSE_BUTTON_LAST; i++) {
- int state = glfwGetMouseButton(glfw_window, i);
- if (state == GLFW_PRESS) {
- glfw_click_raw(i, is_interactable ? GLFW_PRESS : GLFW_RELEASE);
- }
- }
- }
-}
-
-#endif
-
-// Enable/Disable Raw Mouse Motion
-void media_set_raw_mouse_motion_enabled(int enabled) {
- raw_mouse_motion_enabled = enabled;
-#ifndef MCPI_HEADLESS_MODE
- if (is_running) {
- glfwSetInputMode(glfw_window, GLFW_RAW_MOUSE_MOTION, GLFW_FALSE);
- }
-#endif
- if (!raw_mouse_motion_enabled) {
- WARN("Raw mouse motion has been DISABLED, this IS NOT recommended, and should only ever be used on systems that don't support or have broken raw mouse motion.");
- }
-}
-
-// Disable V-Sync
-static int disable_vsync = 0;
-void media_disable_vsync() {
- disable_vsync = 1;
-#ifndef MCPI_HEADLESS_MODE
- if (is_running) {
- glfwSwapInterval(0);
- }
-#endif
-}
-
-// Force EGL
-static int force_egl = 0;
-void media_force_egl() {
- if (force_egl == -1) {
- IMPOSSIBLE();
- }
- force_egl = 1;
-}
-
-// Init Media Layer
-#define GL_VERSION 0x1f02
-typedef const unsigned char *(*glGetString_t)(unsigned int name);
-void SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *icon) {
- // Don't Enable GLFW In Headless Mode
-#ifndef MCPI_HEADLESS_MODE
- // Init GLFW
- glfwSetErrorCallback(glfw_error);
-
- if (!glfwInit()) {
- ERR("Unable To Initialize GLFW");
- }
-
- // Create OpenGL ES Context
- glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
-#ifdef MCPI_USE_GLES1_COMPATIBILITY_LAYER
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
-#else
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 1);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
-#endif
- // Use EGL
- if (force_egl) {
- glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
- }
- force_egl = -1;
- // Extra Settings
- glfwWindowHint(GLFW_AUTO_ICONIFY, GLFW_FALSE);
- glfwWindowHint(GLFW_ALPHA_BITS, 0); // Fix Transparent Window On Wayland
- // App ID
- glfwWindowHintString(GLFW_X11_CLASS_NAME, MCPI_APP_ID);
- glfwWindowHintString(GLFW_WAYLAND_APP_ID, MCPI_APP_ID);
-
- // Create Window
- glfw_window = glfwCreateWindow(DEFAULT_WIDTH, DEFAULT_HEIGHT, title, NULL, NULL);
- if (!glfw_window) {
- ERR("Unable To Create GLFW Window");
- }
-
- // Event Handlers
- glfwSetKeyCallback(glfw_window, glfw_key);
- glfwSetCharCallback(glfw_window, glfw_char);
- glfwSetCursorPosCallback(glfw_window, glfw_motion);
- glfwSetMouseButtonCallback(glfw_window, glfw_click);
- glfwSetScrollCallback(glfw_window, glfw_scroll);
-
- // Setup Controller Support
- find_controllers();
- glfwSetJoystickCallback(glfw_joystick);
-
- // Make Window Context Current
- glfwMakeContextCurrent(glfw_window);
-
- // Setup Compatibility Layer
-#ifdef MCPI_USE_GLES1_COMPATIBILITY_LAYER
- extern void init_gles_compatibility_layer(void *);
- init_gles_compatibility_layer(glfwGetProcAddress);
-#endif
-
- // Debug
- glGetString_t glGetString = (glGetString_t) glfwGetProcAddress("glGetString");
- DEBUG("Using %s", (*glGetString)(GL_VERSION));
-
- // Init OpenAL
- _media_audio_init();
-#else
- (void) title; // Mark As Used
-#endif
-
- // Set State
- is_running = 1;
-
- // Update State
- update_cursor();
- if (disable_vsync) {
- media_disable_vsync();
- }
-
- // Always Cleanup Media Layer
- atexit(media_cleanup);
-}
-
-void media_swap_buffers() {
-#ifndef MCPI_HEADLESS_MODE
- // Don't Swap Buffers In A Context-Less Window
- glfwSwapBuffers(glfw_window);
-#endif
-}
-
-// Fullscreen Not Needed In Headless Mode
-#ifndef MCPI_HEADLESS_MODE
-static int is_fullscreen = 0;
-
-// Old Size And Position To Use When Exiting Fullscreen
-static int old_width = -1;
-static int old_height = -1;
-static int old_x = -1;
-static int old_y = -1;
-
-// Toggle Fullscreen
-void media_toggle_fullscreen() {
- if (is_fullscreen) {
- glfwSetWindowMonitor(glfw_window, NULL, old_x, old_y, old_width, old_height, GLFW_DONT_CARE);
-
- old_width = -1;
- old_height = -1;
- old_x = -1;
- old_y = -1;
- } else {
- glfwGetWindowSize(glfw_window, &old_width, &old_height);
- glfwGetWindowPos(glfw_window, &old_x, &old_y);
-
- GLFWmonitor *monitor = glfwGetPrimaryMonitor();
- const GLFWvidmode *mode = glfwGetVideoMode(monitor);
-
- glfwSetWindowMonitor(glfw_window, monitor, 0, 0, mode->width, mode->height, GLFW_DONT_CARE);
- }
- is_fullscreen = !is_fullscreen;
-}
-#else
-void media_toggle_fullscreen() {
-}
-#endif
-
-// Intercept SDL Events
-void _media_handle_SDL_PollEvent() {
- // GLFW And Audio Are Disabled Disabled In Headless Mode
-#ifndef MCPI_HEADLESS_MODE
- // Process GLFW Events
- glfwPollEvents();
-
- // Controller
- update_controller_state();
-
- // Close Window
- if (glfwWindowShouldClose(glfw_window)) {
- SDL_Event event;
- event.type = SDL_QUIT;
- SDL_PushEvent(&event);
- glfwSetWindowShouldClose(glfw_window, GLFW_FALSE);
- }
-#endif
-}
-
-// Cleanup Media Layer
-void media_cleanup() {
- if (is_running) {
- // GLFW And Audio Are Disabled In Headless Mode
-#ifndef MCPI_HEADLESS_MODE
- // Ignore GLFW Errors During Termination
- glfwSetErrorCallback(NULL);
-
- // Terminate GLFW
- glfwDestroyWindow(glfw_window);
- glfwTerminate();
-
- // Cleanup OpenAL
- _media_audio_cleanup();
-#endif
-
- // Update State
- is_running = 0;
- }
-}
-
-// Update GLFW Cursor State (Client Only)
-static void update_cursor() {
-#ifndef MCPI_HEADLESS_MODE
- if (is_running) {
- // Get New State
- int new_cursor_visible = is_interactable ? cursor_visible : 1;
- int new_cursor_grabbed = is_interactable ? cursor_grabbed : 0;
-
- // Store Old Mode
- int old_mode = glfwGetInputMode(glfw_window, GLFW_CURSOR);
-
- // Handle Cursor Visibility
- int new_mode;
- if (!new_cursor_visible) {
- if (new_cursor_grabbed) {
- new_mode = GLFW_CURSOR_DISABLED;
- } else {
- new_mode = GLFW_CURSOR_HIDDEN;
- }
- } else {
- new_mode = GLFW_CURSOR_NORMAL;
- }
- if (new_mode != old_mode) {
- // Ignore Relative Cursor Motion When Locking
- if (new_mode == GLFW_CURSOR_DISABLED && old_mode != GLFW_CURSOR_DISABLED) {
- ignore_relative_motion = 1;
- }
-
- // Set New Mode
- glfwSetInputMode(glfw_window, GLFW_CURSOR, new_mode);
-
- // Handle Cursor Lock/Unlock
- if ((new_mode == GLFW_CURSOR_DISABLED && old_mode != GLFW_CURSOR_DISABLED) || (new_mode != GLFW_CURSOR_DISABLED && old_mode == GLFW_CURSOR_DISABLED)) {
- // Use Raw Mouse Motion
- if (raw_mouse_motion_enabled) {
- glfwSetInputMode(glfw_window, GLFW_RAW_MOUSE_MOTION, new_mode == GLFW_CURSOR_DISABLED ? GLFW_TRUE : GLFW_FALSE);
- }
-
- // Request Focus
- if (!glfwGetWindowAttrib(glfw_window, GLFW_FOCUSED)) {
- glfwRequestWindowAttention(glfw_window);
- }
- }
-
- // Reset Mouse Position When Unlocking
- if (new_mode != GLFW_CURSOR_DISABLED && old_mode == GLFW_CURSOR_DISABLED) {
- double cursor_x;
- double cursor_y;
- glfwGetCursorPos(glfw_window, &cursor_x, &cursor_y);
- glfw_motion(glfw_window, cursor_x, cursor_y);
- }
- }
- }
-#endif
-}
-
-// Fix SDL Cursor Visibility/Grabbing
-SDL_GrabMode SDL_WM_GrabInput(SDL_GrabMode mode) {
- if (mode == SDL_GRAB_QUERY) {
- // Query
- return cursor_grabbed ? SDL_GRAB_ON : SDL_GRAB_OFF;
- } else if (mode == SDL_GRAB_ON) {
- // Store State
- cursor_grabbed = 1;
- } else if (mode == SDL_GRAB_OFF) {
- // Store State
- cursor_grabbed = 0;
- }
- // Update Cursor GLFW State (Client Only)
- update_cursor();
- // Return
- return mode;
-}
-
-// Stub SDL Cursor Visibility
-int SDL_ShowCursor(int toggle) {
- if (toggle == SDL_QUERY) {
- // Query
- return cursor_visible ? SDL_ENABLE : SDL_DISABLE;
- } else if (toggle == SDL_ENABLE) {
- // Store State
- cursor_visible = 1;
- } else if (toggle == SDL_DISABLE) {
- // Store State
- cursor_visible = 0;
- }
- // Update Cursor GLFW State (Client Only)
- update_cursor();
- // Return
- return toggle;
-}
-
-// Get Framebuffer Size
-void media_get_framebuffer_size(int *width, int *height) {
-#ifndef MCPI_HEADLESS_MODE
- if (glfw_window != NULL) {
- glfwGetFramebufferSize(glfw_window, width, height);
- return;
- }
-#endif
- *width = DEFAULT_WIDTH;
- *height = DEFAULT_HEIGHT;
-}
diff --git a/media-layer/core/src/media.cpp b/media-layer/core/src/media.cpp
new file mode 100644
index 00000000..8ac8e8b0
--- /dev/null
+++ b/media-layer/core/src/media.cpp
@@ -0,0 +1,561 @@
+#include
+#include
+
+#include
+#include
+
+#define GLFW_INCLUDE_NONE
+#include
+
+#include
+#include
+
+#include "audio/engine.h"
+
+// Allow Disabling Interaction
+static void update_cursor();
+static int is_interactable = 1;
+void media_set_interactable(int toggle) {
+ if (bool(toggle) != is_interactable) {
+ is_interactable = toggle;
+ update_cursor();
+ }
+}
+
+// Store Cursor State
+static bool cursor_grabbed = false;
+static bool cursor_visible = true;
+
+// Track If Raw Mouse Motion Is Enabled
+static bool raw_mouse_motion_enabled = true;
+
+// Window
+static GLFWwindow *glfw_window = nullptr;
+
+// Handle GLFW Error
+static void glfw_error(__attribute__((unused)) int error, const char *description) {
+ WARN("GLFW Error: %s", description);
+}
+
+// Convert GLFW Key To SDL Key
+static SDLKey glfw_key_to_sdl_key(const int key) {
+ switch (key) {
+ // Movement
+ case GLFW_KEY_W:
+ return SDLK_w;
+ case GLFW_KEY_A:
+ return SDLK_a;
+ case GLFW_KEY_S:
+ return SDLK_s;
+ case GLFW_KEY_D:
+ return SDLK_d;
+ case GLFW_KEY_SPACE:
+ return SDLK_SPACE;
+ case GLFW_KEY_LEFT_SHIFT:
+ return SDLK_LSHIFT;
+ case GLFW_KEY_RIGHT_SHIFT:
+ return SDLK_RSHIFT;
+ // Inventory
+ case GLFW_KEY_E:
+ return SDLK_e;
+ // Drop Item
+ case GLFW_KEY_Q:
+ return SDLK_q;
+ // Toolbar
+ case GLFW_KEY_1:
+ return SDLK_1;
+ case GLFW_KEY_2:
+ return SDLK_2;
+ case GLFW_KEY_3:
+ return SDLK_3;
+ case GLFW_KEY_4:
+ return SDLK_4;
+ case GLFW_KEY_5:
+ return SDLK_5;
+ case GLFW_KEY_6:
+ return SDLK_6;
+ case GLFW_KEY_7:
+ return SDLK_7;
+ case GLFW_KEY_8:
+ return SDLK_8;
+ case GLFW_KEY_9:
+ return SDLK_9;
+ case GLFW_KEY_0:
+ return SDLK_0;
+ // UI Control
+ case GLFW_KEY_ESCAPE:
+ return SDLK_ESCAPE;
+ case GLFW_KEY_UP:
+ return SDLK_UP;
+ case GLFW_KEY_DOWN:
+ return SDLK_DOWN;
+ case GLFW_KEY_LEFT:
+ return SDLK_LEFT;
+ case GLFW_KEY_RIGHT:
+ return SDLK_RIGHT;
+ case GLFW_KEY_TAB:
+ return SDLK_TAB;
+ case GLFW_KEY_ENTER:
+ return SDLK_RETURN;
+ case GLFW_KEY_BACKSPACE:
+ return SDLK_BACKSPACE;
+ case GLFW_KEY_DELETE:
+ return SDLK_DELETE;
+ // Fullscreen
+ case GLFW_KEY_F11:
+ return SDLK_F11;
+ // Screenshot
+ case GLFW_KEY_F2:
+ return SDLK_F2;
+ // Hide GUI
+ case GLFW_KEY_F1:
+ return SDLK_F1;
+ // Third Person
+ case GLFW_KEY_F5:
+ return SDLK_F5;
+ // Chat
+ case GLFW_KEY_T:
+ return SDLK_t;
+ // Unknown
+ default:
+ return SDLK_UNKNOWN;
+ }
+}
+
+// Convert GLFW Key Modifier To SDL Key Modifier
+static SDLMod glfw_modifier_to_sdl_modifier(const int mods) {
+ int ret = KMOD_NONE;
+ // Control
+ if ((mods & GLFW_MOD_CONTROL) != 0) {
+ ret |= KMOD_CTRL;
+ }
+ // Shift
+ if ((mods & GLFW_MOD_SHIFT) != 0) {
+ ret |= KMOD_SHIFT;
+ }
+ // Alt
+ if ((mods & GLFW_MOD_ALT) != 0) {
+ ret |= KMOD_ALT;
+ }
+ // Return
+ return SDLMod(ret);
+}
+
+// Pass Key Presses To SDL
+static void glfw_key_raw(int key, int scancode, int action, int mods) {
+ SDL_Event event1;
+ bool up = action == GLFW_RELEASE;
+ event1.type = up ? SDL_KEYUP : SDL_KEYDOWN;
+ event1.key.state = up ? SDL_RELEASED : SDL_PRESSED;
+ event1.key.keysym.scancode = scancode;
+ event1.key.keysym.mod = glfw_modifier_to_sdl_modifier(mods);
+ event1.key.keysym.sym = glfw_key_to_sdl_key(key);
+ SDL_PushEvent(&event1);
+ // Allow MCPI To Access Original GLFW Keycode
+ SDL_Event event2;
+ event2.type = SDL_USEREVENT;
+ event2.user.code = USER_EVENT_REAL_KEY;
+ event2.user.data1 = event1.key.state;
+ event2.user.data2 = key;
+ SDL_PushEvent(&event2);
+}
+static void glfw_key(__attribute__((unused)) GLFWwindow *window, const int key, const int scancode, const int action, const int mods) {
+ if (is_interactable) {
+ glfw_key_raw(key, scancode, action, mods);
+ }
+}
+
+// Pass Text To Minecraft
+static void character_event(char c) {
+ if (!is_interactable) {
+ return;
+ }
+ // SDL_UserEvent Is Never Used In MCPI, So It Is Repurposed For Character Events
+ SDL_Event event;
+ event.type = SDL_USEREVENT;
+ event.user.code = USER_EVENT_CHARACTER;
+ event.user.data1 = (int) c;
+ SDL_PushEvent(&event);
+}
+static void codepoint_to_utf8(unsigned char *const buffer, const unsigned int code) {
+ // https://stackoverflow.com/a/42013433/16198887
+ if (code <= 0x7f) {
+ buffer[0] = code;
+ } else if (code <= 0x7ff) {
+ buffer[0] = 0xc0 | (code >> 6); // 110xxxxx
+ buffer[1] = 0x80 | (code & 0x3f); // 10xxxxxx
+ } else if (code <= 0xffff) {
+ buffer[0] = 0xe0 | (code >> 12); // 1110xxxx
+ buffer[1] = 0x80 | ((code >> 6) & 0x3f); // 10xxxxxx
+ buffer[2] = 0x80 | (code & 0x3f); // 10xxxxxx
+ } else if (code <= 0x10ffff) {
+ buffer[0] = 0xf0 | (code >> 18); // 11110xxx
+ buffer[1] = 0x80 | ((code >> 12) & 0x3f); // 10xxxxxx
+ buffer[2] = 0x80 | ((code >> 6) & 0x3f); // 10xxxxxx
+ buffer[3] = 0x80 | (code & 0x3f); // 10xxxxxx
+ }
+}
+static void glfw_char(__attribute__((unused)) GLFWwindow *window, const unsigned int codepoint) {
+ // Convert
+ size_t str_size = 4 /* Maximum UTF-8 character size */ + 1 /* NULL-terminator */;
+ char str[str_size] = {};
+ codepoint_to_utf8((unsigned char *) str, codepoint);
+ char *cp437_str = to_cp437(str);
+ // Send Event
+ for (int i = 0; cp437_str[i] != '\0'; i++) {
+ character_event(cp437_str[i]);
+ }
+ // Free
+ free(cp437_str);
+}
+
+// Last Mouse Location
+static double last_mouse_x = 0;
+static double last_mouse_y = 0;
+// Ignore Relative Cursor Motion
+static bool ignore_relative_motion = false;
+
+// Convert Screen Coordinates To Pixels
+static void convert_to_pixels(GLFWwindow *window, double *xpos, double *ypos) {
+ // Skip If Cursor Is Grabbed
+ if (cursor_grabbed && raw_mouse_motion_enabled) {
+ return;
+ }
+ // Get Window Size
+ int window_width;
+ int window_height;
+ glfwGetWindowSize(window, &window_width, &window_height);
+ // Get Framebuffer Size
+ int framebuffer_width;
+ int framebuffer_height;
+ glfwGetFramebufferSize(window, &framebuffer_width, &framebuffer_height);
+ // Calculate Ratios
+ const double width_ratio = ((double) framebuffer_width) / ((double) window_width);
+ const double height_ratio = ((double) framebuffer_height) / ((double) window_height);
+ // Multiply
+ *xpos *= width_ratio;
+ *ypos *= height_ratio;
+}
+
+// Pass Mouse Movement To SDL
+static void glfw_motion(__attribute__((unused)) GLFWwindow *window, double xpos, double ypos) {
+ convert_to_pixels(window, &xpos, &ypos);
+ if (is_interactable) {
+ SDL_Event event;
+ event.type = SDL_MOUSEMOTION;
+ event.motion.x = xpos;
+ event.motion.y = ypos;
+ event.motion.xrel = !ignore_relative_motion ? (xpos - last_mouse_x) : 0;
+ event.motion.yrel = !ignore_relative_motion ? (ypos - last_mouse_y) : 0;
+ SDL_PushEvent(&event);
+ }
+ ignore_relative_motion = false;
+ last_mouse_x = xpos;
+ last_mouse_y = ypos;
+}
+
+// Create And Push SDL Mouse Click Event
+static void click_event(int button, bool up) {
+ SDL_Event event;
+ event.type = up ? SDL_MOUSEBUTTONUP : SDL_MOUSEBUTTONDOWN;
+ event.button.x = last_mouse_x;
+ event.button.y = last_mouse_y;
+ event.button.state = up ? SDL_RELEASED : SDL_PRESSED;
+ event.button.button = button;
+ SDL_PushEvent(&event);
+}
+
+// Pass Mouse Click To SDL
+static void glfw_click_raw(const int button, const int action) {
+ const bool up = action == GLFW_RELEASE;
+ const int sdl_button = button == GLFW_MOUSE_BUTTON_RIGHT ? SDL_BUTTON_RIGHT : (button == GLFW_MOUSE_BUTTON_LEFT ? SDL_BUTTON_LEFT : SDL_BUTTON_MIDDLE);
+ click_event(sdl_button, up);
+}
+static void glfw_click(__attribute__((unused)) GLFWwindow *window, const int button, const int action, __attribute__((unused)) int mods) {
+ if (is_interactable) {
+ glfw_click_raw(button, action);
+ }
+}
+
+// Pass Mouse Scroll To SDL
+static void glfw_scroll(__attribute__((unused)) GLFWwindow *window, __attribute__((unused)) double xoffset, double yoffset) {
+ if (is_interactable && yoffset != 0) {
+ int sdl_button = yoffset > 0 ? SDL_BUTTON_WHEELUP : SDL_BUTTON_WHEELDOWN;
+ click_event(sdl_button, false);
+ click_event(sdl_button, true);
+ }
+}
+
+// Enable/Disable Raw Mouse Motion
+void media_set_raw_mouse_motion_enabled(const int enabled) {
+ raw_mouse_motion_enabled = enabled;
+ if (glfw_window) {
+ glfwSetInputMode(glfw_window, GLFW_RAW_MOUSE_MOTION, GLFW_FALSE);
+ }
+ if (!raw_mouse_motion_enabled) {
+ WARN("Raw mouse motion has been DISABLED, this IS NOT recommended, and should only ever be used on systems that don't support or have broken raw mouse motion.");
+ }
+}
+
+// Disable V-Sync
+static int disable_vsync = 0;
+void media_disable_vsync() {
+ disable_vsync = 1;
+ if (glfw_window) {
+ glfwSwapInterval(0);
+ }
+}
+
+// Force EGL
+static int force_egl = 0;
+void media_force_egl() {
+ if (force_egl == -1) {
+ IMPOSSIBLE();
+ }
+ force_egl = 1;
+}
+
+// Init Media Layer
+#define GL_VERSION 0x1f02
+typedef const char *(*glGetString_t)(unsigned int name);
+#ifdef MCPI_USE_GLES1_COMPATIBILITY_LAYER
+extern "C" void init_gles_compatibility_layer(void *);
+#endif
+void SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *icon) {
+ // Disable In Headless Mode
+ if (reborn_is_headless()) {
+ return;
+ }
+
+ // Init GLFW
+ reborn_check_display();
+ glfwSetErrorCallback(glfw_error);
+ if (!glfwInit()) {
+ ERR("Unable To Initialize GLFW");
+ }
+
+ // Create OpenGL ES Context
+ glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
+#ifdef MCPI_USE_GLES1_COMPATIBILITY_LAYER
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+#else
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 1);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
+#endif
+ // Use EGL
+ if (force_egl) {
+ glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
+ }
+ force_egl = -1;
+ // Extra Settings
+ glfwWindowHint(GLFW_AUTO_ICONIFY, GLFW_FALSE);
+ glfwWindowHint(GLFW_ALPHA_BITS, 0); // Fix Transparent Window On Wayland
+ // App ID
+ glfwWindowHintString(GLFW_X11_CLASS_NAME, MCPI_APP_ID);
+ glfwWindowHintString(GLFW_WAYLAND_APP_ID, MCPI_APP_ID);
+
+ // Create Window
+ glfw_window = glfwCreateWindow(DEFAULT_WIDTH, DEFAULT_HEIGHT, title, NULL, NULL);
+ if (!glfw_window) {
+ ERR("Unable To Create GLFW Window");
+ }
+
+ // Event Handlers
+ glfwSetKeyCallback(glfw_window, glfw_key);
+ glfwSetCharCallback(glfw_window, glfw_char);
+ glfwSetCursorPosCallback(glfw_window, glfw_motion);
+ glfwSetMouseButtonCallback(glfw_window, glfw_click);
+ glfwSetScrollCallback(glfw_window, glfw_scroll);
+
+ // Make Window Context Current
+ glfwMakeContextCurrent(glfw_window);
+
+ // Setup Compatibility Layer
+#ifdef MCPI_USE_GLES1_COMPATIBILITY_LAYER
+ init_gles_compatibility_layer((void *) glfwGetProcAddress);
+#endif
+
+ // Debug
+ glGetString_t glGetString = (glGetString_t) glfwGetProcAddress("glGetString");
+ DEBUG("Using %s", (*glGetString)(GL_VERSION));
+
+ // Init OpenAL
+ _media_audio_init();
+
+ // Update State
+ update_cursor();
+ if (disable_vsync) {
+ media_disable_vsync();
+ }
+
+ // Always Cleanup Media Layer
+ atexit(media_cleanup);
+}
+
+void media_swap_buffers() {
+ if (glfw_window) {
+ glfwSwapBuffers(glfw_window);
+ }
+}
+
+// Track Fullscreen
+static bool is_fullscreen = false;
+// Old Size And Position To Use When Exiting Fullscreen
+static int old_width = -1;
+static int old_height = -1;
+static int old_x = -1;
+static int old_y = -1;
+
+// Toggle Fullscreen
+void media_toggle_fullscreen() {
+ if (glfw_window) {
+ if (is_fullscreen) {
+ glfwSetWindowMonitor(glfw_window, nullptr, old_x, old_y, old_width, old_height, GLFW_DONT_CARE);
+
+ old_width = -1;
+ old_height = -1;
+ old_x = -1;
+ old_y = -1;
+ } else {
+ glfwGetWindowSize(glfw_window, &old_width, &old_height);
+ glfwGetWindowPos(glfw_window, &old_x, &old_y);
+
+ GLFWmonitor *monitor = glfwGetPrimaryMonitor();
+ const GLFWvidmode *mode = glfwGetVideoMode(monitor);
+
+ glfwSetWindowMonitor(glfw_window, monitor, 0, 0, mode->width, mode->height, GLFW_DONT_CARE);
+ }
+ is_fullscreen = !is_fullscreen;
+ }
+}
+
+// Intercept SDL Events
+void _media_handle_SDL_PollEvent() {
+ if (glfw_window) {
+ // Process GLFW Events
+ glfwPollEvents();
+ // Close Window
+ if (glfwWindowShouldClose(glfw_window)) {
+ SDL_Event event;
+ event.type = SDL_QUIT;
+ SDL_PushEvent(&event);
+ glfwSetWindowShouldClose(glfw_window, GLFW_FALSE);
+ }
+ }
+}
+
+// Cleanup Media Layer
+void media_cleanup() {
+ if (glfw_window) {
+ // Ignore GLFW Errors During Termination
+ glfwSetErrorCallback(nullptr);
+
+ // Terminate GLFW
+ glfwDestroyWindow(glfw_window);
+ glfwTerminate();
+
+ // Cleanup OpenAL
+ _media_audio_cleanup();
+ }
+}
+
+// Update GLFW Cursor State (Client Only)
+static void update_cursor() {
+ if (glfw_window) {
+ // Get New State
+ const bool new_cursor_visible = is_interactable ? cursor_visible : true;
+ const bool new_cursor_grabbed = is_interactable ? cursor_grabbed : false;
+
+ // Store Old Mode
+ const int old_mode = glfwGetInputMode(glfw_window, GLFW_CURSOR);
+
+ // Handle Cursor Visibility
+ int new_mode;
+ if (!new_cursor_visible) {
+ if (new_cursor_grabbed) {
+ new_mode = GLFW_CURSOR_DISABLED;
+ } else {
+ new_mode = GLFW_CURSOR_HIDDEN;
+ }
+ } else {
+ new_mode = GLFW_CURSOR_NORMAL;
+ }
+ if (new_mode != old_mode) {
+ // Ignore Relative Cursor Motion When Locking
+ if (new_mode == GLFW_CURSOR_DISABLED && old_mode != GLFW_CURSOR_DISABLED) {
+ ignore_relative_motion = true;
+ }
+
+ // Set New Mode
+ glfwSetInputMode(glfw_window, GLFW_CURSOR, new_mode);
+
+ // Handle Cursor Lock/Unlock
+ if ((new_mode == GLFW_CURSOR_DISABLED && old_mode != GLFW_CURSOR_DISABLED) || (new_mode != GLFW_CURSOR_DISABLED && old_mode == GLFW_CURSOR_DISABLED)) {
+ // Use Raw Mouse Motion
+ if (raw_mouse_motion_enabled) {
+ glfwSetInputMode(glfw_window, GLFW_RAW_MOUSE_MOTION, new_mode == GLFW_CURSOR_DISABLED ? GLFW_TRUE : GLFW_FALSE);
+ }
+
+ // Request Focus
+ if (!glfwGetWindowAttrib(glfw_window, GLFW_FOCUSED)) {
+ glfwRequestWindowAttention(glfw_window);
+ }
+ }
+
+ // Reset Mouse Position When Unlocking
+ if (new_mode != GLFW_CURSOR_DISABLED && old_mode == GLFW_CURSOR_DISABLED) {
+ double cursor_x;
+ double cursor_y;
+ glfwGetCursorPos(glfw_window, &cursor_x, &cursor_y);
+ glfw_motion(glfw_window, cursor_x, cursor_y);
+ }
+ }
+ }
+}
+
+// Fix SDL Cursor Visibility/Grabbing
+SDL_GrabMode SDL_WM_GrabInput(const SDL_GrabMode mode) {
+ if (mode == SDL_GRAB_QUERY) {
+ // Query
+ return cursor_grabbed ? SDL_GRAB_ON : SDL_GRAB_OFF;
+ } else if (mode == SDL_GRAB_ON) {
+ // Store State
+ cursor_grabbed = true;
+ } else if (mode == SDL_GRAB_OFF) {
+ // Store State
+ cursor_grabbed = false;
+ }
+ // Update Cursor GLFW State (Client Only)
+ update_cursor();
+ // Return
+ return mode;
+}
+
+// Stub SDL Cursor Visibility
+int SDL_ShowCursor(const int toggle) {
+ if (toggle == SDL_QUERY) {
+ // Query
+ return cursor_visible ? SDL_ENABLE : SDL_DISABLE;
+ } else if (toggle == SDL_ENABLE) {
+ // Store State
+ cursor_visible = true;
+ } else if (toggle == SDL_DISABLE) {
+ // Store State
+ cursor_visible = false;
+ }
+ // Update Cursor GLFW State (Client Only)
+ update_cursor();
+ // Return
+ return toggle;
+}
+
+// Get Framebuffer Size
+void media_get_framebuffer_size(int *width, int *height) {
+ if (glfw_window) {
+ glfwGetFramebufferSize(glfw_window, width, height);
+ } else {
+ *width = DEFAULT_WIDTH;
+ *height = DEFAULT_HEIGHT;
+ }
+}
diff --git a/media-layer/trampoline/CMakeLists.txt b/media-layer/trampoline/CMakeLists.txt
index 3dd5bbd4..1f4307e6 100644
--- a/media-layer/trampoline/CMakeLists.txt
+++ b/media-layer/trampoline/CMakeLists.txt
@@ -1,19 +1,13 @@
project(media-layer-trampoline)
-# Configuration
-set(MEDIA_LAYER_TRAMPOLINE_SRC src/media-layer-core.cpp) # Media Layer Trampoline Source
-if(NOT MCPI_HEADLESS_MODE)
- list(APPEND MEDIA_LAYER_TRAMPOLINE_SRC src/GLESv1_CM.cpp)
-endif()
+# Common Sources
+set(MEDIA_LAYER_TRAMPOLINE_SRC src/media-layer-core.cpp src/GLESv1_CM.cpp)
# Build
if(BUILD_NATIVE_COMPONENTS)
# Host Component
add_library(media-layer-trampoline src/host/host.cpp ${MEDIA_LAYER_TRAMPOLINE_SRC})
- target_link_libraries(media-layer-trampoline reborn-util media-layer-core trampoline-headers)
- if(NOT MCPI_HEADLESS_MODE)
- target_link_libraries(media-layer-trampoline GLESv1_CM)
- endif()
+ target_link_libraries(media-layer-trampoline reborn-util media-layer-core GLESv1_CM trampoline-headers)
target_compile_definitions(media-layer-trampoline PRIVATE -DMEDIA_LAYER_TRAMPOLINE_HOST)
# Install
install(TARGETS media-layer-trampoline DESTINATION "${MCPI_LIB_DIR}")
diff --git a/mods/CMakeLists.txt b/mods/CMakeLists.txt
index e252e0bf..60e58c19 100644
--- a/mods/CMakeLists.txt
+++ b/mods/CMakeLists.txt
@@ -46,72 +46,52 @@ set(SRC
src/text-input-box/TextInputScreen.cpp
# test
src/test/test.cpp
+ # sound
+ src/sound/sound.cpp
+ src/sound/repository.cpp
+ # camera
+ src/camera/camera.cpp
+ # input
+ src/input/input.cpp
+ src/input/bow.cpp
+ src/input/attack.cpp
+ src/input/toggle.cpp
+ src/input/misc.cpp
+ src/input/drop.cpp
+ # sign
+ src/sign/sign.cpp
+ # atlas
+ src/atlas/atlas.cpp
+ # title-screen
+ src/title-screen/title-screen.cpp
+ src/title-screen/splashes.txt # Show In IDE
+ src/title-screen/welcome.cpp
+ # skin
+ src/skin/skin.cpp
+ src/skin/loader.cpp
+ # screenshot
+ src/screenshot/screenshot.cpp
+ # textures
+ src/textures/textures.cpp
+ src/textures/lava.cpp
+ src/textures/headless.cpp
+ # fps
+ src/fps/fps.cpp
+ # server
+ src/server/server.cpp
+ src/server/server_properties.cpp
+ # multiplayer
+ src/multiplayer/multiplayer.cpp
+ # benchmark
+ src/benchmark/benchmark.cpp
# init
src/init/init.cpp
)
-
-# Server-Only Sources
-if(MCPI_SERVER_MODE)
- list(APPEND SRC
- # server
- src/server/server.cpp
- src/server/server_properties.cpp
- )
-else()
- list(APPEND SRC
- # multiplayer
- src/multiplayer/multiplayer.cpp
- # benchmark
- src/benchmark/benchmark.cpp
- )
-endif()
-
-# Headless-Only Sources
-if(MCPI_HEADLESS_MODE)
- list(APPEND SRC
- # textures
- src/textures/headless.cpp
- )
-else()
- list(APPEND SRC
- # sound
- src/sound/sound.cpp
- src/sound/repository.cpp
- # camera
- src/camera/camera.cpp
- # input
- src/input/input.cpp
- src/input/bow.cpp
- src/input/attack.cpp
- src/input/toggle.cpp
- src/input/misc.cpp
- src/input/drop.cpp
- src/input/crafting.cpp
- # sign
- src/sign/sign.cpp
- # atlas
- src/atlas/atlas.cpp
- # title-screen
- src/title-screen/title-screen.cpp
- src/title-screen/splashes.txt # Show In IDE
- src/title-screen/welcome.cpp
- # skin
- src/skin/skin.cpp
- src/skin/loader.cpp
- # screenshot
- src/screenshot/screenshot.cpp
- # textures
- src/textures/textures.cpp
- src/textures/lava.cpp
- # fps
- src/fps/fps.cpp
- )
- # Install Splashes
- install(
- FILES "src/title-screen/splashes.txt"
- DESTINATION "${MCPI_INSTALL_DIR}/data"
- )
-endif()
+# Install Splashes
+install(
+ FILES "src/title-screen/splashes.txt"
+ DESTINATION "${MCPI_INSTALL_DIR}/data"
+)
# Build
add_library(mods SHARED ${SRC})
@@ -122,10 +102,7 @@ install(TARGETS mods DESTINATION "${MCPI_INSTALL_DIR}/mods")
install(TARGETS mods EXPORT sdk DESTINATION "${MCPI_SDK_LIB_DIR}")
# Dependencies
-target_link_libraries(mods symbols reborn-patch media-layer-core dl pthread)
-if(NOT MCPI_HEADLESS_MODE)
- target_link_libraries(mods stb_image)
-endif()
+target_link_libraries(mods symbols reborn-patch media-layer-core stb_image dl pthread)
# Headers
target_include_directories(
diff --git a/mods/include/mods/feature/feature.h b/mods/include/mods/feature/feature.h
index 0a0b64c7..40fcf5f0 100644
--- a/mods/include/mods/feature/feature.h
+++ b/mods/include/mods/feature/feature.h
@@ -1,16 +1,10 @@
#pragma once
-#include
-
extern "C" {
-bool _feature_has(const char *name);
+bool _feature_has(const char *name, int server_default);
}
-#ifdef MCPI_SERVER_MODE
-#define _feature_has__server_defaul_is_server_disabled(name) 0
-#define _feature_has__server_defaul_is_server_auto(name) _feature_has(name)
-#define _feature_has__server_defaul_is_server_enabled(name) 1
-#define feature_has(name, server_default) _feature_has__server_defaul_is_##server_default(name)
-#else
-#define feature_has(name, server_default) _feature_has(name)
-#endif
+#define _feature_has_server_disabled (0)
+#define _feature_has_server_auto (-1)
+#define _feature_has_server_enabled (1)
+#define feature_has(name, server_default) _feature_has(name, _feature_has_##server_default)
diff --git a/mods/include/mods/game-mode/game-mode.h b/mods/include/mods/game-mode/game-mode.h
new file mode 100644
index 00000000..7d037e53
--- /dev/null
+++ b/mods/include/mods/game-mode/game-mode.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#include
+
+int get_seed_from_string(std::string str);
\ No newline at end of file
diff --git a/mods/include/mods/home/home.h b/mods/include/mods/home/home.h
index 09e42830..87dfa546 100644
--- a/mods/include/mods/home/home.h
+++ b/mods/include/mods/home/home.h
@@ -1,5 +1,5 @@
#pragma once
extern "C" {
-char *home_get();
+const char *home_get();
}
diff --git a/mods/include/mods/init/init.h b/mods/include/mods/init/init.h
index f8ad672e..eaaabd4d 100644
--- a/mods/include/mods/init/init.h
+++ b/mods/include/mods/init/init.h
@@ -4,13 +4,9 @@ extern "C" {
void run_tests();
void init_version();
void init_compat();
-#ifdef MCPI_SERVER_MODE
void init_server();
-#else
void init_multiplayer();
void init_benchmark();
-#endif
-#ifndef MCPI_HEADLESS_MODE
void init_sound();
void init_input();
void init_sign();
@@ -19,7 +15,6 @@ void init_atlas();
void init_title_screen();
void init_skin();
void init_fps();
-#endif
void init_touch();
void init_textures();
void init_creative();
diff --git a/mods/include/mods/input/input.h b/mods/include/mods/input/input.h
index 1e7322f3..1075f15f 100644
--- a/mods/include/mods/input/input.h
+++ b/mods/include/mods/input/input.h
@@ -9,7 +9,6 @@ void input_run_on_tick(input_tick_function_t function);
void input_set_is_right_click(int val);
int input_back();
void input_drop(int drop_slot);
-void input_open_crafting();
void input_set_is_left_click(int val);
diff --git a/mods/include/mods/screenshot/screenshot.h b/mods/include/mods/screenshot/screenshot.h
index 2ac4dffe..693e0625 100644
--- a/mods/include/mods/screenshot/screenshot.h
+++ b/mods/include/mods/screenshot/screenshot.h
@@ -1,5 +1,5 @@
#pragma once
extern "C" {
-void screenshot_take(char *home);
+void screenshot_take(const char *home);
}
\ No newline at end of file
diff --git a/mods/src/atlas/atlas.cpp b/mods/src/atlas/atlas.cpp
index bd749c36..0b60a029 100644
--- a/mods/src/atlas/atlas.cpp
+++ b/mods/src/atlas/atlas.cpp
@@ -91,9 +91,6 @@ static void FurnaceScreen_render_ItemRenderer_renderGuiItem_one_injection(Font *
// Init
void init_atlas() {
- // Add Better nullptr-Check (And More UI Fixes When The gui_blocks Atlas Is Disabled)
- overwrite_calls(ItemRenderer_renderGuiItem_two, ItemRenderer_renderGuiItem_two_injection);
-
// Disable The gui_blocks Atlas Which Contains Pre-Rendered Textures For Blocks In The Inventory
if (feature_has("Disable \"gui_blocks\" Atlas", server_disabled)) {
unsigned char disable_gui_blocks_atlas_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop"
@@ -105,5 +102,6 @@ void init_atlas() {
overwrite_calls(Tesselator_color, Tesselator_color_injection);
overwrite_call((void *) 0x32324, (void *) FurnaceScreen_render_ItemRenderer_renderGuiItem_one_injection);
overwrite_call((void *) 0x1e21c, (void *) InventoryPane_renderBatch_Tesselator_color_injection);
+ overwrite_calls(ItemRenderer_renderGuiItem_two, ItemRenderer_renderGuiItem_two_injection);
}
}
diff --git a/mods/src/benchmark/benchmark.cpp b/mods/src/benchmark/benchmark.cpp
index 77c33491..0d7fdb49 100644
--- a/mods/src/benchmark/benchmark.cpp
+++ b/mods/src/benchmark/benchmark.cpp
@@ -47,14 +47,12 @@ static void start_world(Minecraft *minecraft) {
}
// Track Frames
-#ifndef MCPI_HEADLESS_MODE
static unsigned long long int frames = 0;
HOOK(media_swap_buffers, void, ()) {
ensure_media_swap_buffers();
real_media_swap_buffers();
frames++;
}
-#endif
// Track Ticks
static unsigned long long int ticks = 0;
@@ -74,9 +72,7 @@ static long long int get_time() {
// Store Time When World Loaded
static bool world_loaded = false;
static long long int world_loaded_time;
-#ifndef MCPI_HEADLESS_MODE
static unsigned long long int world_loaded_frames;
-#endif
static unsigned long long int world_loaded_ticks;
// Last Logged Status Update
@@ -98,11 +94,9 @@ static void Minecraft_update_injection(Minecraft *minecraft) {
// Detect World Loaded
if (!world_loaded && minecraft->isLevelGenerated()) {
world_loaded = true;
- INFO("Loaded");
+ INFO("Benchmark Loaded");
world_loaded_time = now;
-#ifndef MCPI_HEADLESS_MODE
world_loaded_frames = frames;
-#endif
world_loaded_ticks = ticks;
}
@@ -110,9 +104,7 @@ static void Minecraft_update_injection(Minecraft *minecraft) {
if (!exit_requested && world_loaded) {
// Get Time
long long int current_time = now - world_loaded_time;
-#ifndef MCPI_HEADLESS_MODE
unsigned long long int current_frames = frames - world_loaded_frames;
-#endif
unsigned long long int current_ticks = ticks - world_loaded_ticks;
// Log
@@ -151,11 +143,11 @@ static void Minecraft_update_injection(Minecraft *minecraft) {
// Calculate FPS & TPS
INFO("Benchmark Completed");
INFO(" Total Time: %lld Nanoseconds", current_time);
-#ifndef MCPI_HEADLESS_MODE
- static double frames_per_nanosecond = ((double) current_frames) / ((double) current_time);
- static double frames_per_second = frames_per_nanosecond * NANOSECONDS_IN_SECOND;
- INFO(" FPS: %f (%llu Total Frames)", frames_per_second, current_frames);
-#endif
+ if (!reborn_is_headless()) {
+ static double frames_per_nanosecond = ((double) current_frames) / ((double) current_time);
+ static double frames_per_second = frames_per_nanosecond * NANOSECONDS_IN_SECOND;
+ INFO(" FPS: %f (%llu Total Frames)", frames_per_second, current_frames);
+ }
static double ticks_per_nanosecond = ((double) current_ticks) / ((double) current_time);
static double ticks_per_second = ticks_per_nanosecond * NANOSECONDS_IN_SECOND;
INFO(" TPS: %f (%llu Total Ticks)", ticks_per_second, current_ticks);
diff --git a/mods/src/camera/camera.cpp b/mods/src/camera/camera.cpp
index 8cbb4bd0..496e59ca 100644
--- a/mods/src/camera/camera.cpp
+++ b/mods/src/camera/camera.cpp
@@ -8,9 +8,7 @@
// Take Screenshot Using TripodCamera
static void AppPlatform_saveScreenshot_injection(__attribute__((unused)) AppPlatform_saveScreenshot_t original, __attribute__((unused)) AppPlatform *app_platform, __attribute__((unused)) std::string *path, __attribute__((unused)) int32_t width, __attribute__((unused)) int32_t height) {
-#ifndef MCPI_HEADLESS_MODE
screenshot_take(home_get());
-#endif
}
// Enable TripodCameraRenderer
diff --git a/mods/src/compat/compat.cpp b/mods/src/compat/compat.cpp
index 980c7381..b2ee1667 100644
--- a/mods/src/compat/compat.cpp
+++ b/mods/src/compat/compat.cpp
@@ -10,15 +10,12 @@
#include
#include
-
-#ifndef MCPI_HEADLESS_MODE
#include
#include
#include
#include
#include
-#endif
// Custom Title
HOOK(SDL_WM_SetCaption, void, (__attribute__((unused)) const char *title, const char *icon)) {
@@ -37,14 +34,12 @@ HOOK(SDL_ShowCursor, int, (int toggle)) {
HOOK(SDL_PollEvent, int, (SDL_Event *event)) {
// In Server Mode, Exit Requests Are Handled In src/server/server.cpp
// Check If Exit Is Requested
-#ifndef MCPI_SERVER_MODE
- if (compat_check_exit_requested()) {
+ if (!reborn_is_server() && compat_check_exit_requested()) {
// Send SDL_QUIT
SDL_Event new_event;
new_event.type = SDL_QUIT;
SDL_PushEvent(&new_event);
}
-#endif
// Poll Events
ensure_SDL_PollEvent();
@@ -53,8 +48,6 @@ HOOK(SDL_PollEvent, int, (SDL_Event *event)) {
// Handle Events
if (ret == 1 && event != nullptr) {
int handled = 0;
-
-#ifndef MCPI_HEADLESS_MODE
switch (event->type) {
case SDL_KEYDOWN: {
// Handle Key Presses
@@ -71,10 +64,6 @@ HOOK(SDL_PollEvent, int, (SDL_Event *event)) {
// Drop Item
input_drop((event->key.keysym.mod & KMOD_CTRL) != 0);
handled = 1;
- } else if (event->key.keysym.sym == SDLK_WORLD_0) {
- // Crafting
- input_open_crafting();
- handled = 1;
}
break;
}
@@ -97,8 +86,6 @@ HOOK(SDL_PollEvent, int, (SDL_Event *event)) {
break;
}
}
-#endif
-
if (handled) {
// Event Was Handled
return SDL_PollEvent(event);
diff --git a/mods/src/creative/creative.cpp b/mods/src/creative/creative.cpp
index ee06b1b7..6fedff51 100644
--- a/mods/src/creative/creative.cpp
+++ b/mods/src/creative/creative.cpp
@@ -6,7 +6,6 @@
#include
#include
-#ifndef MCPI_SERVER_MODE
// Add Item To Inventory
static void inventory_add_item(FillingContainer *inventory, Item *item) {
ItemInstance *item_instance = new ItemInstance;
@@ -83,7 +82,6 @@ static void Inventory_setupDefault_FillingContainer_addItem_call_injection(Filli
filling_container->addItem(new_item_instance);
}
}
-#endif
// Hook Specific TileItem Constructor
static TileItem *Tile_initTiles_TileItem_injection(TileItem *tile_item, int32_t id) {
@@ -111,9 +109,7 @@ int creative_is_restricted() {
void init_creative() {
// Add Extra Items To Creative Inventory (Only Replace Specific Function Call)
if (feature_has("Expand Creative Mode Inventory", server_enabled)) {
-#ifndef MCPI_SERVER_MODE
misc_run_on_creative_inventory_setup(Inventory_setupDefault_FillingContainer_addItem_call_injection);
-#endif
// Use AuxDataTileItem by default instead of TileItem, so tiles in the Creative
// Inventory can have arbitrary auxiliary values.
diff --git a/mods/src/feature/feature.cpp b/mods/src/feature/feature.cpp
index 498530d1..71b33671 100644
--- a/mods/src/feature/feature.cpp
+++ b/mods/src/feature/feature.cpp
@@ -6,7 +6,11 @@
#include
// Check For Feature
-bool _feature_has(const char *name) {
+bool _feature_has(const char *name, int server_default) {
+ // Server Handling
+ if (reborn_is_server() && server_default != -1) {
+ return server_default > 0;
+ }
// Get Value
char *env = getenv("MCPI_FEATURE_FLAGS");
char *features = strdup(env != nullptr ? env : "");
diff --git a/mods/src/game-mode/ui.cpp b/mods/src/game-mode/ui.cpp
index 709417df..6ed1bcbe 100644
--- a/mods/src/game-mode/ui.cpp
+++ b/mods/src/game-mode/ui.cpp
@@ -1,5 +1,6 @@
#include
#include
+#include
#include
#include
@@ -7,6 +8,7 @@
#include
#include
#include
+#include
#include "game-mode-internal.h"
// Strings
@@ -175,20 +177,24 @@ static std::string getUniqueLevelName(LevelStorageSource *source, const std::str
}
// Create World
-static void create_world(Minecraft *minecraft, std::string name, bool is_creative, std::string seed_str) {
- // Get Seed
+int get_seed_from_string(std::string str) {
int seed;
- seed_str = Util::stringTrim(&seed_str);
- if (!seed_str.empty()) {
+ str = Util::stringTrim(&str);
+ if (!str.empty()) {
int num;
- if (sscanf(seed_str.c_str(), "%d", &num) > 0) {
+ if (sscanf(str.c_str(), "%d", &num) > 0) {
seed = num;
} else {
- seed = Util::hashCode(&seed_str);
+ seed = Util::hashCode(&str);
}
} else {
seed = Common::getEpochTimeS();
}
+ return seed;
+}
+static void create_world(Minecraft *minecraft, std::string name, bool is_creative, std::string seed_str) {
+ // Get Seed
+ int seed = get_seed_from_string(std::move(seed_str));
// Get Folder Name
name = Util::stringTrim(&name);
diff --git a/mods/src/home/home.cpp b/mods/src/home/home.cpp
index fef1b4c3..fd118e06 100644
--- a/mods/src/home/home.cpp
+++ b/mods/src/home/home.cpp
@@ -7,24 +7,20 @@
#include
// Get MCPI Home Directory
-char *home_get() {
- static char *dir = nullptr;
+const char *home_get() {
+ static std::string dir = "";
// Load
- if (dir == nullptr) {
- safe_asprintf(&dir, "%s" HOME_SUBDIRECTORY_FOR_GAME_DATA, getenv("HOME"));
+ if (dir.empty()) {
+ dir = std::string(getenv("HOME")) + std::string(get_home_subdirectory_for_game_data());
}
// Return
- return dir;
-}
-// Free
-__attribute__((destructor)) static void _free_home() {
- free(home_get());
+ return dir.c_str();
}
// Init
void init_home() {
// Store Data In ~/.minecraft-pi Instead Of ~/.minecraft
- patch_address((void *) &Strings::default_path, (void *) HOME_SUBDIRECTORY_FOR_GAME_DATA);
+ patch_address((void *) &Strings::default_path, (void *) get_home_subdirectory_for_game_data());
// The override code resolves assets manually,
// making changing directory redundant.
diff --git a/mods/src/init/init.cpp b/mods/src/init/init.cpp
index abee8037..259636ed 100644
--- a/mods/src/init/init.cpp
+++ b/mods/src/init/init.cpp
@@ -8,12 +8,11 @@ __attribute__((constructor)) static void init() {
run_tests();
init_version();
init_compat();
-#ifdef MCPI_SERVER_MODE
- init_server();
-#else
- init_multiplayer();
-#endif
-#ifndef MCPI_HEADLESS_MODE
+ if (reborn_is_server()) {
+ init_server();
+ } else {
+ init_multiplayer();
+ }
init_sound();
init_input();
init_sign();
@@ -22,7 +21,6 @@ __attribute__((constructor)) static void init() {
init_title_screen();
init_skin();
init_fps();
-#endif
init_touch();
init_textures();
init_creative();
@@ -34,7 +32,7 @@ __attribute__((constructor)) static void init() {
init_bucket();
init_cake();
init_home();
-#ifndef MCPI_SERVER_MODE
- init_benchmark();
-#endif
+ if (!reborn_is_server()) {
+ init_benchmark();
+ }
}
diff --git a/mods/src/input/crafting.cpp b/mods/src/input/crafting.cpp
deleted file mode 100644
index 30f7219b..00000000
--- a/mods/src/input/crafting.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-#include
-#include
-
-#include "input-internal.h"
-#include
-#include
-
-// Store Should Open Crafting Menu
-static int should_open_crafting = 0;
-void input_open_crafting() {
- should_open_crafting = 1;
-}
-static void _handle_open_crafting(Minecraft *minecraft) {
- if (should_open_crafting) {
- should_open_crafting = 0;
-
- // Set Screen
- if (!creative_is_restricted() || !Minecraft_isCreativeMode(minecraft)) {
- WorkbenchScreen *screen = new WorkbenchScreen;
- ALLOC_CHECK(screen);
- screen = screen->constructor(0);
- minecraft->setScreen((Screen *) screen);
- }
- }
-}
-
-// Init
-void _init_crafting() {
- input_run_on_tick(_handle_open_crafting);
-}
diff --git a/mods/src/input/input-internal.h b/mods/src/input/input-internal.h
index 95fd0ba2..779dbf37 100644
--- a/mods/src/input/input-internal.h
+++ b/mods/src/input/input-internal.h
@@ -4,5 +4,4 @@ __attribute__((visibility("internal"))) void _init_attack();
__attribute__((visibility("internal"))) void _init_bow();
__attribute__((visibility("internal"))) void _init_misc();
__attribute__((visibility("internal"))) void _init_toggle();
-__attribute__((visibility("internal"))) void _init_drop();
-__attribute__((visibility("internal"))) void _init_crafting();
\ No newline at end of file
+__attribute__((visibility("internal"))) void _init_drop();
\ No newline at end of file
diff --git a/mods/src/input/input.cpp b/mods/src/input/input.cpp
index 09a18d28..992165de 100644
--- a/mods/src/input/input.cpp
+++ b/mods/src/input/input.cpp
@@ -49,9 +49,6 @@ void init_input() {
// Allow Attacking Mobs
_init_attack();
- // Allow Opening Crafting With Controller
- _init_crafting();
-
// Disable Raw Mouse Motion
if (feature_has("Disable Raw Mouse Motion (Not Recommended)", server_disabled)) {
media_set_raw_mouse_motion_enabled(0);
diff --git a/mods/src/misc/api.cpp b/mods/src/misc/api.cpp
index cbe594d9..5dca2990 100644
--- a/mods/src/misc/api.cpp
+++ b/mods/src/misc/api.cpp
@@ -3,9 +3,7 @@
#include
#include
-#ifndef MCPI_HEADLESS_MODE
#include
-#endif
#include
#include "misc-internal.h"
@@ -95,9 +93,7 @@ void misc_run_on_game_key_press(const std::function &fun
// Render Fancy Background
void misc_render_background(int color, Minecraft *minecraft, int x, int y, int width, int height) {
// https://github.com/ReMinecraftPE/mcpe/blob/f0d65eaecec1b3fe9c2f2b251e114a890c54ab77/source/client/gui/components/RolledSelectionList.cpp#L169-L179
-#ifndef MCPI_HEADLESS_MODE
glColor4f(1, 1, 1, 1);
-#endif
std::string texture = "gui/background.png";
minecraft->textures->loadAndBindTexture(&texture);
Tesselator *t = &Tesselator::instance;
diff --git a/mods/src/misc/misc.cpp b/mods/src/misc/misc.cpp
index 52768020..dd15b7e4 100644
--- a/mods/src/misc/misc.cpp
+++ b/mods/src/misc/misc.cpp
@@ -9,9 +9,7 @@
#include
#include
-#ifndef MCPI_HEADLESS_MODE
#include
-#endif
#include
#include
@@ -143,9 +141,7 @@ static void Gui_renderChatMessages_injection(Gui_renderChatMessages_t original,
// Render Selected Item Text
if (render_selected_item_text) {
// Fix GL Mode
-#ifndef MCPI_HEADLESS_MODE
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-#endif
// Calculate Selected Item Text Scale
Minecraft *minecraft = gui->minecraft;
int32_t screen_width = minecraft->screen_width;
@@ -184,37 +180,25 @@ static void Inventory_selectSlot_injection(Inventory_selectSlot_t original, Inve
// Translucent Toolbar
static void Gui_renderToolBar_injection(Gui_renderToolBar_t original, Gui *gui, float param_1, int32_t param_2, int32_t param_3) {
// Call Original Method
-#ifndef MCPI_HEADLESS_MODE
- int was_blend_enabled = glIsEnabled(GL_BLEND);
+ bool was_blend_enabled = glIsEnabled(GL_BLEND);
if (!was_blend_enabled) {
glEnable(GL_BLEND);
}
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-#endif
original(gui, param_1, param_2, param_3);
-#ifndef MCPI_HEADLESS_MODE
if (!was_blend_enabled) {
glDisable(GL_BLEND);
}
-#endif
}
static void Gui_renderToolBar_glColor4f_injection(GLfloat red, GLfloat green, GLfloat blue, __attribute__((unused)) GLfloat alpha) {
// Fix Alpha
-#ifndef MCPI_HEADLESS_MODE
glColor4f(red, green, blue, 1.0f);
-#else
- (void) red;
- (void) green;
- (void) blue;
-#endif
}
// Fix Screen Rendering When GUI is Hidden
static void Screen_render_injection(Screen_render_t original, Screen *screen, int32_t param_1, int32_t param_2, float param_3) {
// Fix
-#ifndef MCPI_HEADLESS_MODE
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-#endif
// Call Original Method
original(screen, param_1, param_2, param_3);
}
@@ -265,18 +249,21 @@ static const char *RAKNET_ERROR_NAMES[] = {
"Couldn't Generate GUID",
"Unknown"
};
-#ifdef MCPI_SERVER_MODE
-#define PRINT_RAKNET_STARTUP_FAILURE ERR
-#else
-#define PRINT_RAKNET_STARTUP_FAILURE WARN
-#endif
+#define CONDITIONAL_ERR(is_error, ...) \
+ { \
+ if ((is_error)) { \
+ ERR(__VA_ARGS__); \
+ } else { \
+ WARN(__VA_ARGS__); \
+ } \
+ }
static RakNet_StartupResult RakNetInstance_host_RakNet_RakPeer_Startup_injection(RakNet_RakPeer *rak_peer, unsigned short maxConnections, unsigned char *socketDescriptors, uint32_t socketDescriptorCount, int32_t threadPriority) {
// Call Original Method
RakNet_StartupResult result = rak_peer->Startup(maxConnections, socketDescriptors, socketDescriptorCount, threadPriority);
// Print Error
if (result != RAKNET_STARTED) {
- PRINT_RAKNET_STARTUP_FAILURE("Failed To Start RakNet: %s", RAKNET_ERROR_NAMES[result]);
+ CONDITIONAL_ERR(reborn_is_server(), "Failed To Start RakNet: %s", RAKNET_ERROR_NAMES[result]);
}
// Return
@@ -339,7 +326,6 @@ static int32_t FurnaceScreen_handleAddItem_injection(FurnaceScreen_handleAddItem
//
// The default behavior for Touch GUI is to only render the cursor when the mouse is clicking, this fixes that.
// This also makes the cursor always render if the mouse is unlocked, instead of just when there is a Screen showing.
-#ifndef MCPI_HEADLESS_MODE
static void GameRenderer_render_injection(GameRenderer_render_t original, GameRenderer *game_renderer, float param_1) {
// Call Original Method
original(game_renderer, param_1);
@@ -356,7 +342,6 @@ static void GameRenderer_render_injection(GameRenderer_render_t original, GameRe
Common::renderCursor(x, y, minecraft);
}
}
-#endif
// Get Real Selected Slot
int32_t misc_get_real_selected_slot(Player *player) {
@@ -375,12 +360,12 @@ int32_t misc_get_real_selected_slot(Player *player) {
return selected_slot;
}
-#ifndef MCPI_HEADLESS_MODE
// Properly Generate Buffers
static void anGenBuffers_injection(int32_t count, uint32_t *buffers) {
- glGenBuffers(count, buffers);
+ if (!reborn_is_headless()) {
+ glGenBuffers(count, buffers);
+ }
}
-#endif
// Fix Graphics Bug When Switching To First-Person While Sneaking
static void PlayerRenderer_render_injection(PlayerRenderer *model_renderer, Entity *entity, float param_2, float param_3, float param_4, float param_5, float param_6) {
@@ -564,9 +549,8 @@ static ContainerMenu *ContainerMenu_destructor_injection(ContainerMenu_destructo
return original(container_menu);
}
-#ifndef MCPI_HEADLESS_MODE
// Custom Outline Color
-static void glColor4f_injection(__attribute__((unused)) GLfloat red, __attribute__((unused)) GLfloat green, __attribute__((unused)) GLfloat blue, __attribute__((unused)) GLfloat alpha) {
+static void LevelRenderer_render_AABB_glColor4f_injection(__attribute__((unused)) GLfloat red, __attribute__((unused)) GLfloat green, __attribute__((unused)) GLfloat blue, __attribute__((unused)) GLfloat alpha) {
// Set Color
glColor4f(0, 0, 0, 0.4);
@@ -591,7 +575,6 @@ static void glColor4f_injection(__attribute__((unused)) GLfloat red, __attribute
// Set Line Width
glLineWidth(line_width);
}
-#endif
// Fix Furnace Visual Bug
static int FurnaceTileEntity_getLitProgress_injection(FurnaceTileEntity_getLitProgress_t original, FurnaceTileEntity *furnace, int max) {
@@ -756,7 +739,8 @@ static std::string AppPlatform_linux_getDateString_injection(__attribute__((unus
}
// Init
-static void nop() {
+template
+static void nop(__attribute__((unused)) Args... args) {
}
void init_misc() {
// Remove Invalid Item Background (A Red Background That Appears For Items That Are Not Included In The gui_blocks Atlas)
@@ -820,13 +804,6 @@ void init_misc() {
overwrite_calls(FurnaceScreen_handleAddItem, FurnaceScreen_handleAddItem_injection);
}
-#ifdef MCPI_HEADLESS_MODE
- // Don't Render Game In Headless Mode
- overwrite_manual((void *) GameRenderer_render, (void *) nop);
- overwrite_manual((void *) NinecraftApp_initGLStates, (void *) nop);
- overwrite_manual((void *) Gui_onConfigChanged, (void *) nop);
- overwrite_manual((void *) LevelRenderer_generateSky, (void *) nop);
-#else
// Improved Cursor Rendering
if (feature_has("Improved Cursor Rendering", server_disabled)) {
// Disable Normal Cursor Rendering
@@ -835,7 +812,6 @@ void init_misc() {
// Add Custom Cursor Rendering
overwrite_calls(GameRenderer_render, GameRenderer_render_injection);
}
-#endif
// Disable V-Sync
if (feature_has("Disable V-Sync", server_enabled)) {
@@ -849,13 +825,11 @@ void init_misc() {
// Remove Forced GUI Lag
if (feature_has("Remove Forced GUI Lag (Can Break Joining Servers)", server_enabled)) {
- overwrite_manual((void *) Common_sleepMs, (void *) nop);
+ overwrite_calls(Common_sleepMs, nop);
}
-#ifndef MCPI_HEADLESS_MODE
// Properly Generate Buffers
overwrite(Common_anGenBuffers, anGenBuffers_injection);
-#endif
// Fix Graphics Bug When Switching To First-Person While Sneaking
patch_vtable(PlayerRenderer_render, PlayerRenderer_render_injection);
@@ -928,15 +902,13 @@ void init_misc() {
}
overwrite_calls(ChestTileEntity_shouldSave, ChestTileEntity_shouldSave_injection);
-#ifndef MCPI_HEADLESS_MODE
// Replace Block Highlight With Outline
if (feature_has("Replace Block Highlight With Outline", server_disabled)) {
overwrite(LevelRenderer_renderHitSelect, LevelRenderer_renderHitOutline);
unsigned char fix_outline_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop"
patch((void *) 0x4d830, fix_outline_patch);
- overwrite_call((void *) 0x4d764, (void *) glColor4f_injection);
+ overwrite_call((void *) 0x4d764, (void *) LevelRenderer_render_AABB_glColor4f_injection);
}
-#endif
// Fix Furnace Visual Bug
overwrite_calls(FurnaceTileEntity_getLitProgress, FurnaceTileEntity_getLitProgress_injection);
@@ -999,4 +971,12 @@ void init_misc() {
// Init Logging
_init_misc_logging();
_init_misc_api();
+
+ // Don't Render Game In Headless Mode
+ if (reborn_is_headless()) {
+ overwrite_calls(GameRenderer_render, nop);
+ overwrite_calls(NinecraftApp_initGLStates, nop);
+ overwrite_calls(Gui_onConfigChanged, nop);
+ overwrite_calls(LevelRenderer_generateSky, nop);
+ }
}
diff --git a/mods/src/multiplayer/multiplayer.cpp b/mods/src/multiplayer/multiplayer.cpp
index 41049620..11d543e8 100644
--- a/mods/src/multiplayer/multiplayer.cpp
+++ b/mods/src/multiplayer/multiplayer.cpp
@@ -1,16 +1,10 @@
-// Config Needs To Load First
-#include
-
-#ifdef MCPI_SERVER_MODE
-#error "External Server Code Requires Client Mode"
-#endif
-
#include
#include
#include
#include
#include
+#include
#include
#include
@@ -21,7 +15,7 @@ struct server_list_entry {
std::string address;
int port;
};
-static std::vector server_list_entries;
+static std::vector server_list_entries;
static bool server_list_loaded = false;
static void load_servers() {
// Prepare
@@ -54,40 +48,34 @@ static void load_servers() {
std::string line;
while (std::getline(server_list_file, line)) {
// Check Line
- if (line.length() > 0) {
+ if (!line.empty()) {
if (line[0] == '#') {
continue;
}
// Parse
std::string address;
std::string port_str;
- // Add Default Port If Needed
- size_t last_colon = line.find_last_of(':');
- if (last_colon == std::string::npos) {
- line.append(":19132");
- last_colon = line.find_last_of(':');
- }
- // Loop
- for (std::string::size_type i = 0; i < line.length(); i++) {
- if (i > last_colon) {
- port_str.push_back(line[i]);
- } else if (i < last_colon) {
- address.push_back(line[i]);
- }
+ size_t separator_pos = line.find_last_of('|');
+ if (separator_pos == std::string::npos) {
+ port_str = "19132";
+ address = line;
+ } else {
+ address = line.substr(0, separator_pos);
+ port_str = line.substr(separator_pos + 1);
}
// Check Line
- if (address.length() < 1 || port_str.length() < 1 || port_str.find_first_not_of("0123456789") != std::string::npos) {
+ if (address.empty() || port_str.empty() || port_str.find_first_not_of("0123456789") != std::string::npos) {
// Invalid Line
WARN("Invalid Server: %s", line.c_str());
continue;
}
// Parse Port
int port = std::stoi(port_str);
-
// Done
- struct server_list_entry entry;
- entry.address = address;
- entry.port = port;
+ server_list_entry entry = {
+ .address = address,
+ .port = port
+ };
server_list_entries.push_back(entry);
}
}
@@ -98,7 +86,7 @@ static void load_servers() {
}
// Iterare Server List
-static void iterate_servers(std::function callback) {
+static void iterate_servers(const std::function &callback) {
// Load
if (!server_list_loaded) {
load_servers();
@@ -106,8 +94,7 @@ static void iterate_servers(std::function c
}
// Loop
- for (std::vector::size_type i = 0; i < server_list_entries.size(); i++) {
- struct server_list_entry entry = server_list_entries[i];
+ for (const server_list_entry &entry : server_list_entries) {
callback(entry.address.c_str(), entry.port);
}
}
@@ -122,7 +109,7 @@ static void RakNetInstance_pingForHosts_injection(RakNetInstance_pingForHosts_t
// Add External Servers
iterate_servers([rak_peer](const char *address, int port) {
- rak_peer->Ping(address, port, 1, 0);
+ rak_peer->Ping(address, port, true, 0);
});
}
diff --git a/mods/src/options/info.cpp b/mods/src/options/info.cpp
index b5b1e7c6..b6ee17a6 100644
--- a/mods/src/options/info.cpp
+++ b/mods/src/options/info.cpp
@@ -40,9 +40,9 @@ static std::string extra_version_info_full = !extra_version_info.empty() ? (" ("
// Profile Directory
static std::string profile_directory_suffix =
#ifdef MCPI_IS_FLATPAK_BUILD
- "/.var/app/" MCPI_APP_ID
+ "/.var/app/" MCPI_APP_ID +
#endif
- HOME_SUBDIRECTORY_FOR_GAME_DATA
+ std::string(get_home_subdirectory_for_game_data())
;
static std::string get_profile_directory_url() {
const char *home = getenv("HOME");
diff --git a/mods/src/options/options.cpp b/mods/src/options/options.cpp
index 47c33d9f..db786867 100644
--- a/mods/src/options/options.cpp
+++ b/mods/src/options/options.cpp
@@ -57,10 +57,8 @@ static void Options_initDefaultValue_injection(Options_initDefaultValue_t origin
original(options);
// Default Graphics Settings
-#ifndef MCPI_SERVER_MODE
options->fancy_graphics = true;
options->ambient_occlusion = true;
-#endif
// Store
stored_options = options;
@@ -133,26 +131,15 @@ static std::vector OptionsFile_getOptionStrings_injection(OptionsFi
}
// Get New options.txt Path
-#ifndef MCPI_SERVER_MODE
-static char *get_new_options_txt_path() {
- static char *path = nullptr;
+static const char *get_new_options_txt_path() {
+ static std::string path = "";
// Path
- if (path == nullptr) {
- safe_asprintf(&path, "%s/options.txt", home_get());
+ if (path.empty()) {
+ path = !reborn_is_server() ? (std::string(home_get()) + "/options.txt") : "/dev/null";
}
// Return
- return path;
+ return path.c_str();
}
-// Free
-__attribute__((destructor)) static void _free_new_options_txt_path() {
- free(get_new_options_txt_path());
-}
-#else
-static char *get_new_options_txt_path() {
- // Block options.txt On Servers
- return (char *) "/dev/null";
-}
-#endif
// Init
void init_options() {
diff --git a/mods/src/override/override.cpp b/mods/src/override/override.cpp
index e782095a..56d76ba3 100644
--- a/mods/src/override/override.cpp
+++ b/mods/src/override/override.cpp
@@ -34,34 +34,32 @@ char *override_get_path(const char *filename) {
}
// Get MCPI Home Path
- char *home_path = home_get();
+ const std::string home_path = home_get();
// Get Asset Override Path
- char *overrides = nullptr;
- safe_asprintf(&overrides, "%s/overrides", home_path);
+ const std::string overrides = home_path + "/overrides";
// Data Prefiix
- const char *data_prefix = "data/";
- int data_prefix_length = strlen(data_prefix);
+ const std::string data_prefix = "data/";
+ int data_prefix_length = data_prefix.length();
// Folders To Check
- char *asset_folders[] = {
+ std::string asset_folders[] = {
overrides,
getenv("MCPI_REBORN_ASSETS_PATH"),
getenv("MCPI_VANILLA_ASSETS_PATH"),
- nullptr
+ ""
};
// Check For Override
- char *new_path = nullptr;
- if (starts_with(filename, data_prefix)) {
+ std::string new_path;
+ if (std::string(filename).rfind(data_prefix, 0) == 0) {
// Test Asset Folders
- for (int i = 0; asset_folders[i] != nullptr; i++) {
- safe_asprintf(&new_path, "%s/%s", asset_folders[i], &filename[data_prefix_length]);
+ for (int i = 0; !asset_folders[i].empty(); i++) {
+ new_path = asset_folders[i] + '/' + &filename[data_prefix_length];
ensure_access();
- if (real_access(new_path, F_OK) == -1) {
+ if (real_access(new_path.c_str(), F_OK) == -1) {
// Not Found In Asset Folder
- free(new_path);
- new_path = nullptr;
+ new_path = "";
continue;
} else {
// Found
@@ -70,11 +68,14 @@ char *override_get_path(const char *filename) {
}
}
- // Free
- free(overrides);
-
// Return
- return new_path;
+ if (new_path.empty()) {
+ return nullptr;
+ } else {
+ char *ret = strdup(new_path.c_str());
+ ALLOC_CHECK(ret);
+ return ret;
+ }
}
// Hook fopen
diff --git a/mods/src/screenshot/screenshot.cpp b/mods/src/screenshot/screenshot.cpp
index edbcd8d6..384f1102 100644
--- a/mods/src/screenshot/screenshot.cpp
+++ b/mods/src/screenshot/screenshot.cpp
@@ -38,7 +38,12 @@ static int save_png(const char *filename, unsigned char *pixels, int line_size,
// Write Image
return !stbi_write_png(filename, width, height, 4, pixels, line_size);
}
-void screenshot_take(char *home) {
+void screenshot_take(const char *home) {
+ // Check
+ if (reborn_is_headless()) {
+ IMPOSSIBLE();
+ }
+
// Get Directory
char *screenshots = nullptr;
safe_asprintf(&screenshots, "%s/screenshots", home);
diff --git a/mods/src/server/server.cpp b/mods/src/server/server.cpp
index 5f29ce07..9556167d 100644
--- a/mods/src/server/server.cpp
+++ b/mods/src/server/server.cpp
@@ -1,12 +1,5 @@
-// Config Needs To Load First
-#include
-
-#ifndef MCPI_SERVER_MODE
-#error "Server Code Requires Server Mode"
-#endif
-
#include
-#include
+#include
#include
#include
#include
@@ -19,15 +12,16 @@
#include
+#include
#include
#include
-
#include
#include
#include
#include
#include
+#include
// --only-generate: Ony Generate World And Then Exit
static bool only_generate = false;
@@ -79,8 +73,8 @@ static void start_world(Minecraft *minecraft) {
// Specify Level Settings
LevelSettings settings;
settings.game_type = get_server_properties().get_int("game-mode", DEFAULT_GAME_MODE);
- std::string seed_str = get_server_properties().get_string("seed", DEFAULT_SEED);
- int32_t seed = seed_str.length() > 0 ? std::stoi(seed_str) : time(nullptr);
+ const std::string seed_str = get_server_properties().get_string("seed", DEFAULT_SEED);
+ const int32_t seed = get_seed_from_string(seed_str);
settings.seed = seed;
// Select Level
@@ -89,7 +83,7 @@ static void start_world(Minecraft *minecraft) {
// Don't Open Port When Using --only-generate
if (!only_generate) {
// Open Port
- int port = get_server_properties().get_int("port", DEFAULT_PORT);
+ const int port = get_server_properties().get_int("port", DEFAULT_PORT);
INFO("Listening On: %i", port);
minecraft->hostMultiplayer(port);
}
@@ -130,8 +124,8 @@ static Level *get_level(Minecraft *minecraft) {
}
// Find Players With Username And Run Callback
-typedef void (*player_callback_t)(Minecraft *minecraft, std::string username, Player *player);
-static void find_players(Minecraft *minecraft, std::string target_username, player_callback_t callback, bool all_players) {
+typedef void (*player_callback_t)(Minecraft *minecraft, const std::string &username, Player *player);
+static void find_players(Minecraft *minecraft, const std::string &target_username, player_callback_t callback, bool all_players) {
Level *level = get_level(minecraft);
std::vector players = get_players_in_level(level);
bool found_player = false;
@@ -177,7 +171,7 @@ static char *get_player_ip(Minecraft *minecraft, Player *player) {
// Ban Player
static bool is_ip_in_blacklist(const char *ip);
-static void ban_callback(Minecraft *minecraft, std::string username, Player *player) {
+static void ban_callback(Minecraft *minecraft, const std::string &username, Player *player) {
// Get IP
char *ip = get_player_ip(minecraft, player);
@@ -198,13 +192,13 @@ static void ban_callback(Minecraft *minecraft, std::string username, Player *pla
}
// Kill Player
-static void kill_callback(__attribute__((unused)) Minecraft *minecraft, __attribute__((unused)) std::string username, Player *player) {
+static void kill_callback(__attribute__((unused)) Minecraft *minecraft, __attribute__((unused)) const std::string &username, Player *player) {
player->hurt(nullptr, INT32_MAX);
INFO("Killed: %s", username.c_str());
}
// List Player
-static void list_callback(Minecraft *minecraft, std::string username, Player *player) {
+static void list_callback(Minecraft *minecraft, const std::string &username, Player *player) {
INFO(" - %s (%s)", username.c_str(), get_player_ip(minecraft, player));
}
@@ -416,7 +410,7 @@ static bool is_ip_in_blacklist(const char *ip) {
} else {
// Check List
for (std::string &x : ips) {
- if (x.compare(ip) == 0) {
+ if (x == ip) {
return true;
}
}
diff --git a/mods/src/skin/skin.cpp b/mods/src/skin/skin.cpp
index f86904df..36e75719 100644
--- a/mods/src/skin/skin.cpp
+++ b/mods/src/skin/skin.cpp
@@ -79,6 +79,10 @@ static int32_t Textures_loadAndBindTexture_injection(Textures *textures, __attri
// Init
void init_skin() {
+ // Not Needed On Headless Mode
+ if (reborn_is_headless()) {
+ return;
+ }
// Check Feature Flag
if (feature_has("Load Custom Skins", server_disabled)) {
// LocalPlayer
diff --git a/mods/src/sound/sound.cpp b/mods/src/sound/sound.cpp
index cb720e36..3d6f2fd8 100644
--- a/mods/src/sound/sound.cpp
+++ b/mods/src/sound/sound.cpp
@@ -117,6 +117,10 @@ static void SoundEngine_init_injection(SoundEngine_init_t original, SoundEngine
// Init
void init_sound() {
+ // Not Needed On Headless Mode
+ if (reborn_is_headless()) {
+ return;
+ }
// Implement Sound Engine
if (feature_has("Implement Sound Engine", server_disabled)) {
overwrite(SoundEngine_playUI, SoundEngine_playUI_injection);
diff --git a/mods/src/test/test.cpp b/mods/src/test/test.cpp
index 46c983c1..7381ac63 100644
--- a/mods/src/test/test.cpp
+++ b/mods/src/test/test.cpp
@@ -10,10 +10,9 @@
void run_tests() {
// Test ~/.minecraft-pi Permissions
{
- char *path = home_get();
+ const char *path = home_get();
int exists = access(path, F_OK) == 0;
int can_write = exists ? access(path, R_OK | W_OK) == 0 : 1;
-
if (!can_write) {
// Failure
ERR("Invalid Data Directory Permissions");
diff --git a/mods/src/textures/headless.cpp b/mods/src/textures/headless.cpp
index 20276ba8..8ff4df20 100644
--- a/mods/src/textures/headless.cpp
+++ b/mods/src/textures/headless.cpp
@@ -1,6 +1,7 @@
#include
#include
-#include
+
+#include "textures-internal.h"
// Disable Texture Loading
static Texture AppPlatform_linux_loadTexture_injection(__attribute__((unused)) AppPlatform_linux_loadTexture_t original, __attribute__((unused)) AppPlatform_linux *app_platform, __attribute__((unused)) std::string *path, __attribute__((unused)) bool b) {
@@ -17,7 +18,7 @@ static Texture AppPlatform_linux_loadTexture_injection(__attribute__((unused)) A
}
// Init
-void init_textures() {
+void _init_textures_headless() {
// Disable Texture Loading
overwrite_calls(AppPlatform_linux_loadTexture, AppPlatform_linux_loadTexture_injection);
}
diff --git a/mods/src/textures/textures-internal.h b/mods/src/textures/textures-internal.h
index 7b4ed7cf..a26e7813 100644
--- a/mods/src/textures/textures-internal.h
+++ b/mods/src/textures/textures-internal.h
@@ -1,3 +1,4 @@
#pragma once
+__attribute__((visibility("internal"))) void _init_textures_headless();
__attribute__((visibility("internal"))) void _init_textures_lava(bool animated_water, bool animated_lava, bool animated_fire);
diff --git a/mods/src/textures/textures.cpp b/mods/src/textures/textures.cpp
index 3dc3f114..7a6155b6 100644
--- a/mods/src/textures/textures.cpp
+++ b/mods/src/textures/textures.cpp
@@ -213,6 +213,12 @@ static Texture AppPlatform_linux_loadTexture_injection(__attribute__((unused)) A
// Init
void init_textures() {
+ // Handle Headless Mode
+ if (reborn_is_headless()) {
+ _init_textures_headless();
+ return;
+ }
+
// Tick Dynamic Textures (Animated Water)
bool animated_water = feature_has("Animated Water", server_disabled);
bool animated_lava = feature_has("Animated Lava", server_disabled);
diff --git a/mods/src/title-screen/welcome.cpp b/mods/src/title-screen/welcome.cpp
index 3e72a73d..5a9fc12b 100644
--- a/mods/src/title-screen/welcome.cpp
+++ b/mods/src/title-screen/welcome.cpp
@@ -10,7 +10,7 @@
#include "title-screen-internal.h"
// Constants
-static std::string line1 = "Welcome to " MCPI_APP_BASE_TITLE " v" MCPI_VERSION "!";
+static std::string line1 = "Welcome to " MCPI_APP_TITLE " v" MCPI_VERSION "!";
static int line_height = 8;
static int button_width = 120;
static int button_height = 24;
diff --git a/scripts/build.mjs b/scripts/build.mjs
index 4fadeda1..af6aac9e 100755
--- a/scripts/build.mjs
+++ b/scripts/build.mjs
@@ -47,10 +47,6 @@ const PackageTypes = wrap(new Enum([
'AppImage',
'Flatpak'
]));
-const Variants = wrap(new Enum([
- 'Client',
- 'Server'
-]));
const Architectures = wrap(new Enum([
'AMD64',
'ARM64',
@@ -67,7 +63,6 @@ let out = path.join(root, 'out');
// Positional Arguments
let argIndex = 2; // Skip First Two Arguments
-const POSITIONAL_ARGUMENT_COUNT = 3;
function readArg(from, type) {
// Check Argument Count
if (argIndex >= process.argv.length) {
@@ -84,8 +79,6 @@ function readArg(from, type) {
}
// Type Of Packaging
const packageType = readArg(PackageTypes, 'Package Type');
-// Build Variant
-const variant = readArg(Variants, 'Variant');
// Build Architecture
const architecture = readArg(Architectures, 'Architecture');
// Flatpak Builds Work Best Without Custom Toolchains
@@ -133,7 +126,7 @@ function updateDir(dir) {
if (packageType !== PackageTypes.None) {
dir = path.join(dir, packageType.name);
}
- return path.join(dir, variant.name, architecture.name);
+ return path.join(dir, architecture.name);
}
build = updateDir(build);
let cleanOut = false;
@@ -147,7 +140,6 @@ if (packageType !== PackageTypes.AppImage) {
function toCmakeBool(val) {
return val ? 'ON' : 'OFF';
}
-options.set('MCPI_SERVER_MODE', toCmakeBool(variant === Variants.Server));
options.set('MCPI_IS_APPIMAGE_BUILD', toCmakeBool(packageType === PackageTypes.AppImage));
options.set('MCPI_IS_FLATPAK_BUILD', toCmakeBool(packageType === PackageTypes.Flatpak));
if (architecture !== Architectures.Host) {
diff --git a/scripts/fix-appimage-for-docker.sh b/scripts/fix-appimage-for-docker.sh
new file mode 100755
index 00000000..1c9988de
--- /dev/null
+++ b/scripts/fix-appimage-for-docker.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+exec sed -i '0,/AI\x02/{s|AI\x02|\x00\x00\x00|}' "$1"
\ No newline at end of file
diff --git a/scripts/install-dependencies.sh b/scripts/install-dependencies.sh
index e40a9a49..3d2fcf11 100755
--- a/scripts/install-dependencies.sh
+++ b/scripts/install-dependencies.sh
@@ -9,75 +9,44 @@ if [ "$(id -u)" -eq 0 ]; then
}
fi
-# Main Script
-run() {
- # Add ARM Repository
- for arch in "$@"; do
- sudo dpkg --add-architecture "$(echo "${arch}" | tr '[:upper:]' '[:lower:]')"
- done
+# Run APT
+install_pkg() {
+ sudo apt-get install --no-install-recommends -y "$@"
+}
- # Update APT
- sudo apt-get update
- sudo apt-get dist-upgrade -y
-
- # Install Everything In One Go
- PKG_QUEUE=''
- queue_pkg() {
- PKG_QUEUE="${PKG_QUEUE} $@"
- }
-
- # Build System
- queue_pkg \
+# Build Dependencies
+run_build() {
+ install_pkg \
+ `# Build System` \
git \
cmake \
ninja-build \
python3 \
- python3-venv
-
- # Host Dependencies Needed For Compile
- queue_pkg \
- libwayland-bin
-
- # Architecture-Specific Dependencies
- architecture_specific_pkg() {
- # Compiler
- queue_pkg crossbuild-essential-$1
-
- # Dependencies
- queue_pkg \
- libopenal-dev:$1
-
- # GLFW Dependencies
- queue_pkg \
- libwayland-dev:$1 \
- libxkbcommon-dev:$1 \
- libx11-dev:$1 \
- libxcursor-dev:$1 \
- libxi-dev:$1 \
- libxinerama-dev:$1 \
- libxrandr-dev:$1 \
- libxext-dev:$1
-
- # Zenity Dependencies
- queue_pkg \
- libgtk-3-dev:$1 \
- libglib2.0-dev:$1
- }
- for arch in "$@"; do
- architecture_specific_pkg "$(echo "${arch}" | tr '[:upper:]' '[:lower:]')"
- done
-
- # AppStream Verification
- queue_pkg \
+ python3-venv \
+ `# Host Dependencies Needed For Compile` \
+ libwayland-bin \
+ `# Compiler` \
+ "crossbuild-essential-$1" \
+ `# Main Dependencies` \
+ "libopenal-dev:$1" \
+ `# GLFW Dependencies` \
+ "libwayland-dev:$1" \
+ "libxkbcommon-dev:$1" \
+ "libx11-dev:$1" \
+ "libxcursor-dev:$1" \
+ "libxi-dev:$1" \
+ "libxinerama-dev:$1" \
+ "libxrandr-dev:$1" \
+ "libxext-dev:$1" \
+ `# Zenity Dependencies` \
+ "libgtk-3-dev:$1" \
+ "libglib2.0-dev:$1" \
+ `# AppStream Verification` \
appstream
- # Install Queue
- sudo apt-get install --no-install-recommends -y ${PKG_QUEUE}
-
# Install appimagetool
- sudo rm -rf /opt/squashfs-root /opt/appimagetool.AppDir
- sudo rm -f /opt/appimagetool /usr/local/bin/appimagetool
- case "$(dpkg-architecture -qDEB_BUILD_ARCH)" in
+ sudo rm -rf /opt/squashfs-root /opt/appimagetool /usr/local/bin/appimagetool
+ case "$(dpkg --print-architecture)" in
'armhf') APPIMAGE_ARCH='armhf';;
'arm64') APPIMAGE_ARCH='aarch64';;
'i386') APPIMAGE_ARCH='i686';;
@@ -87,19 +56,43 @@ run() {
sudo wget -O /opt/appimagetool "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-${APPIMAGE_ARCH}.AppImage"
sudo chmod +x /opt/appimagetool
# Workaround AppImage Issues With Docker
- cd /opt
- sudo sed -i '0,/AI\x02/{s|AI\x02|\x00\x00\x00|}' ./appimagetool
+ sudo ./scripts/fix-appimage-for-docker.sh /opt/appimagetool
# Extract
- sudo ./appimagetool --appimage-extract
+ cd /opt
+ sudo ./appimagetool --appimage-extract > /dev/null
sudo rm -f ./appimagetool
# Link
- sudo mv ./squashfs-root ./appimagetool.AppDir
- sudo ln -s /opt/appimagetool.AppDir/AppRun /usr/local/bin/appimagetool
+ sudo mv ./squashfs-root ./appimagetool
+ sudo ln -s /opt/appimagetool/AppRun /usr/local/bin/appimagetool
}
-# Run
-if [ "$#" -lt 1 ]; then
- run "$(dpkg-architecture -qDEB_BUILD_ARCH)"
-else
- run "$@"
-fi
+# Test Dependencies
+run_test() {
+ sudo apt-get install --no-install-recommends -y \
+ "libc6:$1" \
+ "libopenal1:$1" \
+ "libglib2.0-0:$1"
+}
+
+# Example Mods Dependencies
+run_example_mods() {
+ install_pkg \
+ cmake \
+ ninja-build \
+ g++-arm-linux-gnueabihf \
+ gcc-arm-linux-gnueabihf
+}
+
+# Variables
+MODE="$1"
+ARCH="$(echo "$2" | tr '[:upper:]' '[:lower:]')"
+
+# Add ARM Repository
+sudo dpkg --add-architecture "${ARCH}"
+
+# Update APT
+sudo apt-get update
+sudo apt-get dist-upgrade -y
+
+# Install Packages
+"run_${MODE}" "${ARCH}"
diff --git a/scripts/simulate-ci.sh b/scripts/simulate-ci.sh
deleted file mode 100755
index 77c194a1..00000000
--- a/scripts/simulate-ci.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/sh
-
-set -e
-
-# Change Directory
-cd "$(dirname "$0")/../"
-
-# Run
-act push -W '.gitea/workflows/build.yml'
diff --git a/scripts/test.sh b/scripts/test.sh
index 57299fb4..47c10d00 100755
--- a/scripts/test.sh
+++ b/scripts/test.sh
@@ -7,29 +7,35 @@ cd "$(dirname "$0")/../"
# Variables
MODE="$(echo "$1" | tr '[:upper:]' '[:lower:]')"
-ARCH='host'
+ARCH="$(echo "$2" | tr '[:upper:]' '[:lower:]')"
+APPIMAGE="$(pwd)/out/minecraft-pi-reborn-$(cat VERSION)-${ARCH}.AppImage"
-# Build
-./scripts/build.mjs none "${MODE}" "${ARCH}" -DMCPI_HEADLESS_MODE=ON
-
-# Add To PATH
-export PATH="$(pwd)/out/${MODE}/${ARCH}/usr/bin:${PATH}"
+# Check If File Exists
+if [ ! -f "${APPIMAGE}" ]; then
+ echo 'Missing AppImage!' > /dev/stderr
+ exit 1
+fi
# Make Test Directory
TEST_WORKING_DIR="$(pwd)/.testing-tmp"
rm -rf "${TEST_WORKING_DIR}"
mkdir -p "${TEST_WORKING_DIR}"
+ROOT="$(pwd)"
+cd "${TEST_WORKING_DIR}"
+
+# Prepare AppImage For Docker
+cp "${APPIMAGE}" tmp.AppImage
+"${ROOT}/scripts/fix-appimage-for-docker.sh" tmp.AppImage
+chmod +x tmp.AppImage
# Run
if [ "${MODE}" = "server" ]; then
# Server Test
- cd "${TEST_WORKING_DIR}"
- minecraft-pi-reborn-server --only-generate
+ ./tmp.AppImage --appimage-extract-and-run --server --only-generate
else
# Client Test
- export _MCPI_SKIP_ROOT_CHECK=1
export HOME="${TEST_WORKING_DIR}"
- minecraft-pi-reborn-client --default --no-cache --benchmark
+ ./tmp.AppImage --appimage-extract-and-run --default --no-cache --benchmark --force-headless
fi
# Clean Up