The Great Unification
This commit is contained in:
parent
460cd38ddf
commit
38d7dda1a7
@ -14,9 +14,6 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
mode:
|
|
||||||
- Client
|
|
||||||
- Server
|
|
||||||
arch:
|
arch:
|
||||||
- AMD64
|
- AMD64
|
||||||
- ARM64
|
- ARM64
|
||||||
@ -31,40 +28,19 @@ jobs:
|
|||||||
submodules: true
|
submodules: true
|
||||||
# Dependencies
|
# Dependencies
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: ./scripts/install-dependencies.sh ${{ matrix.arch }}
|
run: ./scripts/install-dependencies.sh build ${{ matrix.arch }}
|
||||||
# Build
|
# Build
|
||||||
- name: Build
|
- name: Build
|
||||||
run: ./scripts/build.mjs appimage ${{ matrix.mode }} ${{ matrix.arch }}
|
run: ./scripts/build.mjs appimage ${{ matrix.arch }}
|
||||||
- name: Upload Artifacts
|
- name: Upload Artifacts
|
||||||
uses: christopherhx/gitea-upload-artifact@v4
|
uses: christopherhx/gitea-upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.mode }} (${{ matrix.arch }})
|
name: ${{ matrix.arch }}
|
||||||
path: ./out/*.AppImage*
|
path: ./out/*.AppImage*
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
# Test Project
|
# Test Project
|
||||||
test:
|
test:
|
||||||
strategy:
|
needs: build
|
||||||
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:
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@ -72,10 +48,11 @@ jobs:
|
|||||||
- Client
|
- Client
|
||||||
- Server
|
- Server
|
||||||
arch:
|
arch:
|
||||||
|
- AMD64
|
||||||
- ARM64
|
- ARM64
|
||||||
- ARMHF
|
- ARMHF
|
||||||
name: Raspberry Pi Test
|
name: Test
|
||||||
runs-on: raspberry-pi
|
runs-on: ${{ startsWith(matrix.arch, 'ARM') && 'raspberry-pi' || 'ubuntu-latest' }}
|
||||||
container: node:lts-bullseye
|
container: node:lts-bullseye
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
@ -84,12 +61,19 @@ jobs:
|
|||||||
submodules: true
|
submodules: true
|
||||||
# Dependencies
|
# Dependencies
|
||||||
- name: Install 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
|
# Test
|
||||||
- name: Test
|
- name: Test
|
||||||
run: ./scripts/test.sh ${{ matrix.mode }}
|
run: ./scripts/test.sh ${{ matrix.mode }} ${{ matrix.arch }}
|
||||||
# Example Mods
|
# Example Mods
|
||||||
example-mods:
|
example-mods:
|
||||||
|
needs: build
|
||||||
name: Build Example Mods
|
name: Build Example Mods
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: node:lts-bullseye
|
container: node:lts-bullseye
|
||||||
@ -100,16 +84,18 @@ jobs:
|
|||||||
submodules: true
|
submodules: true
|
||||||
# Dependencies
|
# Dependencies
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: ./scripts/install-dependencies.sh
|
run: ./scripts/install-dependencies.sh example_mods amd64
|
||||||
- name: Install ARM Toolchain
|
# SDK
|
||||||
run: apt-get install --no-install-recommends -y g++-arm-linux-gnueabihf gcc-arm-linux-gnueabihf
|
- name: Download SDK
|
||||||
# Build SDK
|
uses: christopherhx/gitea-download-artifact@v4
|
||||||
- name: Build SDK
|
with:
|
||||||
|
name: AMD64
|
||||||
|
path: out
|
||||||
|
- name: Extract SDK
|
||||||
run: |
|
run: |
|
||||||
./scripts/build.mjs none client host
|
./scripts/fix-appimage-for-docker.sh ./out/*.AppImage
|
||||||
export _MCPI_SKIP_ROOT_CHECK=1
|
chmod +x ./out/*.AppImage
|
||||||
export DISPLAY=
|
./out/*.AppImage --copy-sdk
|
||||||
./out/client/host/usr/bin/minecraft-pi-reborn-client --copy-sdk
|
|
||||||
# Build Example Mods
|
# Build Example Mods
|
||||||
- name: Build Example Mods
|
- name: Build Example Mods
|
||||||
run: ./example-mods/build.sh
|
run: ./example-mods/build.sh
|
||||||
@ -122,7 +108,10 @@ jobs:
|
|||||||
# Create Release
|
# Create Release
|
||||||
release:
|
release:
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
needs: build
|
needs:
|
||||||
|
- build
|
||||||
|
- test
|
||||||
|
- example-mods
|
||||||
name: Release
|
name: Release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: node:lts-bullseye
|
container: node:lts-bullseye
|
||||||
|
@ -6,10 +6,6 @@ if(MCPI_IS_APPIMAGE_BUILD AND MCPI_IS_FLATPAK_BUILD)
|
|||||||
message(FATAL_ERROR "Invalid Build Configuration")
|
message(FATAL_ERROR "Invalid Build Configuration")
|
||||||
endif()
|
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
|
# Prebuilt ARMHF Toolchain
|
||||||
if(BUILD_NATIVE_COMPONENTS)
|
if(BUILD_NATIVE_COMPONENTS)
|
||||||
set(MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN FALSE)
|
set(MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN FALSE)
|
||||||
@ -22,16 +18,12 @@ if(BUILD_NATIVE_COMPONENTS)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Media Layer
|
# Media Layer
|
||||||
if(NOT MCPI_HEADLESS_MODE)
|
set(DEFAULT_USE_MEDIA_LAYER_TRAMPOLINE FALSE)
|
||||||
set(DEFAULT_USE_MEDIA_LAYER_TRAMPOLINE FALSE)
|
if(BUILD_NATIVE_COMPONENTS AND NOT IS_ARM_TARGETING)
|
||||||
if(BUILD_NATIVE_COMPONENTS AND NOT IS_ARM_TARGETING)
|
set(DEFAULT_USE_MEDIA_LAYER_TRAMPOLINE TRUE)
|
||||||
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()
|
endif()
|
||||||
|
mcpi_option(USE_MEDIA_LAYER_TRAMPOLINE "Whether To Enable The Media Layer Trampoline" BOOL "${DEFAULT_USE_MEDIA_LAYER_TRAMPOLINE}")
|
||||||
|
mcpi_option(USE_GLES1_COMPATIBILITY_LAYER "Whether To Enable The GLESv1_CM Compatibility Layer" BOOL TRUE)
|
||||||
if(MCPI_USE_MEDIA_LAYER_TRAMPOLINE)
|
if(MCPI_USE_MEDIA_LAYER_TRAMPOLINE)
|
||||||
set(BUILD_MEDIA_LAYER_CORE "${BUILD_NATIVE_COMPONENTS}")
|
set(BUILD_MEDIA_LAYER_CORE "${BUILD_NATIVE_COMPONENTS}")
|
||||||
else()
|
else()
|
||||||
@ -40,30 +32,12 @@ endif()
|
|||||||
|
|
||||||
# Specify Variant Name
|
# Specify Variant Name
|
||||||
set(MCPI_VARIANT_NAME "minecraft-pi-reborn")
|
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
|
# App ID
|
||||||
set(DEFAULT_APP_ID "com.thebrokenrail.MCPIReborn")
|
mcpi_option(APP_ID "App ID" STRING "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}")
|
|
||||||
|
|
||||||
# App Title
|
# App Title
|
||||||
mcpi_option(APP_BASE_TITLE "Base App Title" STRING "Minecraft: Pi Edition: Reborn")
|
mcpi_option(APP_TITLE "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}")
|
|
||||||
|
|
||||||
# Skin Server
|
# Skin Server
|
||||||
mcpi_option(SKIN_SERVER "Skin Server" STRING "https://raw.githubusercontent.com/MCPI-Revival/Skins/data")
|
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)
|
project(dependencies)
|
||||||
|
|
||||||
# stb_image
|
# stb_image
|
||||||
if(BUILD_ARM_COMPONENTS AND NOT MCPI_HEADLESS_MODE)
|
if(BUILD_ARM_COMPONENTS)
|
||||||
add_subdirectory(stb_image)
|
add_subdirectory(stb_image)
|
||||||
endif()
|
endif()
|
||||||
# Minecraft: Pi Edition
|
# Minecraft: Pi Edition
|
||||||
@ -9,21 +9,21 @@ if(BUILD_ARM_COMPONENTS AND NOT MCPI_OPEN_SOURCE_ONLY)
|
|||||||
add_subdirectory(minecraft-pi)
|
add_subdirectory(minecraft-pi)
|
||||||
endif()
|
endif()
|
||||||
# Zenity (Minimal Build)
|
# Zenity (Minimal Build)
|
||||||
if(BUILD_NATIVE_COMPONENTS AND NOT MCPI_SERVER_MODE)
|
if(BUILD_NATIVE_COMPONENTS)
|
||||||
add_subdirectory(zenity)
|
add_subdirectory(zenity)
|
||||||
endif()
|
endif()
|
||||||
# LIEF
|
# 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)
|
add_subdirectory(LIEF)
|
||||||
endif()
|
endif()
|
||||||
# Extra Runtime
|
# Extra Runtime
|
||||||
add_subdirectory(runtime)
|
add_subdirectory(runtime)
|
||||||
# GLFW
|
# GLFW
|
||||||
if(BUILD_MEDIA_LAYER_CORE AND NOT MCPI_HEADLESS_MODE)
|
if(BUILD_MEDIA_LAYER_CORE)
|
||||||
add_subdirectory(glfw)
|
add_subdirectory(glfw)
|
||||||
endif()
|
endif()
|
||||||
# GLES Compatibility Layer
|
# 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)
|
add_subdirectory(gles-compatibility-layer)
|
||||||
endif()
|
endif()
|
||||||
# UTF8-CPP
|
# 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"
|
DESTINATION "${MCPI_INSTALL_DIR}/game"
|
||||||
USE_SOURCE_PERMISSIONS
|
USE_SOURCE_PERMISSIONS
|
||||||
REGEX "api" EXCLUDE
|
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")
|
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
|
* Fix Furnace Visual Bug When Using Lava Bucket As Fuel
|
||||||
* Add Splash Text To Start Screen
|
* Add Splash Text To Start Screen
|
||||||
* `overwrite_calls` Now Scans VTables
|
* `overwrite_calls` Now Scans VTables
|
||||||
|
* Unify Server/Client Builds
|
||||||
|
* Use `|` For Separator In `servers.txt` Instead Of `:`
|
||||||
|
|
||||||
**2.5.3**
|
**2.5.3**
|
||||||
* Add `Replace Block Highlight With Outline` Feature Flag (Enabled By Default)
|
* 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].
|
This server is also compatible with MCPE Alpha v0.6.1[^1].
|
||||||
|
|
||||||
## Setup
|
## 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
|
## Server Limitations
|
||||||
* Player data is not saved because of limitations with MCPE LAN worlds
|
* 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
|
* 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:
|
It also requires some additional packages. To install them, run:
|
||||||
```sh
|
```sh
|
||||||
sudo apt install -y libfuse2 libgtk-3-0 libopenal1
|
sudo apt install -y libfuse2 libgtk-3-0 libopenal1 libglib2.0-0
|
||||||
```
|
```
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
@ -1,19 +1,11 @@
|
|||||||
# Example Mods
|
# 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).
|
* **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.
|
* **Recipes Mod**: This specific mod demos custom recipes.
|
||||||
|
|
||||||
## The SDK
|
## The SDK
|
||||||
The modding SDK is a collection of exported CMake targets that allows anyone to create their own MCPI mod!
|
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.
|
The SDK is copied to `~/.minecraft-pi/sdk` whenever MCPI-Reborn is started.
|
||||||
|
|
||||||
## How do I use this?
|
|
||||||
```sh
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake ..
|
|
||||||
cp libexpanded-creative.so ~/.minecraft-pi/mods
|
|
||||||
```
|
|
||||||
|
@ -10,7 +10,7 @@ set(CMAKE_SYSTEM_PROCESSOR "arm")
|
|||||||
project(chat-commands)
|
project(chat-commands)
|
||||||
|
|
||||||
# Include SDK
|
# 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
|
# Build
|
||||||
add_library(chat-commands SHARED chat-commands.cpp)
|
add_library(chat-commands SHARED chat-commands.cpp)
|
||||||
|
@ -10,7 +10,7 @@ set(CMAKE_SYSTEM_PROCESSOR "arm")
|
|||||||
project(expanded-creative)
|
project(expanded-creative)
|
||||||
|
|
||||||
# Include SDK
|
# 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
|
# Build
|
||||||
add_library(expanded-creative SHARED expanded-creative.cpp)
|
add_library(expanded-creative SHARED expanded-creative.cpp)
|
||||||
|
@ -10,7 +10,7 @@ set(CMAKE_SYSTEM_PROCESSOR "arm")
|
|||||||
project(recipes)
|
project(recipes)
|
||||||
|
|
||||||
# Include SDK
|
# 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
|
# Build
|
||||||
add_library(recipes SHARED recipes.cpp)
|
add_library(recipes SHARED recipes.cpp)
|
||||||
|
@ -1,21 +1,17 @@
|
|||||||
project(images)
|
project(images)
|
||||||
|
|
||||||
# Title Background
|
# Title Background
|
||||||
if(NOT MCPI_HEADLESS_MODE)
|
install(
|
||||||
install(
|
FILES "background.png"
|
||||||
FILES "background.png"
|
DESTINATION "${MCPI_INSTALL_DIR}/data/images/gui"
|
||||||
DESTINATION "${MCPI_INSTALL_DIR}/data/images/gui"
|
RENAME "titleBG.png"
|
||||||
RENAME "titleBG.png"
|
)
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Chest Model
|
# Chest Model
|
||||||
if(NOT MCPI_HEADLESS_MODE)
|
install(
|
||||||
install(
|
FILES "chest.png"
|
||||||
FILES "chest.png"
|
DESTINATION "${MCPI_INSTALL_DIR}/data/images/item"
|
||||||
DESTINATION "${MCPI_INSTALL_DIR}/data/images/item"
|
)
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Icon
|
# Icon
|
||||||
install(
|
install(
|
||||||
|
@ -10,15 +10,11 @@ add_executable(launcher
|
|||||||
src/mods.cpp
|
src/mods.cpp
|
||||||
src/options/parser.cpp
|
src/options/parser.cpp
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
|
src/client/configuration.cpp
|
||||||
|
src/client/cache.cpp
|
||||||
|
src/client/available-feature-flags # Show In IDE
|
||||||
)
|
)
|
||||||
if(NOT MCPI_SERVER_MODE)
|
embed_resource(launcher src/client/available-feature-flags)
|
||||||
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()
|
|
||||||
target_link_libraries(launcher reborn-util LIB_LIEF trampoline-headers)
|
target_link_libraries(launcher reborn-util LIB_LIEF trampoline-headers)
|
||||||
# RPath
|
# RPath
|
||||||
set_target_properties(launcher PROPERTIES INSTALL_RPATH "$ORIGIN/lib/native")
|
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"
|
"Type=Application\n"
|
||||||
"Categories=Game;\n"
|
"Categories=Game;\n"
|
||||||
)
|
)
|
||||||
if(MCPI_HEADLESS_MODE)
|
file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/launcher.desktop"
|
||||||
file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/launcher.desktop"
|
"Terminal=true\n"
|
||||||
"Terminal=true\n"
|
"NoDisplay=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(
|
install(
|
||||||
FILES "${CMAKE_CURRENT_BINARY_DIR}/launcher.desktop"
|
FILES "${CMAKE_CURRENT_BINARY_DIR}/launcher.desktop"
|
||||||
DESTINATION "${MCPI_SHARE_DIR}/applications"
|
DESTINATION "${MCPI_SHARE_DIR}/applications"
|
||||||
@ -100,17 +88,11 @@ file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/appstream.xml"
|
|||||||
" <releases>\n"
|
" <releases>\n"
|
||||||
" <release version=\"${MCPI_VERSION}\" date=\"${MCPI_VERSION_DATE}\"></release>\n"
|
" <release version=\"${MCPI_VERSION}\" date=\"${MCPI_VERSION_DATE}\"></release>\n"
|
||||||
" </releases>\n"
|
" </releases>\n"
|
||||||
)
|
" <screenshots>\n"
|
||||||
if(NOT MCPI_HEADLESS_MODE)
|
" <screenshot type=\"default\">\n"
|
||||||
file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/appstream.xml"
|
" <image>https://gitea.thebrokenrail.com/TheBrokenRail/minecraft-pi-reborn/raw/branch/master/images/start.png</image>\n"
|
||||||
" <screenshots>\n"
|
" </screenshot>\n"
|
||||||
" <screenshot type=\"default\">\n"
|
" </screenshots>\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"
|
"</component>\n"
|
||||||
)
|
)
|
||||||
install(
|
install(
|
||||||
|
@ -17,7 +17,7 @@ static std::string get_cache_path() {
|
|||||||
if (home == nullptr) {
|
if (home == nullptr) {
|
||||||
IMPOSSIBLE();
|
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
|
// Load
|
||||||
@ -165,7 +165,8 @@ void wipe_cache() {
|
|||||||
INFO("Wiping Launcher Cache...");
|
INFO("Wiping Launcher Cache...");
|
||||||
|
|
||||||
// Unlink File
|
// 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));
|
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
|
// Use Zenity To Set Environmental Variable
|
||||||
#define DIALOG_TITLE "Launcher"
|
#define DIALOG_TITLE "Launcher"
|
||||||
static void run_zenity_and_set_env(const char *env_name, std::vector<std::string> command) {
|
static void run_zenity_and_set_env(const char *env_name, std::vector<std::string> command) {
|
||||||
|
reborn_check_display();
|
||||||
// Create Full Command
|
// Create Full Command
|
||||||
std::vector<std::string> full_command;
|
std::vector<std::string> full_command;
|
||||||
full_command.push_back("zenity");
|
full_command.push_back("zenity");
|
||||||
@ -146,31 +147,16 @@ void handle_non_launch_client_only_commands(const options_t &options) {
|
|||||||
});
|
});
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
}
|
// Wipe Cache If Needed
|
||||||
|
if (options.wipe_cache) {
|
||||||
// Check Environment
|
wipe_cache();
|
||||||
void check_environment_client() {
|
exit(EXIT_SUCCESS);
|
||||||
// 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
|
// Configure Client Options
|
||||||
#define LIST_DIALOG_SIZE "400"
|
#define LIST_DIALOG_SIZE "400"
|
||||||
void configure_client(const options_t &options) {
|
void configure_client(const options_t &options) {
|
||||||
// Wipe Cache If Needed
|
|
||||||
if (options.wipe_cache) {
|
|
||||||
wipe_cache();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load Cache
|
// Load Cache
|
||||||
launcher_cache cache = options.no_cache ? empty_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
|
// Handle Non-Launch Commands
|
||||||
void handle_non_launch_client_only_commands(const options_t &options);
|
void handle_non_launch_client_only_commands(const options_t &options);
|
||||||
|
|
||||||
// Check Environment
|
|
||||||
void check_environment_client();
|
|
||||||
|
|
||||||
// Configure Client Options
|
// Configure Client Options
|
||||||
void configure_client(const options_t &options);
|
void configure_client(const options_t &options);
|
@ -15,7 +15,6 @@
|
|||||||
#include "crash-report.h"
|
#include "crash-report.h"
|
||||||
|
|
||||||
// Show Crash Report Dialog
|
// Show Crash Report Dialog
|
||||||
#ifndef MCPI_HEADLESS_MODE
|
|
||||||
#define DIALOG_TITLE "Crash Report"
|
#define DIALOG_TITLE "Crash Report"
|
||||||
#define CRASH_REPORT_DIALOG_WIDTH "640"
|
#define CRASH_REPORT_DIALOG_WIDTH "640"
|
||||||
#define CRASH_REPORT_DIALOG_HEIGHT "480"
|
#define CRASH_REPORT_DIALOG_HEIGHT "480"
|
||||||
@ -35,7 +34,7 @@ static void show_report(const char *log_filename) {
|
|||||||
"--width", CRASH_REPORT_DIALOG_WIDTH,
|
"--width", CRASH_REPORT_DIALOG_WIDTH,
|
||||||
"--height", CRASH_REPORT_DIALOG_HEIGHT,
|
"--height", CRASH_REPORT_DIALOG_HEIGHT,
|
||||||
"--text-info",
|
"--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,
|
"--filename", log_filename,
|
||||||
"--no-wrap",
|
"--no-wrap",
|
||||||
"--font", "Monospace",
|
"--font", "Monospace",
|
||||||
@ -46,7 +45,6 @@ static void show_report(const char *log_filename) {
|
|||||||
safe_execvpe(command, (const char *const *) environ);
|
safe_execvpe(command, (const char *const *) environ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Exit Handler
|
// Exit Handler
|
||||||
static void exit_handler(__attribute__((unused)) int signal) {
|
static void exit_handler(__attribute__((unused)) int signal) {
|
||||||
@ -248,11 +246,9 @@ void setup_crash_report() {
|
|||||||
unsetenv(MCPI_LOG_ENV);
|
unsetenv(MCPI_LOG_ENV);
|
||||||
|
|
||||||
// Show Crash Log
|
// Show Crash Log
|
||||||
#ifndef MCPI_HEADLESS_MODE
|
if (is_crash && !reborn_is_headless()) {
|
||||||
if (is_crash) {
|
|
||||||
show_report(log_filename);
|
show_report(log_filename);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Exit
|
// Exit
|
||||||
exit(WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE);
|
exit(WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE);
|
||||||
|
@ -6,9 +6,7 @@
|
|||||||
#include "options/parser.h"
|
#include "options/parser.h"
|
||||||
#include "crash-report.h"
|
#include "crash-report.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#ifndef MCPI_SERVER_MODE
|
|
||||||
#include "client/configuration.h"
|
#include "client/configuration.h"
|
||||||
#endif
|
|
||||||
|
|
||||||
// Bind Options To Environmental Variable
|
// Bind Options To Environmental Variable
|
||||||
static void bind_to_env(const char *env, const bool value) {
|
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) {
|
static void setup_environment(const options_t &options) {
|
||||||
// Passthrough Options To Game
|
// 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);
|
bind_to_env("_MCPI_BENCHMARK", options.benchmark);
|
||||||
#else
|
|
||||||
bind_to_env("_MCPI_ONLY_GENERATE", options.only_generate);
|
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
|
// GTK Dark Mode
|
||||||
#ifndef MCPI_HEADLESS_MODE
|
|
||||||
set_and_print_env("GTK_THEME", "Adwaita:dark");
|
set_and_print_env("GTK_THEME", "Adwaita:dark");
|
||||||
#endif
|
|
||||||
|
|
||||||
// Configure PATH
|
// Configure PATH
|
||||||
{
|
{
|
||||||
@ -72,9 +68,6 @@ static void start_game(const options_t &options) {
|
|||||||
// Disable stdout Buffering
|
// Disable stdout Buffering
|
||||||
setvbuf(stdout, nullptr, _IONBF, 0);
|
setvbuf(stdout, nullptr, _IONBF, 0);
|
||||||
|
|
||||||
// Environemntal Variable Options
|
|
||||||
setup_environment(options);
|
|
||||||
|
|
||||||
// Setup Crash Reporting
|
// Setup Crash Reporting
|
||||||
if (!options.disable_crash_report) {
|
if (!options.disable_crash_report) {
|
||||||
setup_log_file();
|
setup_log_file();
|
||||||
@ -92,35 +85,35 @@ static void start_game(const options_t &options) {
|
|||||||
sigaction(SIGTERM, &act_sigterm, nullptr);
|
sigaction(SIGTERM, &act_sigterm, nullptr);
|
||||||
|
|
||||||
// Setup Home
|
// Setup Home
|
||||||
#ifndef MCPI_SERVER_MODE
|
if (!reborn_is_server()) {
|
||||||
// Ensure $HOME
|
// Ensure $HOME
|
||||||
const char *home = getenv("HOME");
|
const char *home = getenv("HOME");
|
||||||
if (home == nullptr) {
|
if (home == nullptr) {
|
||||||
ERR("$HOME Isn't Set");
|
ERR("$HOME Is Not Set");
|
||||||
}
|
}
|
||||||
// Create If Needed
|
// 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 = {};
|
struct stat tmp_stat = {};
|
||||||
bool exists = stat(minecraft_folder.c_str(), &tmp_stat) != 0 ? false : S_ISDIR(tmp_stat.st_mode);
|
bool exists = stat(minecraft_folder.c_str(), &tmp_stat) != 0 ? false : S_ISDIR(tmp_stat.st_mode);
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
// Doesn't Exist
|
// Doesn't Exist
|
||||||
if (mkdir(minecraft_folder.c_str(), S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
|
if (mkdir(minecraft_folder.c_str(), S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
|
||||||
ERR("Unable To Create Data Directory: %s", strerror(errno));
|
ERR("Unable To Create Data Directory: %s", strerror(errno));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Set Home To Current Directory, So World Data Is Stored There
|
||||||
|
char *launch_directory = getcwd(nullptr, 0);
|
||||||
|
set_and_print_env("HOME", launch_directory);
|
||||||
|
free(launch_directory);
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
// Set Home To Current Directory, So World Data Is Stored There
|
|
||||||
char *launch_directory = getcwd(NULL, 0);
|
|
||||||
set_and_print_env("HOME", launch_directory);
|
|
||||||
free(launch_directory);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Configure Client Options
|
// Configure Client Options
|
||||||
#ifndef MCPI_SERVER_MODE
|
if (!reborn_is_server()) {
|
||||||
configure_client(options);
|
configure_client(options);
|
||||||
#endif
|
}
|
||||||
|
|
||||||
// Bootstrap
|
// Bootstrap
|
||||||
bootstrap();
|
bootstrap();
|
||||||
@ -138,17 +131,12 @@ int main(int argc, char *argv[]) {
|
|||||||
unsetenv(MCPI_LOG_ENV);
|
unsetenv(MCPI_LOG_ENV);
|
||||||
bind_to_env(MCPI_DEBUG_ENV, options.debug);
|
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 (Copy SDK, Print Feature Flags, Etc)
|
||||||
handle_non_launch_commands(options);
|
handle_non_launch_commands(options);
|
||||||
#ifndef MCPI_SERVER_MODE
|
|
||||||
handle_non_launch_client_only_commands(options);
|
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 The Game
|
||||||
start_game(options);
|
start_game(options);
|
||||||
|
@ -60,7 +60,7 @@ std::string bootstrap_mods(const std::string &binary_directory) {
|
|||||||
// ~/.minecraft-pi/mods
|
// ~/.minecraft-pi/mods
|
||||||
{
|
{
|
||||||
// Get Mods Folder
|
// 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 Mods From ./mods
|
||||||
load(preload, mods_folder);
|
load(preload, mods_folder);
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
OPTION(debug, "debug", 'd', "Enable Debug Logging (" MCPI_DEBUG_ENV ")")
|
OPTION(debug, "debug", 'd', "Enable Debug Logging (" MCPI_DEBUG_ENV ")")
|
||||||
OPTION(copy_sdk, "copy-sdk", -2, "Extract Modding SDK And Exit")
|
OPTION(copy_sdk, "copy-sdk", -2, "Extract Modding SDK And Exit")
|
||||||
OPTION(disable_crash_report, "disable-crash-report", -1, "Disable Crash Report Dialog")
|
OPTION(disable_crash_report, "disable-crash-report", -1, "Disable Crash Report Dialog")
|
||||||
#ifndef MCPI_SERVER_MODE
|
OPTION(use_default, "default", -3, "Skip Client-Mode Configuration Dialogs")
|
||||||
OPTION(use_default, "default", -3, "Skip Configuration Dialogs")
|
OPTION(no_cache, "no-cache", -4, "Disable Client-Mode Configuration Cache")
|
||||||
OPTION(no_cache, "no-cache", -4, "Disable Configuration Cache")
|
OPTION(wipe_cache, "wipe-cache", -5, "Wipe Cached Client-Mode Configuration And Exit")
|
||||||
OPTION(wipe_cache, "wipe-cache", -5, "Wipe Cached Configuration")
|
OPTION(print_available_feature_flags, "print-available-feature-flags", -6, "Print Available Client-Mode Feature Flags")
|
||||||
OPTION(print_available_feature_flags, "print-available-feature-flags", -6, "Print Available Feature Flags")
|
OPTION(benchmark, "benchmark", -7, "Run Client-Mode Benchmark")
|
||||||
OPTION(benchmark, "benchmark", -7, "Run Benchmark")
|
OPTION(only_generate, "only-generate", -8, "Generate World And Exit (Server-Mode Only)")
|
||||||
#else
|
OPTION(force_headless, "force-headless", -9, "Force Disable Game Rendering")
|
||||||
OPTION(only_generate, "only-generate", -8, "Generate World And Exit")
|
OPTION(force_non_headless, "force-non-headless", -10, "Force Enable Game Rendering")
|
||||||
#endif
|
OPTION(server_mode, "server", -11, "Run In Server-Mode")
|
@ -14,7 +14,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Copy SDK Into ~/.minecraft-pi
|
// 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) {
|
void copy_sdk(const std::string &binary_directory, const bool log_with_debug) {
|
||||||
// Ensure SDK Directory
|
// Ensure SDK Directory
|
||||||
std::string sdk_path;
|
std::string sdk_path;
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#cmakedefine MCPI_SERVER_MODE
|
|
||||||
#cmakedefine MCPI_HEADLESS_MODE
|
|
||||||
#cmakedefine MCPI_IS_APPIMAGE_BUILD
|
#cmakedefine MCPI_IS_APPIMAGE_BUILD
|
||||||
#cmakedefine MCPI_IS_FLATPAK_BUILD
|
#cmakedefine MCPI_IS_FLATPAK_BUILD
|
||||||
#cmakedefine MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
|
#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 "util.h"
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
#include "exec.h"
|
#include "exec.h"
|
||||||
#include "home.h"
|
|
||||||
#include "patch.h"
|
#include "patch.h"
|
||||||
|
@ -51,10 +51,19 @@ int lock_file(const char *file);
|
|||||||
void unlock_file(const char *file, int fd);
|
void unlock_file(const char *file, int fd);
|
||||||
|
|
||||||
// Access Configuration At Runtime
|
// 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();
|
const char *reborn_get_version();
|
||||||
int reborn_is_headless();
|
int reborn_is_headless();
|
||||||
int reborn_is_server();
|
int reborn_is_server();
|
||||||
|
|
||||||
|
// Check $DISPLAY
|
||||||
|
void reborn_check_display();
|
||||||
|
|
||||||
|
// Get Home Subdirectory
|
||||||
|
const char *get_home_subdirectory_for_game_data();
|
||||||
|
|
||||||
// Customize VTable
|
// Customize VTable
|
||||||
#define CUSTOM_VTABLE(name, parent) \
|
#define CUSTOM_VTABLE(name, parent) \
|
||||||
void _setup_##name##_vtable(parent##_vtable *vtable); \
|
void _setup_##name##_vtable(parent##_vtable *vtable); \
|
||||||
|
@ -50,16 +50,43 @@ const char *reborn_get_version() {
|
|||||||
return MCPI_VERSION;
|
return MCPI_VERSION;
|
||||||
}
|
}
|
||||||
int reborn_is_headless() {
|
int reborn_is_headless() {
|
||||||
#ifdef MCPI_HEADLESS_MODE
|
static int ret;
|
||||||
return 1;
|
static int is_set = 0;
|
||||||
#else
|
if (!is_set) {
|
||||||
return 0;
|
ret = reborn_is_server();
|
||||||
#endif
|
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() {
|
int reborn_is_server() {
|
||||||
#ifdef MCPI_SERVER_MODE
|
static int ret;
|
||||||
return 1;
|
static int is_set = 0;
|
||||||
#else
|
if (!is_set) {
|
||||||
return 0;
|
ret = getenv(MCPI_SERVER_MODE_ENV) != NULL;
|
||||||
#endif
|
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)
|
project(media-layer-core)
|
||||||
|
|
||||||
# OpenGL
|
# OpenGL
|
||||||
if(NOT MCPI_HEADLESS_MODE)
|
add_subdirectory(gles)
|
||||||
add_subdirectory(gles)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Configuration
|
# SDL Re-Implementation Using GLFW
|
||||||
set(CORE_SRC src/base.cpp src/media.c $<TARGET_OBJECTS:media-layer-extras>) # SDL Re-Implementation Using GLFW
|
set(CORE_SRC
|
||||||
if(NOT MCPI_HEADLESS_MODE)
|
src/base.cpp
|
||||||
list(APPEND CORE_SRC src/audio/api.cpp src/audio/engine.c src/audio/file.cpp)
|
src/media.cpp
|
||||||
else()
|
src/audio/api.cpp
|
||||||
list(APPEND CORE_SRC src/audio/stubs.c)
|
src/audio/engine.c
|
||||||
endif()
|
src/audio/file.cpp
|
||||||
|
$<TARGET_OBJECTS:media-layer-extras>
|
||||||
|
)
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
add_library(media-layer-core-real SHARED ${CORE_SRC}) # Dependencies Are Setup Later
|
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}")
|
install(TARGETS media-layer-core-real DESTINATION "${MCPI_LIB_DIR}")
|
||||||
|
|
||||||
# Link
|
# Link
|
||||||
target_link_libraries(media-layer-core-real PUBLIC media-layer-headers PUBLIC reborn-util PUBLIC dl)
|
find_library(OPENAL_LIBRARY NAMES openal REQUIRED)
|
||||||
if(NOT MCPI_HEADLESS_MODE)
|
target_link_libraries(media-layer-core-real
|
||||||
# OpenAL
|
PUBLIC media-layer-headers
|
||||||
find_library(OPENAL_LIBRARY NAMES openal REQUIRED)
|
PUBLIC reborn-util
|
||||||
# Link
|
PRIVATE "${OPENAL_LIBRARY}"
|
||||||
target_link_libraries(media-layer-core-real PRIVATE "${OPENAL_LIBRARY}" PRIVATE m PRIVATE glfw PUBLIC GLESv1_CM PRIVATE LIB_LIEF)
|
PRIVATE m
|
||||||
endif()
|
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)
|
project(media-layer-trampoline)
|
||||||
|
|
||||||
# Configuration
|
# Common Sources
|
||||||
set(MEDIA_LAYER_TRAMPOLINE_SRC src/media-layer-core.cpp) # Media Layer Trampoline Source
|
set(MEDIA_LAYER_TRAMPOLINE_SRC src/media-layer-core.cpp src/GLESv1_CM.cpp)
|
||||||
if(NOT MCPI_HEADLESS_MODE)
|
|
||||||
list(APPEND MEDIA_LAYER_TRAMPOLINE_SRC src/GLESv1_CM.cpp)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
if(BUILD_NATIVE_COMPONENTS)
|
if(BUILD_NATIVE_COMPONENTS)
|
||||||
# Host Component
|
# Host Component
|
||||||
add_library(media-layer-trampoline src/host/host.cpp ${MEDIA_LAYER_TRAMPOLINE_SRC})
|
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)
|
target_link_libraries(media-layer-trampoline reborn-util media-layer-core GLESv1_CM trampoline-headers)
|
||||||
if(NOT MCPI_HEADLESS_MODE)
|
|
||||||
target_link_libraries(media-layer-trampoline GLESv1_CM)
|
|
||||||
endif()
|
|
||||||
target_compile_definitions(media-layer-trampoline PRIVATE -DMEDIA_LAYER_TRAMPOLINE_HOST)
|
target_compile_definitions(media-layer-trampoline PRIVATE -DMEDIA_LAYER_TRAMPOLINE_HOST)
|
||||||
# Install
|
# Install
|
||||||
install(TARGETS media-layer-trampoline DESTINATION "${MCPI_LIB_DIR}")
|
install(TARGETS media-layer-trampoline DESTINATION "${MCPI_LIB_DIR}")
|
||||||
|
@ -46,72 +46,52 @@ set(SRC
|
|||||||
src/text-input-box/TextInputScreen.cpp
|
src/text-input-box/TextInputScreen.cpp
|
||||||
# test
|
# test
|
||||||
src/test/test.cpp
|
src/test/test.cpp
|
||||||
|
# sound
|
||||||
|
src/sound/sound.cpp
|
||||||
|
src/sound/repository.cpp
|
||||||
|
# camera
|
||||||
|
src/camera/camera.cpp
|
||||||
|
# input
|
||||||
|
src/input/input.cpp
|
||||||
|
src/input/bow.cpp
|
||||||
|
src/input/attack.cpp
|
||||||
|
src/input/toggle.cpp
|
||||||
|
src/input/misc.cpp
|
||||||
|
src/input/drop.cpp
|
||||||
|
# sign
|
||||||
|
src/sign/sign.cpp
|
||||||
|
# atlas
|
||||||
|
src/atlas/atlas.cpp
|
||||||
|
# title-screen
|
||||||
|
src/title-screen/title-screen.cpp
|
||||||
|
src/title-screen/splashes.txt # Show In IDE
|
||||||
|
src/title-screen/welcome.cpp
|
||||||
|
# skin
|
||||||
|
src/skin/skin.cpp
|
||||||
|
src/skin/loader.cpp
|
||||||
|
# screenshot
|
||||||
|
src/screenshot/screenshot.cpp
|
||||||
|
# textures
|
||||||
|
src/textures/textures.cpp
|
||||||
|
src/textures/lava.cpp
|
||||||
|
src/textures/headless.cpp
|
||||||
|
# fps
|
||||||
|
src/fps/fps.cpp
|
||||||
|
# server
|
||||||
|
src/server/server.cpp
|
||||||
|
src/server/server_properties.cpp
|
||||||
|
# multiplayer
|
||||||
|
src/multiplayer/multiplayer.cpp
|
||||||
|
# benchmark
|
||||||
|
src/benchmark/benchmark.cpp
|
||||||
# init
|
# init
|
||||||
src/init/init.cpp
|
src/init/init.cpp
|
||||||
)
|
)
|
||||||
|
# Install Splashes
|
||||||
# Server-Only Sources
|
install(
|
||||||
if(MCPI_SERVER_MODE)
|
FILES "src/title-screen/splashes.txt"
|
||||||
list(APPEND SRC
|
DESTINATION "${MCPI_INSTALL_DIR}/data"
|
||||||
# server
|
)
|
||||||
src/server/server.cpp
|
|
||||||
src/server/server_properties.cpp
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
list(APPEND SRC
|
|
||||||
# multiplayer
|
|
||||||
src/multiplayer/multiplayer.cpp
|
|
||||||
# benchmark
|
|
||||||
src/benchmark/benchmark.cpp
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Headless-Only Sources
|
|
||||||
if(MCPI_HEADLESS_MODE)
|
|
||||||
list(APPEND SRC
|
|
||||||
# textures
|
|
||||||
src/textures/headless.cpp
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
list(APPEND SRC
|
|
||||||
# sound
|
|
||||||
src/sound/sound.cpp
|
|
||||||
src/sound/repository.cpp
|
|
||||||
# camera
|
|
||||||
src/camera/camera.cpp
|
|
||||||
# input
|
|
||||||
src/input/input.cpp
|
|
||||||
src/input/bow.cpp
|
|
||||||
src/input/attack.cpp
|
|
||||||
src/input/toggle.cpp
|
|
||||||
src/input/misc.cpp
|
|
||||||
src/input/drop.cpp
|
|
||||||
src/input/crafting.cpp
|
|
||||||
# sign
|
|
||||||
src/sign/sign.cpp
|
|
||||||
# atlas
|
|
||||||
src/atlas/atlas.cpp
|
|
||||||
# title-screen
|
|
||||||
src/title-screen/title-screen.cpp
|
|
||||||
src/title-screen/splashes.txt # Show In IDE
|
|
||||||
src/title-screen/welcome.cpp
|
|
||||||
# skin
|
|
||||||
src/skin/skin.cpp
|
|
||||||
src/skin/loader.cpp
|
|
||||||
# screenshot
|
|
||||||
src/screenshot/screenshot.cpp
|
|
||||||
# textures
|
|
||||||
src/textures/textures.cpp
|
|
||||||
src/textures/lava.cpp
|
|
||||||
# fps
|
|
||||||
src/fps/fps.cpp
|
|
||||||
)
|
|
||||||
# Install Splashes
|
|
||||||
install(
|
|
||||||
FILES "src/title-screen/splashes.txt"
|
|
||||||
DESTINATION "${MCPI_INSTALL_DIR}/data"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
add_library(mods SHARED ${SRC})
|
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}")
|
install(TARGETS mods EXPORT sdk DESTINATION "${MCPI_SDK_LIB_DIR}")
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
target_link_libraries(mods symbols reborn-patch media-layer-core dl pthread)
|
target_link_libraries(mods symbols reborn-patch media-layer-core stb_image dl pthread)
|
||||||
if(NOT MCPI_HEADLESS_MODE)
|
|
||||||
target_link_libraries(mods stb_image)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Headers
|
# Headers
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
|
@ -1,16 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libreborn/libreborn.h>
|
|
||||||
|
|
||||||
extern "C" {
|
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_disabled (0)
|
||||||
#define _feature_has__server_defaul_is_server_disabled(name) 0
|
#define _feature_has_server_auto (-1)
|
||||||
#define _feature_has__server_defaul_is_server_auto(name) _feature_has(name)
|
#define _feature_has_server_enabled (1)
|
||||||
#define _feature_has__server_defaul_is_server_enabled(name) 1
|
#define feature_has(name, server_default) _feature_has(name, _feature_has_##server_default)
|
||||||
#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
|
|
||||||
|
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
|
#pragma once
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
char *home_get();
|
const char *home_get();
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,9 @@ extern "C" {
|
|||||||
void run_tests();
|
void run_tests();
|
||||||
void init_version();
|
void init_version();
|
||||||
void init_compat();
|
void init_compat();
|
||||||
#ifdef MCPI_SERVER_MODE
|
|
||||||
void init_server();
|
void init_server();
|
||||||
#else
|
|
||||||
void init_multiplayer();
|
void init_multiplayer();
|
||||||
void init_benchmark();
|
void init_benchmark();
|
||||||
#endif
|
|
||||||
#ifndef MCPI_HEADLESS_MODE
|
|
||||||
void init_sound();
|
void init_sound();
|
||||||
void init_input();
|
void init_input();
|
||||||
void init_sign();
|
void init_sign();
|
||||||
@ -19,7 +15,6 @@ void init_atlas();
|
|||||||
void init_title_screen();
|
void init_title_screen();
|
||||||
void init_skin();
|
void init_skin();
|
||||||
void init_fps();
|
void init_fps();
|
||||||
#endif
|
|
||||||
void init_touch();
|
void init_touch();
|
||||||
void init_textures();
|
void init_textures();
|
||||||
void init_creative();
|
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);
|
void input_set_is_right_click(int val);
|
||||||
int input_back();
|
int input_back();
|
||||||
void input_drop(int drop_slot);
|
void input_drop(int drop_slot);
|
||||||
void input_open_crafting();
|
|
||||||
|
|
||||||
void input_set_is_left_click(int val);
|
void input_set_is_left_click(int val);
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
extern "C" {
|
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
|
// Init
|
||||||
void init_atlas() {
|
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
|
// Disable The gui_blocks Atlas Which Contains Pre-Rendered Textures For Blocks In The Inventory
|
||||||
if (feature_has("Disable \"gui_blocks\" Atlas", server_disabled)) {
|
if (feature_has("Disable \"gui_blocks\" Atlas", server_disabled)) {
|
||||||
unsigned char disable_gui_blocks_atlas_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop"
|
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_calls(Tesselator_color, Tesselator_color_injection);
|
||||||
overwrite_call((void *) 0x32324, (void *) FurnaceScreen_render_ItemRenderer_renderGuiItem_one_injection);
|
overwrite_call((void *) 0x32324, (void *) FurnaceScreen_render_ItemRenderer_renderGuiItem_one_injection);
|
||||||
overwrite_call((void *) 0x1e21c, (void *) InventoryPane_renderBatch_Tesselator_color_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
|
// Track Frames
|
||||||
#ifndef MCPI_HEADLESS_MODE
|
|
||||||
static unsigned long long int frames = 0;
|
static unsigned long long int frames = 0;
|
||||||
HOOK(media_swap_buffers, void, ()) {
|
HOOK(media_swap_buffers, void, ()) {
|
||||||
ensure_media_swap_buffers();
|
ensure_media_swap_buffers();
|
||||||
real_media_swap_buffers();
|
real_media_swap_buffers();
|
||||||
frames++;
|
frames++;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Track Ticks
|
// Track Ticks
|
||||||
static unsigned long long int ticks = 0;
|
static unsigned long long int ticks = 0;
|
||||||
@ -74,9 +72,7 @@ static long long int get_time() {
|
|||||||
// Store Time When World Loaded
|
// Store Time When World Loaded
|
||||||
static bool world_loaded = false;
|
static bool world_loaded = false;
|
||||||
static long long int world_loaded_time;
|
static long long int world_loaded_time;
|
||||||
#ifndef MCPI_HEADLESS_MODE
|
|
||||||
static unsigned long long int world_loaded_frames;
|
static unsigned long long int world_loaded_frames;
|
||||||
#endif
|
|
||||||
static unsigned long long int world_loaded_ticks;
|
static unsigned long long int world_loaded_ticks;
|
||||||
|
|
||||||
// Last Logged Status Update
|
// Last Logged Status Update
|
||||||
@ -98,11 +94,9 @@ static void Minecraft_update_injection(Minecraft *minecraft) {
|
|||||||
// Detect World Loaded
|
// Detect World Loaded
|
||||||
if (!world_loaded && minecraft->isLevelGenerated()) {
|
if (!world_loaded && minecraft->isLevelGenerated()) {
|
||||||
world_loaded = true;
|
world_loaded = true;
|
||||||
INFO("Loaded");
|
INFO("Benchmark Loaded");
|
||||||
world_loaded_time = now;
|
world_loaded_time = now;
|
||||||
#ifndef MCPI_HEADLESS_MODE
|
|
||||||
world_loaded_frames = frames;
|
world_loaded_frames = frames;
|
||||||
#endif
|
|
||||||
world_loaded_ticks = ticks;
|
world_loaded_ticks = ticks;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,9 +104,7 @@ static void Minecraft_update_injection(Minecraft *minecraft) {
|
|||||||
if (!exit_requested && world_loaded) {
|
if (!exit_requested && world_loaded) {
|
||||||
// Get Time
|
// Get Time
|
||||||
long long int current_time = now - world_loaded_time;
|
long long int current_time = now - world_loaded_time;
|
||||||
#ifndef MCPI_HEADLESS_MODE
|
|
||||||
unsigned long long int current_frames = frames - world_loaded_frames;
|
unsigned long long int current_frames = frames - world_loaded_frames;
|
||||||
#endif
|
|
||||||
unsigned long long int current_ticks = ticks - world_loaded_ticks;
|
unsigned long long int current_ticks = ticks - world_loaded_ticks;
|
||||||
|
|
||||||
// Log
|
// Log
|
||||||
@ -151,11 +143,11 @@ static void Minecraft_update_injection(Minecraft *minecraft) {
|
|||||||
// Calculate FPS & TPS
|
// Calculate FPS & TPS
|
||||||
INFO("Benchmark Completed");
|
INFO("Benchmark Completed");
|
||||||
INFO(" Total Time: %lld Nanoseconds", current_time);
|
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_nanosecond = ((double) current_frames) / ((double) current_time);
|
||||||
static double frames_per_second = frames_per_nanosecond * NANOSECONDS_IN_SECOND;
|
static double frames_per_second = frames_per_nanosecond * NANOSECONDS_IN_SECOND;
|
||||||
INFO(" FPS: %f (%llu Total Frames)", frames_per_second, current_frames);
|
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_nanosecond = ((double) current_ticks) / ((double) current_time);
|
||||||
static double ticks_per_second = ticks_per_nanosecond * NANOSECONDS_IN_SECOND;
|
static double ticks_per_second = ticks_per_nanosecond * NANOSECONDS_IN_SECOND;
|
||||||
INFO(" TPS: %f (%llu Total Ticks)", ticks_per_second, current_ticks);
|
INFO(" TPS: %f (%llu Total Ticks)", ticks_per_second, current_ticks);
|
||||||
|
@ -8,9 +8,7 @@
|
|||||||
|
|
||||||
// Take Screenshot Using TripodCamera
|
// 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) {
|
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());
|
screenshot_take(home_get());
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable TripodCameraRenderer
|
// Enable TripodCameraRenderer
|
||||||
|
@ -10,15 +10,12 @@
|
|||||||
#include <libreborn/libreborn.h>
|
#include <libreborn/libreborn.h>
|
||||||
|
|
||||||
#include <SDL/SDL.h>
|
#include <SDL/SDL.h>
|
||||||
|
|
||||||
#ifndef MCPI_HEADLESS_MODE
|
|
||||||
#include <media-layer/core.h>
|
#include <media-layer/core.h>
|
||||||
|
|
||||||
#include <mods/input/input.h>
|
#include <mods/input/input.h>
|
||||||
#include <mods/sign/sign.h>
|
#include <mods/sign/sign.h>
|
||||||
#include <mods/chat/chat.h>
|
#include <mods/chat/chat.h>
|
||||||
#include <mods/home/home.h>
|
#include <mods/home/home.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
// Custom Title
|
// Custom Title
|
||||||
HOOK(SDL_WM_SetCaption, void, (__attribute__((unused)) const char *title, const char *icon)) {
|
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)) {
|
HOOK(SDL_PollEvent, int, (SDL_Event *event)) {
|
||||||
// In Server Mode, Exit Requests Are Handled In src/server/server.cpp
|
// In Server Mode, Exit Requests Are Handled In src/server/server.cpp
|
||||||
// Check If Exit Is Requested
|
// Check If Exit Is Requested
|
||||||
#ifndef MCPI_SERVER_MODE
|
if (!reborn_is_server() && compat_check_exit_requested()) {
|
||||||
if (compat_check_exit_requested()) {
|
|
||||||
// Send SDL_QUIT
|
// Send SDL_QUIT
|
||||||
SDL_Event new_event;
|
SDL_Event new_event;
|
||||||
new_event.type = SDL_QUIT;
|
new_event.type = SDL_QUIT;
|
||||||
SDL_PushEvent(&new_event);
|
SDL_PushEvent(&new_event);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Poll Events
|
// Poll Events
|
||||||
ensure_SDL_PollEvent();
|
ensure_SDL_PollEvent();
|
||||||
@ -53,8 +48,6 @@ HOOK(SDL_PollEvent, int, (SDL_Event *event)) {
|
|||||||
// Handle Events
|
// Handle Events
|
||||||
if (ret == 1 && event != nullptr) {
|
if (ret == 1 && event != nullptr) {
|
||||||
int handled = 0;
|
int handled = 0;
|
||||||
|
|
||||||
#ifndef MCPI_HEADLESS_MODE
|
|
||||||
switch (event->type) {
|
switch (event->type) {
|
||||||
case SDL_KEYDOWN: {
|
case SDL_KEYDOWN: {
|
||||||
// Handle Key Presses
|
// Handle Key Presses
|
||||||
@ -71,10 +64,6 @@ HOOK(SDL_PollEvent, int, (SDL_Event *event)) {
|
|||||||
// Drop Item
|
// Drop Item
|
||||||
input_drop((event->key.keysym.mod & KMOD_CTRL) != 0);
|
input_drop((event->key.keysym.mod & KMOD_CTRL) != 0);
|
||||||
handled = 1;
|
handled = 1;
|
||||||
} else if (event->key.keysym.sym == SDLK_WORLD_0) {
|
|
||||||
// Crafting
|
|
||||||
input_open_crafting();
|
|
||||||
handled = 1;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -97,8 +86,6 @@ HOOK(SDL_PollEvent, int, (SDL_Event *event)) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
if (handled) {
|
if (handled) {
|
||||||
// Event Was Handled
|
// Event Was Handled
|
||||||
return SDL_PollEvent(event);
|
return SDL_PollEvent(event);
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
#include <mods/misc/misc.h>
|
#include <mods/misc/misc.h>
|
||||||
#include <mods/creative/creative.h>
|
#include <mods/creative/creative.h>
|
||||||
|
|
||||||
#ifndef MCPI_SERVER_MODE
|
|
||||||
// Add Item To Inventory
|
// Add Item To Inventory
|
||||||
static void inventory_add_item(FillingContainer *inventory, Item *item) {
|
static void inventory_add_item(FillingContainer *inventory, Item *item) {
|
||||||
ItemInstance *item_instance = new ItemInstance;
|
ItemInstance *item_instance = new ItemInstance;
|
||||||
@ -83,7 +82,6 @@ static void Inventory_setupDefault_FillingContainer_addItem_call_injection(Filli
|
|||||||
filling_container->addItem(new_item_instance);
|
filling_container->addItem(new_item_instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Hook Specific TileItem Constructor
|
// Hook Specific TileItem Constructor
|
||||||
static TileItem *Tile_initTiles_TileItem_injection(TileItem *tile_item, int32_t id) {
|
static TileItem *Tile_initTiles_TileItem_injection(TileItem *tile_item, int32_t id) {
|
||||||
@ -111,9 +109,7 @@ int creative_is_restricted() {
|
|||||||
void init_creative() {
|
void init_creative() {
|
||||||
// Add Extra Items To Creative Inventory (Only Replace Specific Function Call)
|
// Add Extra Items To Creative Inventory (Only Replace Specific Function Call)
|
||||||
if (feature_has("Expand Creative Mode Inventory", server_enabled)) {
|
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);
|
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
|
// Use AuxDataTileItem by default instead of TileItem, so tiles in the Creative
|
||||||
// Inventory can have arbitrary auxiliary values.
|
// Inventory can have arbitrary auxiliary values.
|
||||||
|
@ -6,7 +6,11 @@
|
|||||||
#include <mods/feature/feature.h>
|
#include <mods/feature/feature.h>
|
||||||
|
|
||||||
// Check For Feature
|
// 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
|
// Get Value
|
||||||
char *env = getenv("MCPI_FEATURE_FLAGS");
|
char *env = getenv("MCPI_FEATURE_FLAGS");
|
||||||
char *features = strdup(env != nullptr ? env : "");
|
char *features = strdup(env != nullptr ? env : "");
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include <symbols/minecraft.h>
|
#include <symbols/minecraft.h>
|
||||||
#include <libreborn/libreborn.h>
|
#include <libreborn/libreborn.h>
|
||||||
@ -7,6 +8,7 @@
|
|||||||
#include <mods/text-input-box/TextInputScreen.h>
|
#include <mods/text-input-box/TextInputScreen.h>
|
||||||
#include <mods/touch/touch.h>
|
#include <mods/touch/touch.h>
|
||||||
#include <mods/misc/misc.h>
|
#include <mods/misc/misc.h>
|
||||||
|
#include <mods/game-mode/game-mode.h>
|
||||||
#include "game-mode-internal.h"
|
#include "game-mode-internal.h"
|
||||||
|
|
||||||
// Strings
|
// Strings
|
||||||
@ -175,20 +177,24 @@ static std::string getUniqueLevelName(LevelStorageSource *source, const std::str
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create World
|
// Create World
|
||||||
static void create_world(Minecraft *minecraft, std::string name, bool is_creative, std::string seed_str) {
|
int get_seed_from_string(std::string str) {
|
||||||
// Get Seed
|
|
||||||
int seed;
|
int seed;
|
||||||
seed_str = Util::stringTrim(&seed_str);
|
str = Util::stringTrim(&str);
|
||||||
if (!seed_str.empty()) {
|
if (!str.empty()) {
|
||||||
int num;
|
int num;
|
||||||
if (sscanf(seed_str.c_str(), "%d", &num) > 0) {
|
if (sscanf(str.c_str(), "%d", &num) > 0) {
|
||||||
seed = num;
|
seed = num;
|
||||||
} else {
|
} else {
|
||||||
seed = Util::hashCode(&seed_str);
|
seed = Util::hashCode(&str);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
seed = Common::getEpochTimeS();
|
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
|
// Get Folder Name
|
||||||
name = Util::stringTrim(&name);
|
name = Util::stringTrim(&name);
|
||||||
|
@ -7,24 +7,20 @@
|
|||||||
#include <mods/init/init.h>
|
#include <mods/init/init.h>
|
||||||
|
|
||||||
// Get MCPI Home Directory
|
// Get MCPI Home Directory
|
||||||
char *home_get() {
|
const char *home_get() {
|
||||||
static char *dir = nullptr;
|
static std::string dir = "";
|
||||||
// Load
|
// Load
|
||||||
if (dir == nullptr) {
|
if (dir.empty()) {
|
||||||
safe_asprintf(&dir, "%s" HOME_SUBDIRECTORY_FOR_GAME_DATA, getenv("HOME"));
|
dir = std::string(getenv("HOME")) + std::string(get_home_subdirectory_for_game_data());
|
||||||
}
|
}
|
||||||
// Return
|
// Return
|
||||||
return dir;
|
return dir.c_str();
|
||||||
}
|
|
||||||
// Free
|
|
||||||
__attribute__((destructor)) static void _free_home() {
|
|
||||||
free(home_get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init
|
// Init
|
||||||
void init_home() {
|
void init_home() {
|
||||||
// Store Data In ~/.minecraft-pi Instead Of ~/.minecraft
|
// 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,
|
// The override code resolves assets manually,
|
||||||
// making changing directory redundant.
|
// making changing directory redundant.
|
||||||
|
@ -8,12 +8,11 @@ __attribute__((constructor)) static void init() {
|
|||||||
run_tests();
|
run_tests();
|
||||||
init_version();
|
init_version();
|
||||||
init_compat();
|
init_compat();
|
||||||
#ifdef MCPI_SERVER_MODE
|
if (reborn_is_server()) {
|
||||||
init_server();
|
init_server();
|
||||||
#else
|
} else {
|
||||||
init_multiplayer();
|
init_multiplayer();
|
||||||
#endif
|
}
|
||||||
#ifndef MCPI_HEADLESS_MODE
|
|
||||||
init_sound();
|
init_sound();
|
||||||
init_input();
|
init_input();
|
||||||
init_sign();
|
init_sign();
|
||||||
@ -22,7 +21,6 @@ __attribute__((constructor)) static void init() {
|
|||||||
init_title_screen();
|
init_title_screen();
|
||||||
init_skin();
|
init_skin();
|
||||||
init_fps();
|
init_fps();
|
||||||
#endif
|
|
||||||
init_touch();
|
init_touch();
|
||||||
init_textures();
|
init_textures();
|
||||||
init_creative();
|
init_creative();
|
||||||
@ -34,7 +32,7 @@ __attribute__((constructor)) static void init() {
|
|||||||
init_bucket();
|
init_bucket();
|
||||||
init_cake();
|
init_cake();
|
||||||
init_home();
|
init_home();
|
||||||
#ifndef MCPI_SERVER_MODE
|
if (!reborn_is_server()) {
|
||||||
init_benchmark();
|
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_misc();
|
||||||
__attribute__((visibility("internal"))) void _init_toggle();
|
__attribute__((visibility("internal"))) void _init_toggle();
|
||||||
__attribute__((visibility("internal"))) void _init_drop();
|
__attribute__((visibility("internal"))) void _init_drop();
|
||||||
__attribute__((visibility("internal"))) void _init_crafting();
|
|
@ -49,9 +49,6 @@ void init_input() {
|
|||||||
// Allow Attacking Mobs
|
// Allow Attacking Mobs
|
||||||
_init_attack();
|
_init_attack();
|
||||||
|
|
||||||
// Allow Opening Crafting With Controller
|
|
||||||
_init_crafting();
|
|
||||||
|
|
||||||
// Disable Raw Mouse Motion
|
// Disable Raw Mouse Motion
|
||||||
if (feature_has("Disable Raw Mouse Motion (Not Recommended)", server_disabled)) {
|
if (feature_has("Disable Raw Mouse Motion (Not Recommended)", server_disabled)) {
|
||||||
media_set_raw_mouse_motion_enabled(0);
|
media_set_raw_mouse_motion_enabled(0);
|
||||||
|
@ -3,9 +3,7 @@
|
|||||||
|
|
||||||
#include <libreborn/libreborn.h>
|
#include <libreborn/libreborn.h>
|
||||||
#include <symbols/minecraft.h>
|
#include <symbols/minecraft.h>
|
||||||
#ifndef MCPI_HEADLESS_MODE
|
|
||||||
#include <GLES/gl.h>
|
#include <GLES/gl.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <mods/misc/misc.h>
|
#include <mods/misc/misc.h>
|
||||||
#include "misc-internal.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
|
// Render Fancy Background
|
||||||
void misc_render_background(int color, Minecraft *minecraft, int x, int y, int width, int height) {
|
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
|
// https://github.com/ReMinecraftPE/mcpe/blob/f0d65eaecec1b3fe9c2f2b251e114a890c54ab77/source/client/gui/components/RolledSelectionList.cpp#L169-L179
|
||||||
#ifndef MCPI_HEADLESS_MODE
|
|
||||||
glColor4f(1, 1, 1, 1);
|
glColor4f(1, 1, 1, 1);
|
||||||
#endif
|
|
||||||
std::string texture = "gui/background.png";
|
std::string texture = "gui/background.png";
|
||||||
minecraft->textures->loadAndBindTexture(&texture);
|
minecraft->textures->loadAndBindTexture(&texture);
|
||||||
Tesselator *t = &Tesselator::instance;
|
Tesselator *t = &Tesselator::instance;
|
||||||
|
@ -9,9 +9,7 @@
|
|||||||
#include <streambuf>
|
#include <streambuf>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#ifndef MCPI_HEADLESS_MODE
|
|
||||||
#include <GLES/gl.h>
|
#include <GLES/gl.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <libreborn/libreborn.h>
|
#include <libreborn/libreborn.h>
|
||||||
#include <symbols/minecraft.h>
|
#include <symbols/minecraft.h>
|
||||||
@ -143,9 +141,7 @@ static void Gui_renderChatMessages_injection(Gui_renderChatMessages_t original,
|
|||||||
// Render Selected Item Text
|
// Render Selected Item Text
|
||||||
if (render_selected_item_text) {
|
if (render_selected_item_text) {
|
||||||
// Fix GL Mode
|
// Fix GL Mode
|
||||||
#ifndef MCPI_HEADLESS_MODE
|
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
#endif
|
|
||||||
// Calculate Selected Item Text Scale
|
// Calculate Selected Item Text Scale
|
||||||
Minecraft *minecraft = gui->minecraft;
|
Minecraft *minecraft = gui->minecraft;
|
||||||
int32_t screen_width = minecraft->screen_width;
|
int32_t screen_width = minecraft->screen_width;
|
||||||
@ -184,37 +180,25 @@ static void Inventory_selectSlot_injection(Inventory_selectSlot_t original, Inve
|
|||||||
// Translucent Toolbar
|
// Translucent Toolbar
|
||||||
static void Gui_renderToolBar_injection(Gui_renderToolBar_t original, Gui *gui, float param_1, int32_t param_2, int32_t param_3) {
|
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
|
// Call Original Method
|
||||||
#ifndef MCPI_HEADLESS_MODE
|
bool was_blend_enabled = glIsEnabled(GL_BLEND);
|
||||||
int was_blend_enabled = glIsEnabled(GL_BLEND);
|
|
||||||
if (!was_blend_enabled) {
|
if (!was_blend_enabled) {
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
}
|
}
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
#endif
|
|
||||||
original(gui, param_1, param_2, param_3);
|
original(gui, param_1, param_2, param_3);
|
||||||
#ifndef MCPI_HEADLESS_MODE
|
|
||||||
if (!was_blend_enabled) {
|
if (!was_blend_enabled) {
|
||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
static void Gui_renderToolBar_glColor4f_injection(GLfloat red, GLfloat green, GLfloat blue, __attribute__((unused)) GLfloat alpha) {
|
static void Gui_renderToolBar_glColor4f_injection(GLfloat red, GLfloat green, GLfloat blue, __attribute__((unused)) GLfloat alpha) {
|
||||||
// Fix Alpha
|
// Fix Alpha
|
||||||
#ifndef MCPI_HEADLESS_MODE
|
|
||||||
glColor4f(red, green, blue, 1.0f);
|
glColor4f(red, green, blue, 1.0f);
|
||||||
#else
|
|
||||||
(void) red;
|
|
||||||
(void) green;
|
|
||||||
(void) blue;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix Screen Rendering When GUI is Hidden
|
// 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) {
|
static void Screen_render_injection(Screen_render_t original, Screen *screen, int32_t param_1, int32_t param_2, float param_3) {
|
||||||
// Fix
|
// Fix
|
||||||
#ifndef MCPI_HEADLESS_MODE
|
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
#endif
|
|
||||||
// Call Original Method
|
// Call Original Method
|
||||||
original(screen, param_1, param_2, param_3);
|
original(screen, param_1, param_2, param_3);
|
||||||
}
|
}
|
||||||
@ -265,18 +249,21 @@ static const char *RAKNET_ERROR_NAMES[] = {
|
|||||||
"Couldn't Generate GUID",
|
"Couldn't Generate GUID",
|
||||||
"Unknown"
|
"Unknown"
|
||||||
};
|
};
|
||||||
#ifdef MCPI_SERVER_MODE
|
#define CONDITIONAL_ERR(is_error, ...) \
|
||||||
#define PRINT_RAKNET_STARTUP_FAILURE ERR
|
{ \
|
||||||
#else
|
if ((is_error)) { \
|
||||||
#define PRINT_RAKNET_STARTUP_FAILURE WARN
|
ERR(__VA_ARGS__); \
|
||||||
#endif
|
} 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) {
|
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
|
// Call Original Method
|
||||||
RakNet_StartupResult result = rak_peer->Startup(maxConnections, socketDescriptors, socketDescriptorCount, threadPriority);
|
RakNet_StartupResult result = rak_peer->Startup(maxConnections, socketDescriptors, socketDescriptorCount, threadPriority);
|
||||||
|
|
||||||
// Print Error
|
// Print Error
|
||||||
if (result != RAKNET_STARTED) {
|
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
|
// 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.
|
// 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.
|
// 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) {
|
static void GameRenderer_render_injection(GameRenderer_render_t original, GameRenderer *game_renderer, float param_1) {
|
||||||
// Call Original Method
|
// Call Original Method
|
||||||
original(game_renderer, param_1);
|
original(game_renderer, param_1);
|
||||||
@ -356,7 +342,6 @@ static void GameRenderer_render_injection(GameRenderer_render_t original, GameRe
|
|||||||
Common::renderCursor(x, y, minecraft);
|
Common::renderCursor(x, y, minecraft);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Get Real Selected Slot
|
// Get Real Selected Slot
|
||||||
int32_t misc_get_real_selected_slot(Player *player) {
|
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;
|
return selected_slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef MCPI_HEADLESS_MODE
|
|
||||||
// Properly Generate Buffers
|
// Properly Generate Buffers
|
||||||
static void anGenBuffers_injection(int32_t count, uint32_t *buffers) {
|
static void anGenBuffers_injection(int32_t count, uint32_t *buffers) {
|
||||||
glGenBuffers(count, buffers);
|
if (!reborn_is_headless()) {
|
||||||
|
glGenBuffers(count, buffers);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Fix Graphics Bug When Switching To First-Person While Sneaking
|
// 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) {
|
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);
|
return original(container_menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef MCPI_HEADLESS_MODE
|
|
||||||
// Custom Outline Color
|
// 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
|
// Set Color
|
||||||
glColor4f(0, 0, 0, 0.4);
|
glColor4f(0, 0, 0, 0.4);
|
||||||
|
|
||||||
@ -591,7 +575,6 @@ static void glColor4f_injection(__attribute__((unused)) GLfloat red, __attribute
|
|||||||
// Set Line Width
|
// Set Line Width
|
||||||
glLineWidth(line_width);
|
glLineWidth(line_width);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Fix Furnace Visual Bug
|
// Fix Furnace Visual Bug
|
||||||
static int FurnaceTileEntity_getLitProgress_injection(FurnaceTileEntity_getLitProgress_t original, FurnaceTileEntity *furnace, int max) {
|
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
|
// Init
|
||||||
static void nop() {
|
template <typename... Args>
|
||||||
|
static void nop(__attribute__((unused)) Args... args) {
|
||||||
}
|
}
|
||||||
void init_misc() {
|
void init_misc() {
|
||||||
// Remove Invalid Item Background (A Red Background That Appears For Items That Are Not Included In The gui_blocks Atlas)
|
// 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);
|
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
|
// Improved Cursor Rendering
|
||||||
if (feature_has("Improved Cursor Rendering", server_disabled)) {
|
if (feature_has("Improved Cursor Rendering", server_disabled)) {
|
||||||
// Disable Normal Cursor Rendering
|
// Disable Normal Cursor Rendering
|
||||||
@ -835,7 +812,6 @@ void init_misc() {
|
|||||||
// Add Custom Cursor Rendering
|
// Add Custom Cursor Rendering
|
||||||
overwrite_calls(GameRenderer_render, GameRenderer_render_injection);
|
overwrite_calls(GameRenderer_render, GameRenderer_render_injection);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Disable V-Sync
|
// Disable V-Sync
|
||||||
if (feature_has("Disable V-Sync", server_enabled)) {
|
if (feature_has("Disable V-Sync", server_enabled)) {
|
||||||
@ -849,13 +825,11 @@ void init_misc() {
|
|||||||
|
|
||||||
// Remove Forced GUI Lag
|
// Remove Forced GUI Lag
|
||||||
if (feature_has("Remove Forced GUI Lag (Can Break Joining Servers)", server_enabled)) {
|
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
|
// Properly Generate Buffers
|
||||||
overwrite(Common_anGenBuffers, anGenBuffers_injection);
|
overwrite(Common_anGenBuffers, anGenBuffers_injection);
|
||||||
#endif
|
|
||||||
|
|
||||||
// Fix Graphics Bug When Switching To First-Person While Sneaking
|
// Fix Graphics Bug When Switching To First-Person While Sneaking
|
||||||
patch_vtable(PlayerRenderer_render, PlayerRenderer_render_injection);
|
patch_vtable(PlayerRenderer_render, PlayerRenderer_render_injection);
|
||||||
@ -928,15 +902,13 @@ void init_misc() {
|
|||||||
}
|
}
|
||||||
overwrite_calls(ChestTileEntity_shouldSave, ChestTileEntity_shouldSave_injection);
|
overwrite_calls(ChestTileEntity_shouldSave, ChestTileEntity_shouldSave_injection);
|
||||||
|
|
||||||
#ifndef MCPI_HEADLESS_MODE
|
|
||||||
// Replace Block Highlight With Outline
|
// Replace Block Highlight With Outline
|
||||||
if (feature_has("Replace Block Highlight With Outline", server_disabled)) {
|
if (feature_has("Replace Block Highlight With Outline", server_disabled)) {
|
||||||
overwrite(LevelRenderer_renderHitSelect, LevelRenderer_renderHitOutline);
|
overwrite(LevelRenderer_renderHitSelect, LevelRenderer_renderHitOutline);
|
||||||
unsigned char fix_outline_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop"
|
unsigned char fix_outline_patch[4] = {0x00, 0xf0, 0x20, 0xe3}; // "nop"
|
||||||
patch((void *) 0x4d830, fix_outline_patch);
|
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
|
// Fix Furnace Visual Bug
|
||||||
overwrite_calls(FurnaceTileEntity_getLitProgress, FurnaceTileEntity_getLitProgress_injection);
|
overwrite_calls(FurnaceTileEntity_getLitProgress, FurnaceTileEntity_getLitProgress_injection);
|
||||||
@ -999,4 +971,12 @@ void init_misc() {
|
|||||||
// Init Logging
|
// Init Logging
|
||||||
_init_misc_logging();
|
_init_misc_logging();
|
||||||
_init_misc_api();
|
_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 <fstream>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <symbols/minecraft.h>
|
#include <symbols/minecraft.h>
|
||||||
|
#include <libreborn/libreborn.h>
|
||||||
|
|
||||||
#include <mods/home/home.h>
|
#include <mods/home/home.h>
|
||||||
#include <mods/init/init.h>
|
#include <mods/init/init.h>
|
||||||
@ -21,7 +15,7 @@ struct server_list_entry {
|
|||||||
std::string address;
|
std::string address;
|
||||||
int port;
|
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 bool server_list_loaded = false;
|
||||||
static void load_servers() {
|
static void load_servers() {
|
||||||
// Prepare
|
// Prepare
|
||||||
@ -54,40 +48,34 @@ static void load_servers() {
|
|||||||
std::string line;
|
std::string line;
|
||||||
while (std::getline(server_list_file, line)) {
|
while (std::getline(server_list_file, line)) {
|
||||||
// Check Line
|
// Check Line
|
||||||
if (line.length() > 0) {
|
if (!line.empty()) {
|
||||||
if (line[0] == '#') {
|
if (line[0] == '#') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Parse
|
// Parse
|
||||||
std::string address;
|
std::string address;
|
||||||
std::string port_str;
|
std::string port_str;
|
||||||
// Add Default Port If Needed
|
size_t separator_pos = line.find_last_of('|');
|
||||||
size_t last_colon = line.find_last_of(':');
|
if (separator_pos == std::string::npos) {
|
||||||
if (last_colon == std::string::npos) {
|
port_str = "19132";
|
||||||
line.append(":19132");
|
address = line;
|
||||||
last_colon = line.find_last_of(':');
|
} else {
|
||||||
}
|
address = line.substr(0, separator_pos);
|
||||||
// Loop
|
port_str = line.substr(separator_pos + 1);
|
||||||
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]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Check Line
|
// 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
|
// Invalid Line
|
||||||
WARN("Invalid Server: %s", line.c_str());
|
WARN("Invalid Server: %s", line.c_str());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Parse Port
|
// Parse Port
|
||||||
int port = std::stoi(port_str);
|
int port = std::stoi(port_str);
|
||||||
|
|
||||||
// Done
|
// Done
|
||||||
struct server_list_entry entry;
|
server_list_entry entry = {
|
||||||
entry.address = address;
|
.address = address,
|
||||||
entry.port = port;
|
.port = port
|
||||||
|
};
|
||||||
server_list_entries.push_back(entry);
|
server_list_entries.push_back(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,7 +86,7 @@ static void load_servers() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Iterare Server List
|
// 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
|
// Load
|
||||||
if (!server_list_loaded) {
|
if (!server_list_loaded) {
|
||||||
load_servers();
|
load_servers();
|
||||||
@ -106,8 +94,7 @@ static void iterate_servers(std::function<void(const char *address, int port)> c
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Loop
|
// Loop
|
||||||
for (std::vector<struct server_list_entry>::size_type i = 0; i < server_list_entries.size(); i++) {
|
for (const server_list_entry &entry : server_list_entries) {
|
||||||
struct server_list_entry entry = server_list_entries[i];
|
|
||||||
callback(entry.address.c_str(), entry.port);
|
callback(entry.address.c_str(), entry.port);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,7 +109,7 @@ static void RakNetInstance_pingForHosts_injection(RakNetInstance_pingForHosts_t
|
|||||||
|
|
||||||
// Add External Servers
|
// Add External Servers
|
||||||
iterate_servers([rak_peer](const char *address, int port) {
|
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
|
// Profile Directory
|
||||||
static std::string profile_directory_suffix =
|
static std::string profile_directory_suffix =
|
||||||
#ifdef MCPI_IS_FLATPAK_BUILD
|
#ifdef MCPI_IS_FLATPAK_BUILD
|
||||||
"/.var/app/" MCPI_APP_ID
|
"/.var/app/" MCPI_APP_ID +
|
||||||
#endif
|
#endif
|
||||||
HOME_SUBDIRECTORY_FOR_GAME_DATA
|
std::string(get_home_subdirectory_for_game_data())
|
||||||
;
|
;
|
||||||
static std::string get_profile_directory_url() {
|
static std::string get_profile_directory_url() {
|
||||||
const char *home = getenv("HOME");
|
const char *home = getenv("HOME");
|
||||||
|
@ -57,10 +57,8 @@ static void Options_initDefaultValue_injection(Options_initDefaultValue_t origin
|
|||||||
original(options);
|
original(options);
|
||||||
|
|
||||||
// Default Graphics Settings
|
// Default Graphics Settings
|
||||||
#ifndef MCPI_SERVER_MODE
|
|
||||||
options->fancy_graphics = true;
|
options->fancy_graphics = true;
|
||||||
options->ambient_occlusion = true;
|
options->ambient_occlusion = true;
|
||||||
#endif
|
|
||||||
|
|
||||||
// Store
|
// Store
|
||||||
stored_options = options;
|
stored_options = options;
|
||||||
@ -133,26 +131,15 @@ static std::vector<std::string> OptionsFile_getOptionStrings_injection(OptionsFi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get New options.txt Path
|
// Get New options.txt Path
|
||||||
#ifndef MCPI_SERVER_MODE
|
static const char *get_new_options_txt_path() {
|
||||||
static char *get_new_options_txt_path() {
|
static std::string path = "";
|
||||||
static char *path = nullptr;
|
|
||||||
// Path
|
// Path
|
||||||
if (path == nullptr) {
|
if (path.empty()) {
|
||||||
safe_asprintf(&path, "%s/options.txt", home_get());
|
path = !reborn_is_server() ? (std::string(home_get()) + "/options.txt") : "/dev/null";
|
||||||
}
|
}
|
||||||
// Return
|
// 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
|
// Init
|
||||||
void init_options() {
|
void init_options() {
|
||||||
|
@ -34,34 +34,32 @@ char *override_get_path(const char *filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get MCPI Home Path
|
// Get MCPI Home Path
|
||||||
char *home_path = home_get();
|
const std::string home_path = home_get();
|
||||||
// Get Asset Override Path
|
// Get Asset Override Path
|
||||||
char *overrides = nullptr;
|
const std::string overrides = home_path + "/overrides";
|
||||||
safe_asprintf(&overrides, "%s/overrides", home_path);
|
|
||||||
|
|
||||||
// Data Prefiix
|
// Data Prefiix
|
||||||
const char *data_prefix = "data/";
|
const std::string data_prefix = "data/";
|
||||||
int data_prefix_length = strlen(data_prefix);
|
int data_prefix_length = data_prefix.length();
|
||||||
|
|
||||||
// Folders To Check
|
// Folders To Check
|
||||||
char *asset_folders[] = {
|
std::string asset_folders[] = {
|
||||||
overrides,
|
overrides,
|
||||||
getenv("MCPI_REBORN_ASSETS_PATH"),
|
getenv("MCPI_REBORN_ASSETS_PATH"),
|
||||||
getenv("MCPI_VANILLA_ASSETS_PATH"),
|
getenv("MCPI_VANILLA_ASSETS_PATH"),
|
||||||
nullptr
|
""
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check For Override
|
// Check For Override
|
||||||
char *new_path = nullptr;
|
std::string new_path;
|
||||||
if (starts_with(filename, data_prefix)) {
|
if (std::string(filename).rfind(data_prefix, 0) == 0) {
|
||||||
// Test Asset Folders
|
// Test Asset Folders
|
||||||
for (int i = 0; asset_folders[i] != nullptr; i++) {
|
for (int i = 0; !asset_folders[i].empty(); i++) {
|
||||||
safe_asprintf(&new_path, "%s/%s", asset_folders[i], &filename[data_prefix_length]);
|
new_path = asset_folders[i] + '/' + &filename[data_prefix_length];
|
||||||
ensure_access();
|
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
|
// Not Found In Asset Folder
|
||||||
free(new_path);
|
new_path = "";
|
||||||
new_path = nullptr;
|
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
// Found
|
// Found
|
||||||
@ -70,11 +68,14 @@ char *override_get_path(const char *filename) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free
|
|
||||||
free(overrides);
|
|
||||||
|
|
||||||
// Return
|
// 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
|
// Hook fopen
|
||||||
|
@ -38,7 +38,12 @@ static int save_png(const char *filename, unsigned char *pixels, int line_size,
|
|||||||
// Write Image
|
// Write Image
|
||||||
return !stbi_write_png(filename, width, height, 4, pixels, line_size);
|
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
|
// Get Directory
|
||||||
char *screenshots = nullptr;
|
char *screenshots = nullptr;
|
||||||
safe_asprintf(&screenshots, "%s/screenshots", home);
|
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 <string>
|
||||||
#include <stdint.h>
|
#include <cstdint>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@ -19,15 +12,16 @@
|
|||||||
|
|
||||||
#include <SDL/SDL.h>
|
#include <SDL/SDL.h>
|
||||||
|
|
||||||
|
#include <libreborn/libreborn.h>
|
||||||
#include <symbols/minecraft.h>
|
#include <symbols/minecraft.h>
|
||||||
|
|
||||||
#include <mods/server/server.h>
|
#include <mods/server/server.h>
|
||||||
|
|
||||||
#include <mods/feature/feature.h>
|
#include <mods/feature/feature.h>
|
||||||
#include <mods/init/init.h>
|
#include <mods/init/init.h>
|
||||||
#include <mods/home/home.h>
|
#include <mods/home/home.h>
|
||||||
#include <mods/compat/compat.h>
|
#include <mods/compat/compat.h>
|
||||||
#include <mods/misc/misc.h>
|
#include <mods/misc/misc.h>
|
||||||
|
#include <mods/game-mode/game-mode.h>
|
||||||
|
|
||||||
// --only-generate: Ony Generate World And Then Exit
|
// --only-generate: Ony Generate World And Then Exit
|
||||||
static bool only_generate = false;
|
static bool only_generate = false;
|
||||||
@ -79,8 +73,8 @@ static void start_world(Minecraft *minecraft) {
|
|||||||
// Specify Level Settings
|
// Specify Level Settings
|
||||||
LevelSettings settings;
|
LevelSettings settings;
|
||||||
settings.game_type = get_server_properties().get_int("game-mode", DEFAULT_GAME_MODE);
|
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);
|
const 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 int32_t seed = get_seed_from_string(seed_str);
|
||||||
settings.seed = seed;
|
settings.seed = seed;
|
||||||
|
|
||||||
// Select Level
|
// Select Level
|
||||||
@ -89,7 +83,7 @@ static void start_world(Minecraft *minecraft) {
|
|||||||
// Don't Open Port When Using --only-generate
|
// Don't Open Port When Using --only-generate
|
||||||
if (!only_generate) {
|
if (!only_generate) {
|
||||||
// Open Port
|
// 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);
|
INFO("Listening On: %i", port);
|
||||||
minecraft->hostMultiplayer(port);
|
minecraft->hostMultiplayer(port);
|
||||||
}
|
}
|
||||||
@ -130,8 +124,8 @@ static Level *get_level(Minecraft *minecraft) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find Players With Username And Run Callback
|
// Find Players With Username And Run Callback
|
||||||
typedef void (*player_callback_t)(Minecraft *minecraft, std::string username, Player *player);
|
typedef void (*player_callback_t)(Minecraft *minecraft, const std::string &username, Player *player);
|
||||||
static void find_players(Minecraft *minecraft, std::string target_username, player_callback_t callback, bool all_players) {
|
static void find_players(Minecraft *minecraft, const std::string &target_username, player_callback_t callback, bool all_players) {
|
||||||
Level *level = get_level(minecraft);
|
Level *level = get_level(minecraft);
|
||||||
std::vector<Player *> players = get_players_in_level(level);
|
std::vector<Player *> players = get_players_in_level(level);
|
||||||
bool found_player = false;
|
bool found_player = false;
|
||||||
@ -177,7 +171,7 @@ static char *get_player_ip(Minecraft *minecraft, Player *player) {
|
|||||||
|
|
||||||
// Ban Player
|
// Ban Player
|
||||||
static bool is_ip_in_blacklist(const char *ip);
|
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
|
// Get IP
|
||||||
char *ip = get_player_ip(minecraft, player);
|
char *ip = get_player_ip(minecraft, player);
|
||||||
|
|
||||||
@ -198,13 +192,13 @@ static void ban_callback(Minecraft *minecraft, std::string username, Player *pla
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Kill Player
|
// 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);
|
player->hurt(nullptr, INT32_MAX);
|
||||||
INFO("Killed: %s", username.c_str());
|
INFO("Killed: %s", username.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// List Player
|
// 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));
|
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 {
|
} else {
|
||||||
// Check List
|
// Check List
|
||||||
for (std::string &x : ips) {
|
for (std::string &x : ips) {
|
||||||
if (x.compare(ip) == 0) {
|
if (x == ip) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,10 @@ static int32_t Textures_loadAndBindTexture_injection(Textures *textures, __attri
|
|||||||
|
|
||||||
// Init
|
// Init
|
||||||
void init_skin() {
|
void init_skin() {
|
||||||
|
// Not Needed On Headless Mode
|
||||||
|
if (reborn_is_headless()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Check Feature Flag
|
// Check Feature Flag
|
||||||
if (feature_has("Load Custom Skins", server_disabled)) {
|
if (feature_has("Load Custom Skins", server_disabled)) {
|
||||||
// LocalPlayer
|
// LocalPlayer
|
||||||
|
@ -117,6 +117,10 @@ static void SoundEngine_init_injection(SoundEngine_init_t original, SoundEngine
|
|||||||
|
|
||||||
// Init
|
// Init
|
||||||
void init_sound() {
|
void init_sound() {
|
||||||
|
// Not Needed On Headless Mode
|
||||||
|
if (reborn_is_headless()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Implement Sound Engine
|
// Implement Sound Engine
|
||||||
if (feature_has("Implement Sound Engine", server_disabled)) {
|
if (feature_has("Implement Sound Engine", server_disabled)) {
|
||||||
overwrite(SoundEngine_playUI, SoundEngine_playUI_injection);
|
overwrite(SoundEngine_playUI, SoundEngine_playUI_injection);
|
||||||
|
@ -10,10 +10,9 @@
|
|||||||
void run_tests() {
|
void run_tests() {
|
||||||
// Test ~/.minecraft-pi Permissions
|
// Test ~/.minecraft-pi Permissions
|
||||||
{
|
{
|
||||||
char *path = home_get();
|
const char *path = home_get();
|
||||||
int exists = access(path, F_OK) == 0;
|
int exists = access(path, F_OK) == 0;
|
||||||
int can_write = exists ? access(path, R_OK | W_OK) == 0 : 1;
|
int can_write = exists ? access(path, R_OK | W_OK) == 0 : 1;
|
||||||
|
|
||||||
if (!can_write) {
|
if (!can_write) {
|
||||||
// Failure
|
// Failure
|
||||||
ERR("Invalid Data Directory Permissions");
|
ERR("Invalid Data Directory Permissions");
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include <libreborn/libreborn.h>
|
#include <libreborn/libreborn.h>
|
||||||
#include <symbols/minecraft.h>
|
#include <symbols/minecraft.h>
|
||||||
#include <mods/init/init.h>
|
|
||||||
|
#include "textures-internal.h"
|
||||||
|
|
||||||
// Disable Texture Loading
|
// 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) {
|
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
|
// Init
|
||||||
void init_textures() {
|
void _init_textures_headless() {
|
||||||
// Disable Texture Loading
|
// Disable Texture Loading
|
||||||
overwrite_calls(AppPlatform_linux_loadTexture, AppPlatform_linux_loadTexture_injection);
|
overwrite_calls(AppPlatform_linux_loadTexture, AppPlatform_linux_loadTexture_injection);
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
#pragma once
|
#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);
|
__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
|
// Init
|
||||||
void init_textures() {
|
void init_textures() {
|
||||||
|
// Handle Headless Mode
|
||||||
|
if (reborn_is_headless()) {
|
||||||
|
_init_textures_headless();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Tick Dynamic Textures (Animated Water)
|
// Tick Dynamic Textures (Animated Water)
|
||||||
bool animated_water = feature_has("Animated Water", server_disabled);
|
bool animated_water = feature_has("Animated Water", server_disabled);
|
||||||
bool animated_lava = feature_has("Animated Lava", server_disabled);
|
bool animated_lava = feature_has("Animated Lava", server_disabled);
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
#include "title-screen-internal.h"
|
#include "title-screen-internal.h"
|
||||||
|
|
||||||
// Constants
|
// 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 line_height = 8;
|
||||||
static int button_width = 120;
|
static int button_width = 120;
|
||||||
static int button_height = 24;
|
static int button_height = 24;
|
||||||
|
@ -47,10 +47,6 @@ const PackageTypes = wrap(new Enum([
|
|||||||
'AppImage',
|
'AppImage',
|
||||||
'Flatpak'
|
'Flatpak'
|
||||||
]));
|
]));
|
||||||
const Variants = wrap(new Enum([
|
|
||||||
'Client',
|
|
||||||
'Server'
|
|
||||||
]));
|
|
||||||
const Architectures = wrap(new Enum([
|
const Architectures = wrap(new Enum([
|
||||||
'AMD64',
|
'AMD64',
|
||||||
'ARM64',
|
'ARM64',
|
||||||
@ -67,7 +63,6 @@ let out = path.join(root, 'out');
|
|||||||
|
|
||||||
// Positional Arguments
|
// Positional Arguments
|
||||||
let argIndex = 2; // Skip First Two Arguments
|
let argIndex = 2; // Skip First Two Arguments
|
||||||
const POSITIONAL_ARGUMENT_COUNT = 3;
|
|
||||||
function readArg(from, type) {
|
function readArg(from, type) {
|
||||||
// Check Argument Count
|
// Check Argument Count
|
||||||
if (argIndex >= process.argv.length) {
|
if (argIndex >= process.argv.length) {
|
||||||
@ -84,8 +79,6 @@ function readArg(from, type) {
|
|||||||
}
|
}
|
||||||
// Type Of Packaging
|
// Type Of Packaging
|
||||||
const packageType = readArg(PackageTypes, 'Package Type');
|
const packageType = readArg(PackageTypes, 'Package Type');
|
||||||
// Build Variant
|
|
||||||
const variant = readArg(Variants, 'Variant');
|
|
||||||
// Build Architecture
|
// Build Architecture
|
||||||
const architecture = readArg(Architectures, 'Architecture');
|
const architecture = readArg(Architectures, 'Architecture');
|
||||||
// Flatpak Builds Work Best Without Custom Toolchains
|
// Flatpak Builds Work Best Without Custom Toolchains
|
||||||
@ -133,7 +126,7 @@ function updateDir(dir) {
|
|||||||
if (packageType !== PackageTypes.None) {
|
if (packageType !== PackageTypes.None) {
|
||||||
dir = path.join(dir, packageType.name);
|
dir = path.join(dir, packageType.name);
|
||||||
}
|
}
|
||||||
return path.join(dir, variant.name, architecture.name);
|
return path.join(dir, architecture.name);
|
||||||
}
|
}
|
||||||
build = updateDir(build);
|
build = updateDir(build);
|
||||||
let cleanOut = false;
|
let cleanOut = false;
|
||||||
@ -147,7 +140,6 @@ if (packageType !== PackageTypes.AppImage) {
|
|||||||
function toCmakeBool(val) {
|
function toCmakeBool(val) {
|
||||||
return val ? 'ON' : 'OFF';
|
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_APPIMAGE_BUILD', toCmakeBool(packageType === PackageTypes.AppImage));
|
||||||
options.set('MCPI_IS_FLATPAK_BUILD', toCmakeBool(packageType === PackageTypes.Flatpak));
|
options.set('MCPI_IS_FLATPAK_BUILD', toCmakeBool(packageType === PackageTypes.Flatpak));
|
||||||
if (architecture !== Architectures.Host) {
|
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
|
fi
|
||||||
|
|
||||||
# Main Script
|
# Run APT
|
||||||
run() {
|
install_pkg() {
|
||||||
# Add ARM Repository
|
sudo apt-get install --no-install-recommends -y "$@"
|
||||||
for arch in "$@"; do
|
}
|
||||||
sudo dpkg --add-architecture "$(echo "${arch}" | tr '[:upper:]' '[:lower:]')"
|
|
||||||
done
|
|
||||||
|
|
||||||
# Update APT
|
# Build Dependencies
|
||||||
sudo apt-get update
|
run_build() {
|
||||||
sudo apt-get dist-upgrade -y
|
install_pkg \
|
||||||
|
`# Build System` \
|
||||||
# Install Everything In One Go
|
|
||||||
PKG_QUEUE=''
|
|
||||||
queue_pkg() {
|
|
||||||
PKG_QUEUE="${PKG_QUEUE} $@"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Build System
|
|
||||||
queue_pkg \
|
|
||||||
git \
|
git \
|
||||||
cmake \
|
cmake \
|
||||||
ninja-build \
|
ninja-build \
|
||||||
python3 \
|
python3 \
|
||||||
python3-venv
|
python3-venv \
|
||||||
|
`# Host Dependencies Needed For Compile` \
|
||||||
# Host Dependencies Needed For Compile
|
libwayland-bin \
|
||||||
queue_pkg \
|
`# Compiler` \
|
||||||
libwayland-bin
|
"crossbuild-essential-$1" \
|
||||||
|
`# Main Dependencies` \
|
||||||
# Architecture-Specific Dependencies
|
"libopenal-dev:$1" \
|
||||||
architecture_specific_pkg() {
|
`# GLFW Dependencies` \
|
||||||
# Compiler
|
"libwayland-dev:$1" \
|
||||||
queue_pkg crossbuild-essential-$1
|
"libxkbcommon-dev:$1" \
|
||||||
|
"libx11-dev:$1" \
|
||||||
# Dependencies
|
"libxcursor-dev:$1" \
|
||||||
queue_pkg \
|
"libxi-dev:$1" \
|
||||||
libopenal-dev:$1
|
"libxinerama-dev:$1" \
|
||||||
|
"libxrandr-dev:$1" \
|
||||||
# GLFW Dependencies
|
"libxext-dev:$1" \
|
||||||
queue_pkg \
|
`# Zenity Dependencies` \
|
||||||
libwayland-dev:$1 \
|
"libgtk-3-dev:$1" \
|
||||||
libxkbcommon-dev:$1 \
|
"libglib2.0-dev:$1" \
|
||||||
libx11-dev:$1 \
|
`# AppStream Verification` \
|
||||||
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 \
|
|
||||||
appstream
|
appstream
|
||||||
|
|
||||||
# Install Queue
|
|
||||||
sudo apt-get install --no-install-recommends -y ${PKG_QUEUE}
|
|
||||||
|
|
||||||
# Install appimagetool
|
# Install appimagetool
|
||||||
sudo rm -rf /opt/squashfs-root /opt/appimagetool.AppDir
|
sudo rm -rf /opt/squashfs-root /opt/appimagetool /usr/local/bin/appimagetool
|
||||||
sudo rm -f /opt/appimagetool /usr/local/bin/appimagetool
|
case "$(dpkg --print-architecture)" in
|
||||||
case "$(dpkg-architecture -qDEB_BUILD_ARCH)" in
|
|
||||||
'armhf') APPIMAGE_ARCH='armhf';;
|
'armhf') APPIMAGE_ARCH='armhf';;
|
||||||
'arm64') APPIMAGE_ARCH='aarch64';;
|
'arm64') APPIMAGE_ARCH='aarch64';;
|
||||||
'i386') APPIMAGE_ARCH='i686';;
|
'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 wget -O /opt/appimagetool "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-${APPIMAGE_ARCH}.AppImage"
|
||||||
sudo chmod +x /opt/appimagetool
|
sudo chmod +x /opt/appimagetool
|
||||||
# Workaround AppImage Issues With Docker
|
# Workaround AppImage Issues With Docker
|
||||||
cd /opt
|
sudo ./scripts/fix-appimage-for-docker.sh /opt/appimagetool
|
||||||
sudo sed -i '0,/AI\x02/{s|AI\x02|\x00\x00\x00|}' ./appimagetool
|
|
||||||
# Extract
|
# Extract
|
||||||
sudo ./appimagetool --appimage-extract
|
cd /opt
|
||||||
|
sudo ./appimagetool --appimage-extract > /dev/null
|
||||||
sudo rm -f ./appimagetool
|
sudo rm -f ./appimagetool
|
||||||
# Link
|
# Link
|
||||||
sudo mv ./squashfs-root ./appimagetool.AppDir
|
sudo mv ./squashfs-root ./appimagetool
|
||||||
sudo ln -s /opt/appimagetool.AppDir/AppRun /usr/local/bin/appimagetool
|
sudo ln -s /opt/appimagetool/AppRun /usr/local/bin/appimagetool
|
||||||
}
|
}
|
||||||
|
|
||||||
# Run
|
# Test Dependencies
|
||||||
if [ "$#" -lt 1 ]; then
|
run_test() {
|
||||||
run "$(dpkg-architecture -qDEB_BUILD_ARCH)"
|
sudo apt-get install --no-install-recommends -y \
|
||||||
else
|
"libc6:$1" \
|
||||||
run "$@"
|
"libopenal1:$1" \
|
||||||
fi
|
"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
|
# Variables
|
||||||
MODE="$(echo "$1" | tr '[:upper:]' '[:lower:]')"
|
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
|
# Check If File Exists
|
||||||
./scripts/build.mjs none "${MODE}" "${ARCH}" -DMCPI_HEADLESS_MODE=ON
|
if [ ! -f "${APPIMAGE}" ]; then
|
||||||
|
echo 'Missing AppImage!' > /dev/stderr
|
||||||
# Add To PATH
|
exit 1
|
||||||
export PATH="$(pwd)/out/${MODE}/${ARCH}/usr/bin:${PATH}"
|
fi
|
||||||
|
|
||||||
# Make Test Directory
|
# Make Test Directory
|
||||||
TEST_WORKING_DIR="$(pwd)/.testing-tmp"
|
TEST_WORKING_DIR="$(pwd)/.testing-tmp"
|
||||||
rm -rf "${TEST_WORKING_DIR}"
|
rm -rf "${TEST_WORKING_DIR}"
|
||||||
mkdir -p "${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
|
# Run
|
||||||
if [ "${MODE}" = "server" ]; then
|
if [ "${MODE}" = "server" ]; then
|
||||||
# Server Test
|
# Server Test
|
||||||
cd "${TEST_WORKING_DIR}"
|
./tmp.AppImage --appimage-extract-and-run --server --only-generate
|
||||||
minecraft-pi-reborn-server --only-generate
|
|
||||||
else
|
else
|
||||||
# Client Test
|
# Client Test
|
||||||
export _MCPI_SKIP_ROOT_CHECK=1
|
|
||||||
export HOME="${TEST_WORKING_DIR}"
|
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
|
fi
|
||||||
|
|
||||||
# Clean Up
|
# Clean Up
|
||||||
|
Loading…
Reference in New Issue
Block a user