The Great Unification
This commit is contained in:
parent
460cd38ddf
commit
38d7dda1a7
@ -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
|
||||
|
@ -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)
|
||||
endif()
|
||||
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")
|
||||
|
10
dependencies/CMakeLists.txt
vendored
10
dependencies/CMakeLists.txt
vendored
@ -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
|
||||
|
8
dependencies/minecraft-pi/CMakeLists.txt
vendored
8
dependencies/minecraft-pi/CMakeLists.txt
vendored
@ -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")
|
||||
|
2
dependencies/runtime/src
vendored
2
dependencies/runtime/src
vendored
@ -1 +1 @@
|
||||
Subproject commit 790e7918b1d63102b7a7f39dc1db006b2a5abf48
|
||||
Subproject commit aa874884072a700750956e241c7a1ce91dbfa74c
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
```
|
||||
</details>
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
||||
# Chest Model
|
||||
if(NOT MCPI_HEADLESS_MODE)
|
||||
install(
|
||||
FILES "chest.png"
|
||||
DESTINATION "${MCPI_INSTALL_DIR}/data/images/item"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Icon
|
||||
install(
|
||||
|
@ -10,15 +10,11 @@ add_executable(launcher
|
||||
src/mods.cpp
|
||||
src/options/parser.cpp
|
||||
src/main.cpp
|
||||
)
|
||||
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()
|
||||
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"
|
||||
" <releases>\n"
|
||||
" <release version=\"${MCPI_VERSION}\" date=\"${MCPI_VERSION_DATE}\"></release>\n"
|
||||
" </releases>\n"
|
||||
)
|
||||
if(NOT MCPI_HEADLESS_MODE)
|
||||
file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/appstream.xml"
|
||||
" <screenshots>\n"
|
||||
" <screenshot type=\"default\">\n"
|
||||
" <image>https://gitea.thebrokenrail.com/TheBrokenRail/minecraft-pi-reborn/raw/branch/master/images/start.png</image>\n"
|
||||
" </screenshot>\n"
|
||||
" </screenshots>\n"
|
||||
)
|
||||
endif()
|
||||
file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/appstream.xml"
|
||||
"</component>\n"
|
||||
)
|
||||
install(
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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<std::string> command) {
|
||||
reborn_check_display();
|
||||
// Create Full Command
|
||||
std::vector<std::string> 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);
|
||||
}
|
||||
// Wipe Cache If Needed
|
||||
if (options.wipe_cache) {
|
||||
wipe_cache();
|
||||
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");
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
|
@ -16,8 +16,5 @@ void load_available_feature_flags(const std::function<void(std::string)> &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);
|
@ -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 <a href=\"" MCPI_DISCORD_INVITE "\">Discord server</a>! <i>If you believe this is a problem with " MCPI_APP_BASE_TITLE " itself, please upload this crash report to the #bugs Discord channel.</i>",
|
||||
"--text", MCPI_APP_TITLE " has crashed!\n\nNeed help? Consider asking on the <a href=\"" MCPI_DISCORD_INVITE "\">Discord server</a>! <i>If you believe this is a problem with " MCPI_APP_TITLE " itself, please upload this crash report to the #bugs Discord channel.</i>",
|
||||
"--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);
|
||||
|
@ -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,15 +85,15 @@ static void start_game(const options_t &options) {
|
||||
sigaction(SIGTERM, &act_sigterm, nullptr);
|
||||
|
||||
// Setup Home
|
||||
#ifndef MCPI_SERVER_MODE
|
||||
if (!reborn_is_server()) {
|
||||
// Ensure $HOME
|
||||
const char *home = getenv("HOME");
|
||||
if (home == nullptr) {
|
||||
ERR("$HOME Isn't Set");
|
||||
ERR("$HOME Is Not Set");
|
||||
}
|
||||
// Create If Needed
|
||||
{
|
||||
std::string minecraft_folder = std::string(home) + HOME_SUBDIRECTORY_FOR_GAME_DATA;
|
||||
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) {
|
||||
@ -110,17 +103,17 @@ static void start_game(const options_t &options) {
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
} else {
|
||||
// Set Home To Current Directory, So World Data Is Stored There
|
||||
char *launch_directory = getcwd(NULL, 0);
|
||||
char *launch_directory = getcwd(nullptr, 0);
|
||||
set_and_print_env("HOME", launch_directory);
|
||||
free(launch_directory);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Configure Client Options
|
||||
#ifndef MCPI_SERVER_MODE
|
||||
if (!reborn_is_server()) {
|
||||
configure_client(options);
|
||||
#endif
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
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")
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -5,5 +5,4 @@
|
||||
#include "util.h"
|
||||
#include "string.h"
|
||||
#include "exec.h"
|
||||
#include "home.h"
|
||||
#include "patch.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); \
|
||||
|
@ -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 "";
|
||||
}
|
||||
}
|
@ -1,17 +1,17 @@
|
||||
project(media-layer-core)
|
||||
|
||||
# OpenGL
|
||||
if(NOT MCPI_HEADLESS_MODE)
|
||||
add_subdirectory(gles)
|
||||
endif()
|
||||
|
||||
# Configuration
|
||||
set(CORE_SRC src/base.cpp src/media.c $<TARGET_OBJECTS:media-layer-extras>) # 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
|
||||
$<TARGET_OBJECTS:media-layer-extras>
|
||||
)
|
||||
|
||||
# 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()
|
||||
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
|
||||
)
|
||||
|
@ -1,6 +0,0 @@
|
||||
#include <media-layer/audio.h>
|
||||
|
||||
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) {
|
||||
}
|
@ -1,861 +0,0 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include <SDL/SDL.h>
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
#ifndef MCPI_HEADLESS_MODE
|
||||
#include <time.h>
|
||||
|
||||
#define GLFW_INCLUDE_NONE
|
||||
#include <GLFW/glfw3.h>
|
||||
#endif
|
||||
|
||||
#include <media-layer/core.h>
|
||||
#include <media-layer/internal.h>
|
||||
|
||||
#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;
|
||||
}
|
561
media-layer/core/src/media.cpp
Normal file
561
media-layer/core/src/media.cpp
Normal file
@ -0,0 +1,561 @@
|
||||
#include <ctime>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <SDL/SDL.h>
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
#define GLFW_INCLUDE_NONE
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include <media-layer/core.h>
|
||||
#include <media-layer/internal.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
@ -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}")
|
||||
|
@ -46,34 +46,6 @@ set(SRC
|
||||
src/text-input-box/TextInputScreen.cpp
|
||||
# test
|
||||
src/test/test.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
|
||||
@ -86,7 +58,6 @@ else()
|
||||
src/input/toggle.cpp
|
||||
src/input/misc.cpp
|
||||
src/input/drop.cpp
|
||||
src/input/crafting.cpp
|
||||
# sign
|
||||
src/sign/sign.cpp
|
||||
# atlas
|
||||
@ -103,15 +74,24 @@ else()
|
||||
# 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
|
||||
)
|
||||
# Install Splashes
|
||||
install(
|
||||
FILES "src/title-screen/splashes.txt"
|
||||
DESTINATION "${MCPI_INSTALL_DIR}/data"
|
||||
)
|
||||
endif()
|
||||
|
||||
# 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(
|
||||
|
@ -1,16 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
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)
|
||||
|
5
mods/include/mods/game-mode/game-mode.h
Normal file
5
mods/include/mods/game-mode/game-mode.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
int get_seed_from_string(std::string str);
|
@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
extern "C" {
|
||||
char *home_get();
|
||||
const char *home_get();
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
extern "C" {
|
||||
void screenshot_take(char *home);
|
||||
void screenshot_take(const char *home);
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
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);
|
||||
#endif
|
||||
}
|
||||
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);
|
||||
|
@ -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
|
||||
|
@ -10,15 +10,12 @@
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
#include <SDL/SDL.h>
|
||||
|
||||
#ifndef MCPI_HEADLESS_MODE
|
||||
#include <media-layer/core.h>
|
||||
|
||||
#include <mods/input/input.h>
|
||||
#include <mods/sign/sign.h>
|
||||
#include <mods/chat/chat.h>
|
||||
#include <mods/home/home.h>
|
||||
#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);
|
||||
|
@ -6,7 +6,6 @@
|
||||
#include <mods/misc/misc.h>
|
||||
#include <mods/creative/creative.h>
|
||||
|
||||
#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.
|
||||
|
@ -6,7 +6,11 @@
|
||||
#include <mods/feature/feature.h>
|
||||
|
||||
// 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 : "");
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
#include <symbols/minecraft.h>
|
||||
#include <libreborn/libreborn.h>
|
||||
@ -7,6 +8,7 @@
|
||||
#include <mods/text-input-box/TextInputScreen.h>
|
||||
#include <mods/touch/touch.h>
|
||||
#include <mods/misc/misc.h>
|
||||
#include <mods/game-mode/game-mode.h>
|
||||
#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);
|
||||
|
@ -7,24 +7,20 @@
|
||||
#include <mods/init/init.h>
|
||||
|
||||
// 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.
|
||||
|
@ -8,12 +8,11 @@ __attribute__((constructor)) static void init() {
|
||||
run_tests();
|
||||
init_version();
|
||||
init_compat();
|
||||
#ifdef MCPI_SERVER_MODE
|
||||
if (reborn_is_server()) {
|
||||
init_server();
|
||||
#else
|
||||
} else {
|
||||
init_multiplayer();
|
||||
#endif
|
||||
#ifndef MCPI_HEADLESS_MODE
|
||||
}
|
||||
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
|
||||
if (!reborn_is_server()) {
|
||||
init_benchmark();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -1,30 +0,0 @@
|
||||
#include <libreborn/libreborn.h>
|
||||
#include <symbols/minecraft.h>
|
||||
|
||||
#include "input-internal.h"
|
||||
#include <mods/input/input.h>
|
||||
#include <mods/creative/creative.h>
|
||||
|
||||
// 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);
|
||||
}
|
@ -5,4 +5,3 @@ __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();
|
@ -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);
|
||||
|
@ -3,9 +3,7 @@
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
#include <symbols/minecraft.h>
|
||||
#ifndef MCPI_HEADLESS_MODE
|
||||
#include <GLES/gl.h>
|
||||
#endif
|
||||
|
||||
#include <mods/misc/misc.h>
|
||||
#include "misc-internal.h"
|
||||
@ -95,9 +93,7 @@ void misc_run_on_game_key_press(const std::function<bool(Minecraft *, int)> &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;
|
||||
|
@ -9,9 +9,7 @@
|
||||
#include <streambuf>
|
||||
#include <algorithm>
|
||||
|
||||
#ifndef MCPI_HEADLESS_MODE
|
||||
#include <GLES/gl.h>
|
||||
#endif
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
#include <symbols/minecraft.h>
|
||||
@ -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) {
|
||||
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 <typename... Args>
|
||||
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<Common_sleepMs_t, int>);
|
||||
}
|
||||
|
||||
#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<GameRenderer_render_t, GameRenderer *, float>);
|
||||
overwrite_calls(NinecraftApp_initGLStates, nop<NinecraftApp_initGLStates_t, NinecraftApp *>);
|
||||
overwrite_calls(Gui_onConfigChanged, nop<Gui_onConfigChanged_t, Gui *, Config *>);
|
||||
overwrite_calls(LevelRenderer_generateSky, nop<LevelRenderer_generateSky_t, LevelRenderer *>);
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,10 @@
|
||||
// Config Needs To Load First
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
#ifdef MCPI_SERVER_MODE
|
||||
#error "External Server Code Requires Client Mode"
|
||||
#endif
|
||||
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <symbols/minecraft.h>
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
#include <mods/home/home.h>
|
||||
#include <mods/init/init.h>
|
||||
@ -21,7 +15,7 @@ struct server_list_entry {
|
||||
std::string address;
|
||||
int port;
|
||||
};
|
||||
static std::vector<struct server_list_entry> server_list_entries;
|
||||
static std::vector<server_list_entry> 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<void(const char *address, int port)> callback) {
|
||||
static void iterate_servers(const std::function<void(const char *address, int port)> &callback) {
|
||||
// Load
|
||||
if (!server_list_loaded) {
|
||||
load_servers();
|
||||
@ -106,8 +94,7 @@ static void iterate_servers(std::function<void(const char *address, int port)> c
|
||||
}
|
||||
|
||||
// Loop
|
||||
for (std::vector<struct server_list_entry>::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);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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<std::string> 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() {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -1,12 +1,5 @@
|
||||
// Config Needs To Load First
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
#ifndef MCPI_SERVER_MODE
|
||||
#error "Server Code Requires Server Mode"
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
@ -19,15 +12,16 @@
|
||||
|
||||
#include <SDL/SDL.h>
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
#include <symbols/minecraft.h>
|
||||
|
||||
#include <mods/server/server.h>
|
||||
|
||||
#include <mods/feature/feature.h>
|
||||
#include <mods/init/init.h>
|
||||
#include <mods/home/home.h>
|
||||
#include <mods/compat/compat.h>
|
||||
#include <mods/misc/misc.h>
|
||||
#include <mods/game-mode/game-mode.h>
|
||||
|
||||
// --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<Player *> 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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <libreborn/libreborn.h>
|
||||
#include <symbols/minecraft.h>
|
||||
#include <mods/init/init.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
3
scripts/fix-appimage-for-docker.sh
Executable file
3
scripts/fix-appimage-for-docker.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
exec sed -i '0,/AI\x02/{s|AI\x02|\x00\x00\x00|}' "$1"
|
@ -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
|
||||
|
||||
# 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} $@"
|
||||
# Run APT
|
||||
install_pkg() {
|
||||
sudo apt-get install --no-install-recommends -y "$@"
|
||||
}
|
||||
|
||||
# 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}"
|
||||
|
@ -1,9 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
# Change Directory
|
||||
cd "$(dirname "$0")/../"
|
||||
|
||||
# Run
|
||||
act push -W '.gitea/workflows/build.yml'
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user