Compare commits

..

83 Commits

Author SHA1 Message Date
6f5f405327 Fix Build 2025-01-05 13:09:10 -05:00
489615d47f More Changes 2025-01-04 05:38:38 -05:00
00fee9c410 Basic AppImage Updater 2025-01-03 15:03:30 -05:00
b055980103 JS-Ify Dependency Installation 2025-01-03 10:20:33 -05:00
b767b9d4ec Update Build Script 2025-01-03 08:25:09 -05:00
c5f9a519c5 Basic About 2025-01-03 05:36:28 -05:00
9428a5a1e1 Work On Using Multi-Config 2024-12-22 03:34:31 -05:00
013c1db336 Use CMAKE_BUILD_TYPE Less 2024-12-21 05:10:13 -05:00
a80b5fb5c2 Skip Unnecessary SDK Copying 2024-12-21 04:46:31 -05:00
bf24ace78e Generate SDK Hash 2024-12-19 03:44:36 -05:00
2568b05053 Fix Held Item Poking Through Screen Overlay 2024-12-19 01:28:48 -05:00
dcafeb5c44 Fix Furnace Fix 2024-12-18 21:08:06 -05:00
3771666a14 Batch Font Rendering 2024-12-18 03:04:36 -05:00
b71c089fb3 Fix Hanging When No Valid Spawn Point Exists 2024-12-18 02:44:51 -05:00
71946d2087 Some Fixes 2024-12-18 02:20:07 -05:00
b2ec2728e3 Update Symbol Processor 2024-12-18 01:19:44 -05:00
71042da861 Fix Crash On NVIDIA + Improve Splash Oscillation 2024-12-17 21:17:45 -05:00
5d8aa28113 Make Biome-Tinted Grass Not Suck 2024-12-17 09:22:48 -05:00
aea31dd4c8 Fix Example Mod 2024-12-17 05:55:28 -05:00
f0005ff002 Some Fixes 2024-12-17 05:50:20 -05:00
587ba38ffe Store Server List In Launcher Cache 2024-12-14 03:08:19 -05:00
c6048ec4fb Start Logger After Launcher UI 2024-12-08 15:34:03 -05:00
0ccf578478 Embrace Integer Scaling 2024-11-30 05:33:50 -05:00
814217a259 Better UI Scaling 2024-11-28 05:25:22 -05:00
cfac7d0a12 WIP UI Scale Slider 2024-11-28 02:42:22 -05:00
c531e7ba7d Add Servers Tab To Launcher 2024-11-26 06:47:48 -05:00
bddc299664 Fix GLAD CI Build 2024-11-24 21:31:26 -05:00
cc060accaf Use Legacy GLAD 2024-11-24 21:01:00 -05:00
09c8af0396 Simplify Cache Loading 2024-11-24 19:57:52 -05:00
a740814354 Fix Bug When Resizing ImGui 2024-11-24 18:59:23 -05:00
890bd537b2 Use GLAD 2024-11-24 17:49:17 -05:00
983e474b33 Partly Fix CI 2024-11-23 08:03:10 -05:00
2c3bb41293 Third Time's The Charm! 2024-11-23 00:01:27 -05:00
3d89fb691a Some Fixes 2024-11-22 22:02:55 -05:00
4e1476bcfd Fixing The CI: Take 2 2024-11-22 04:41:53 -05:00
8ed425392a Fix CI (Hopefully) 2024-11-22 04:32:06 -05:00
b2c13c8257 Tweak Camera 2024-11-22 04:20:22 -05:00
2eb6a1c5be Rename GUI Flags To UI 2024-11-22 03:35:26 -05:00
ef3292c5e0 Change Flag Grouping 2024-11-22 03:30:34 -05:00
67ceb4ad00 More Feature Flags! 2024-11-22 03:07:54 -05:00
e1d9fc492b Remove Some Macros 2024-11-22 01:09:54 -05:00
acec86b9b5 Update Example Mods 2024-11-22 00:54:34 -05:00
66d2e43f55 Move Some Stuff Around 2024-11-22 00:47:38 -05:00
596ff01f75 Remove libreborn.h: Part 3 2024-11-22 00:22:49 -05:00
57aed4d0b3 Remove libreborn.h: Part 2 2024-11-21 23:46:23 -05:00
fd26000fd4 Remove libreborn.h: Part 1 2024-11-21 22:36:05 -05:00
454734ab68 Improve feature_has 2024-11-21 22:07:50 -05:00
d3b70878be New Crash Report UI 2024-11-21 21:45:57 -05:00
7f9d1d843e Small Fix 2024-11-21 17:41:24 -05:00
900169a728 Add "Revert" Button 2024-11-21 16:16:53 -05:00
633b165af0 Tweak UI 2024-11-21 14:50:26 -05:00
86e8c0dd67 Update GLFW 2024-11-21 14:06:43 -05:00
332acd49fb Better extend_struct 2024-11-21 14:03:59 -05:00
c2750bbaec More Color Tweaking 2024-11-21 04:29:05 -05:00
70ef421780 Move Reset Button 2024-11-21 04:07:50 -05:00
ed59e19c52 Add Reset Button 2024-11-21 03:53:01 -05:00
dd760cc6f2 Disable Obsolete ImGui Functions 2024-11-21 03:29:37 -05:00
00f90afc2a Color UI 2024-11-21 03:16:11 -05:00
4a91937b0a New Launcher UI 2024-11-21 02:16:25 -05:00
a6cc0b88b5 Leaner ImGui Build 2024-11-20 12:36:34 -05:00
0b542701c5 ImGui Hello World 2024-11-20 12:22:36 -05:00
fc7ecd528a Embed ImGui Fonts 2024-11-20 10:16:50 -05:00
2785e3f138 Build ImGui 2024-11-19 22:48:50 -05:00
386f52a85f CMake Refactor 2024-11-19 19:57:43 -05:00
871288ee12 Fix Example Mod (Again) 2024-11-18 20:15:15 -05:00
5d7056645d Some Fixes 2024-11-18 18:47:13 -05:00
ecbbcef203 Some Renaming/Moving 2024-11-18 01:56:11 -05:00
bbae01a471 Small Tweaks 2024-11-17 22:55:25 -05:00
6b5105e74d Remove OpenGL Occlusion Functions 2024-11-17 22:50:15 -05:00
57503d6a31 More C++ Refactors 2024-11-17 22:48:25 -05:00
58a6706cf9 Some Updates 2024-11-15 15:26:33 -05:00
f501f9a7c9 Please Just Work 2024-11-10 19:05:44 -05:00
203d46c849 Update Archives 2024-11-10 18:52:42 -05:00
58efe19609 Fix CMake 2024-11-10 05:42:56 -05:00
23b3cbe72f Update Runtime 2024-11-10 05:26:41 -05:00
b339b53f42 Revert That, It Sucked 2024-11-09 00:21:41 -05:00
a8d0962491 Occlusion Checking 2024-11-09 00:17:25 -05:00
a63125f335 Add OpenGL Query Functions 2024-11-08 21:40:12 -05:00
eeace9cf14 Small Rename 2024-11-08 20:50:34 -05:00
dd25805af9 Fancy New API! 2024-11-08 02:34:58 -05:00
a6dad72778 Fix Sugar Position In Hand 2024-11-03 20:04:19 -05:00
9ae6cd17cf Convert API Chat Posts To CP-437 2024-11-03 01:41:28 -04:00
644e9e421b Finally Fix Dumb Method Names 2024-11-03 00:02:13 -04:00
286 changed files with 5920 additions and 5339 deletions

View File

@ -1,5 +1,4 @@
name: 'CI'
on:
push:
branches:
@ -8,6 +7,7 @@ on:
- '*'
- '!flatpak'
# Jobs
jobs:
# Build Project
build:
@ -28,12 +28,12 @@ jobs:
submodules: true
# Dependencies
- name: Install Dependencies
run: ./scripts/install-dependencies.sh build ${{ matrix.arch }}
run: ./scripts/install-dependencies.mjs build ${{ matrix.arch }}
# Build
- name: Build
run: ./scripts/build.mjs appimage ${{ matrix.arch }}
- name: Upload Artifacts
uses: christopherhx/gitea-upload-artifact@v4
uses: actions/upload-artifact@v3
with:
name: ${{ matrix.arch }}
path: ./out/*.AppImage*
@ -61,10 +61,10 @@ jobs:
submodules: false
# Dependencies
- name: Install Dependencies
run: ./scripts/install-dependencies.sh test ${{ matrix.arch }}
run: ./scripts/install-dependencies.mjs test ${{ matrix.arch }}
# Download Artifact
- name: Download Artifact
uses: christopherhx/gitea-download-artifact@v4
uses: actions/download-artifact@v3
with:
name: ${{ matrix.arch }}
path: out
@ -84,10 +84,10 @@ jobs:
submodules: false
# Dependencies
- name: Install Dependencies
run: ./scripts/install-dependencies.sh example_mods amd64
run: ./scripts/install-dependencies.mjs sdk amd64
# SDK
- name: Download SDK
uses: christopherhx/gitea-download-artifact@v4
uses: actions/download-artifact@v3
with:
name: AMD64
path: out
@ -100,7 +100,7 @@ jobs:
- name: Build Example Mods
run: ./example-mods/build.sh
- name: Upload Artifacts
uses: christopherhx/gitea-upload-artifact@v4
uses: actions/upload-artifact@v3
with:
name: Example Mods
path: ./example-mods/out/*
@ -123,7 +123,7 @@ jobs:
go-version: '>=1.20.1'
# Download Artifacts
- name: Download Artifacts
uses: christopherhx/gitea-download-artifact@v4
uses: actions/download-artifact@v3
with:
path: out
# Create Release
@ -133,4 +133,4 @@ jobs:
files: ./out/*/*.AppImage*
api_key: ${{ secrets.RELEASE_TOKEN }}
title: v${{ github.ref_name }}
body: "[View Changelog](https://gitea.thebrokenrail.com/minecraft-pi-reborn/minecraft-pi-reborn/src/branch/master/docs/CHANGELOG.md)"
body: '[View Changelog](https://gitea.thebrokenrail.com/minecraft-pi-reborn/minecraft-pi-reborn/src/branch/master/docs/CHANGELOG.md)'

11
.gitmodules vendored
View File

@ -1,9 +1,6 @@
[submodule "dependencies/glfw/src"]
path = dependencies/glfw/src
url = https://github.com/glfw/glfw.git
[submodule "dependencies/zenity/src"]
path = dependencies/zenity/src
url = https://gitea.thebrokenrail.com/minecraft-pi-reborn/zenity.git
[submodule "dependencies/LIEF/src"]
path = dependencies/LIEF/src
url = https://github.com/lief-project/LIEF.git
@ -16,9 +13,17 @@
[submodule "archives"]
path = archives
url = https://gitea.thebrokenrail.com/minecraft-pi-reborn/archives.git
shallow = true
[submodule "dependencies/symbol-processor/src"]
path = dependencies/symbol-processor/src
url = https://gitea.thebrokenrail.com/minecraft-pi-reborn/symbol-processor.git
[submodule "dependencies/runtime/src"]
path = dependencies/runtime/src
url = https://gitea.thebrokenrail.com/minecraft-pi-reborn/runtime.git
[submodule "dependencies/imgui/src"]
path = dependencies/imgui/src
url = https://github.com/ocornut/imgui.git
ignore = dirty
[submodule "dependencies/imgui/glad/src"]
path = dependencies/imgui/glad/src
url = https://github.com/Dav1dde/glad.git

View File

@ -1,35 +1,39 @@
cmake_minimum_required(VERSION 3.17.0)
cmake_minimum_required(VERSION 3.25.0)
# Avoid Warning About DOWNLOAD_EXTRACT_TIMESTAMP
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24.0)
cmake_policy(SET CMP0135 NEW)
endif()
cmake_policy(SET CMP0135 NEW)
# Core Options
include(cmake/options/core-options.cmake)
# Utility Functions
include(cmake/util/util.cmake)
# Build Mode
if(NOT DEFINED CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE)
get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(IS_MULTI_CONFIG)
force_set(CMAKE_CONFIGURATION_TYPES "Release;Debug" STRING)
set(FORCE_BUILD_TYPE "")
elseif(NOT DEFINED CMAKE_BUILD_TYPE)
set(FORCE_BUILD_TYPE "Release")
endif()
if(DEFINED FORCE_BUILD_TYPE)
force_set(CMAKE_BUILD_TYPE "${FORCE_BUILD_TYPE}" STRING)
endif()
# Start Project
project(minecraft-pi-reborn)
# Utility Functions
include(cmake/util/util.cmake)
# Sanity Checks
string(CONCAT ARM_SANITY_CHECK
"include(CheckSymbolExists)\n"
"check_symbol_exists(\"__arm__\" \"\" IS_ARM_TARGETING)"
"check_symbol_exists(\"__arm__\" \"\" IS_ARM_TARGETING)\n"
)
if(BUILD_ARM_COMPONENTS)
string(CONCAT ARM_SANITY_CHECK
"${ARM_SANITY_CHECK}\n"
string(APPEND ARM_SANITY_CHECK
"if(NOT IS_ARM_TARGETING)\n"
" message(FATAL_ERROR \"ARM-Targeting Compiler Required\")\n"
"endif()"
"endif()\n"
)
endif()
cmake_language(EVAL CODE "${ARM_SANITY_CHECK}")
@ -41,14 +45,11 @@ include(cmake/options/extra-options.cmake)
include(cmake/options/paths.cmake)
# Required Compile Flags
set(RELEASE_MODE_GENERATOR "\$<CONFIG:Release>")
string(CONCAT COMPILE_FLAGS_SETUP
# Optimizations
"if(CMAKE_BUILD_TYPE STREQUAL \"Release\")\n"
" add_compile_options(-O3)\n"
" add_link_options(-s)\n"
"else()\n"
" add_compile_options(-g)\n"
"endif()\n"
"add_compile_options(\"\$<IF:${RELEASE_MODE_GENERATOR},-O3,-g>\")\n"
"add_link_options(\"\$<${RELEASE_MODE_GENERATOR}:-s>\")\n"
# PIC
"set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)\n"
@ -62,10 +63,22 @@ string(CONCAT COMPILE_FLAGS_SETUP
"set(CMAKE_CXX_STANDARD 20)\n"
# Skip RPath
"set(CMAKE_SKIP_BUILD_RPATH TRUE)"
"set(CMAKE_SKIP_BUILD_RPATH TRUE)\n"
# Always Build Shared Libraries
"set(BUILD_SHARED_LIBS TRUE CACHE BOOL \"\" FORCE)\n"
)
if(BUILD_ARM_COMPONENTS)
string(APPEND COMPILE_FLAGS_SETUP
# Disable C++11 String ABI
"add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0)\n"
)
endif()
cmake_language(EVAL CODE "${COMPILE_FLAGS_SETUP}")
# Build Dependencies
add_subdirectory(dependencies)
# Fast Math
add_compile_options(-ffast-math)
@ -81,9 +94,6 @@ if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
endif()
endif()
# Buld Dependencies
add_subdirectory(dependencies)
# Build libreborn
add_subdirectory(libreborn)
@ -117,18 +127,35 @@ endif()
# Install SDK
if(BUILD_ARM_COMPONENTS)
install(EXPORT sdk DESTINATION "${MCPI_SDK_DIR}" FILE "sdk-targets.cmake" EXPORT_LINK_INTERFACE_LIBRARIES)
install(EXPORT sdk DESTINATION "${MCPI_SDK_DIR}" FILE "sdk-targets.cmake")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/sdk.cmake"
# Sanity Check
"${ARM_SANITY_CHECK}"
# Compile Flags
"${COMPILE_FLAGS_SETUP}\n"
# Snaity Check
"${ARM_SANITY_CHECK}\n"
"${COMPILE_FLAGS_SETUP}"
# Log
"message(STATUS \"Using Reborn SDK v${MCPI_VERSION}\")\n"
# Include Targets
"include(\"\${CMAKE_CURRENT_LIST_DIR}/sdk-targets.cmake\")\n"
)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/sdk.cmake" DESTINATION "${MCPI_SDK_DIR}")
# Calculate Hash Of SDK
string(CONCAT SDK_HASH_SCRIPT
# Prepare
"set(dir \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${MCPI_SDK_DIR}\")\n"
"set(out \"\${dir}/.hash\")\n"
# Calculate Hashes
"set(content \"\")\n"
"file(GLOB_RECURSE files LIST_DIRECTORIES FALSE \"\${dir}/*\")\n"
"foreach(file IN LISTS files)\n"
" file(SHA256 \"\${file}\" hash)\n"
" cmake_path(RELATIVE_PATH file BASE_DIRECTORY \"\${dir}\")\n"
" string(APPEND content \"\${hash} \${file}\\n\")\n"
"endforeach()\n"
# Write File
"file(WRITE \"\${out}\" \"\${content}\")\n"
)
install(CODE "${SDK_HASH_SCRIPT}")
endif()
# Packaging
@ -146,27 +173,37 @@ if(BUILD_NATIVE_COMPONENTS)
list(APPEND ARM_OPTIONS "-DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>")
if(NOT MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN)
if(DEFINED CMAKE_TOOLCHAIN_FILE)
list(APPEND ARM_OPTIONS "-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${CMAKE_TOOLCHAIN_FILE}")
set(ARM_TOOLCHAIN "${CMAKE_TOOLCHAIN_FILE}")
endif()
else()
list(APPEND ARM_OPTIONS "-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${MCPI_CMAKE_TOOLCHAIN_FILE}")
set(ARM_TOOLCHAIN "${MCPI_CMAKE_TOOLCHAIN_FILE}")
endif()
if(DEFINED ARM_TOOLCHAIN)
list(APPEND ARM_OPTIONS "-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${ARM_TOOLCHAIN}")
endif()
list(APPEND ARM_OPTIONS "-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}")
# Build
ExternalProject_Add(arm-components
# Source Directory
DOWNLOAD_COMMAND ""
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}"
# Configure
CMAKE_CACHE_ARGS ${ARM_OPTIONS}
CMAKE_GENERATOR "Ninja Multi-Config"
# Build
BUILD_COMMAND
"${CMAKE_COMMAND}" "--build" "<BINARY_DIR>" "--config" "$<CONFIG>"
# Install
INSTALL_COMMAND
"${CMAKE_COMMAND}" "-E"
"rm" "-rf" "<INSTALL_DIR>/${MCPI_INSTALL_DIR}"
COMMAND
"${CMAKE_COMMAND}" "-E" "env"
"DESTDIR="
"${CMAKE_COMMAND}" "--install" "<BINARY_DIR>"
"${CMAKE_COMMAND}" "-E" "env" "DESTDIR="
"${CMAKE_COMMAND}" "--install" "<BINARY_DIR>" "--config" "$<CONFIG>"
# Use Terminal
USES_TERMINAL_CONFIGURE TRUE
USES_TERMINAL_BUILD TRUE
USES_TERMINAL_INSTALL TRUE
# Always Build
BUILD_ALWAYS TRUE
)
# Install

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2024 TheBrokenRail
Copyright (c) 2025 TheBrokenRail
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

@ -1 +1 @@
Subproject commit 2f5953779674ec3a14114aa34b24c81005571ec4
Subproject commit 5baa6b1948aeebb5e13af31ff62dc97f00a3b71e

View File

@ -1,4 +1,4 @@
# Downlaod AppImage Runtime
# Download AppImage Runtime
set(RUNTIME_ARCH "unknown")
if(CPACK_MCPI_ARCH STREQUAL "armhf")
set(RUNTIME_ARCH "armhf")
@ -9,14 +9,14 @@ elseif(CPACK_MCPI_ARCH STREQUAL "amd64")
endif()
set(RUNTIME "${CPACK_TOPLEVEL_DIRECTORY}/runtime")
file(DOWNLOAD
"https://github.com/AppImage/AppImageKit/releases/download/continuous/runtime-${RUNTIME_ARCH}"
"https://github.com/AppImage/type2-runtime/releases/download/continuous/runtime-${RUNTIME_ARCH}"
"${RUNTIME}"
STATUS DOWNLOAD_STATUS
)
list(GET DOWNLOAD_STATUS 0 STATUS_CODE)
list(GET DOWNLOAD_STATUS 1 ERROR_MESSAGE)
if(NOT STATUS_CODE EQUAL 0)
message(FATAL_ERROR "Unable To Downlopad AppImage Runtime: ${ERROR_MESSAGE}")
message(FATAL_ERROR "Unable To Download AppImage Runtime: ${ERROR_MESSAGE}")
else()
message(STATUS "Downloaded AppImage Runtime: ${RUNTIME}")
endif()
@ -34,21 +34,19 @@ execute_process(
COMMAND
"${CMAKE_COMMAND}" "-E" "env"
"ARCH=${APPIMAGE_ARCH}"
"VERSION=${CPACK_MCPI_VERSION}"
"appimagetool"
"--updateinformation" "zsync|https://gitea.thebrokenrail.com/minecraft-pi-reborn/minecraft-pi-reborn/releases/download/latest/${CPACK_PACKAGE_FILE_NAME_ZSYNC}.AppImage.zsync"
"--updateinformation" "zsync|${CPACK_MCPI_REPO}/releases/download/latest/${CPACK_PACKAGE_FILE_NAME_ZSYNC}${CPACK_MCPI_APPIMAGE_ZSYNC_EXT}"
"--runtime-file" "${RUNTIME}"
"--comp" "xz"
"--comp" "zstd"
"${CPACK_TEMPORARY_DIRECTORY}"
"${CPACK_PACKAGE_FILE_NAME}.AppImage"
"${CPACK_PACKAGE_FILE_NAME}${CPACK_MCPI_APPIMAGE_EXT}"
WORKING_DIRECTORY "${CPACK_PACKAGE_DIRECTORY}"
RESULT_VARIABLE APPIMAGETOOL_RESULT
COMMAND_ERROR_IS_FATAL ANY
)
if(NOT APPIMAGETOOL_RESULT EQUAL 0)
message(FATAL_ERROR "Unable Package AppImage")
endif()
# Rename ZSync File
file(RENAME "${CPACK_PACKAGE_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}.AppImage.zsync" "${CPACK_PACKAGE_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME_ZSYNC}.AppImage.zsync")
file(RENAME "${CPACK_PACKAGE_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}${CPACK_MCPI_APPIMAGE_ZSYNC_EXT}" "${CPACK_PACKAGE_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME_ZSYNC}${CPACK_MCPI_APPIMAGE_ZSYNC_EXT}")
# Summary Message
function(check_file name)
@ -58,5 +56,5 @@ function(check_file name)
message(FATAL_ERROR "Missing File: ${name}")
endif()
endfunction()
check_file("${CPACK_PACKAGE_FILE_NAME}.AppImage")
check_file("${CPACK_PACKAGE_FILE_NAME_ZSYNC}.AppImage.zsync")
check_file("${CPACK_PACKAGE_FILE_NAME}${CPACK_MCPI_APPIMAGE_EXT}")
check_file("${CPACK_PACKAGE_FILE_NAME_ZSYNC}${CPACK_MCPI_APPIMAGE_ZSYNC_EXT}")

View File

@ -1,25 +1,10 @@
# Determine Architecture
set(CPACK_MCPI_ARCH "unknown")
include(CheckSymbolExists)
function(check_arch symbol name)
set(CMAKE_REQUIRED_QUIET TRUE)
check_symbol_exists("${symbol}" "" "IS_ARCH_${name}")
unset(CMAKE_REQUIRED_QUIET)
if("${IS_ARCH_${name}}")
set(CPACK_MCPI_ARCH "${name}" PARENT_SCOPE)
endif()
endfunction()
check_arch("__arm__" "armhf")
check_arch("__aarch64__" "arm64")
check_arch("__x86_64__" "amd64")
# CPack
set(CPACK_PACKAGE_NAME "${MCPI_VARIANT_NAME}")
set(CPACK_PACKAGE_VENDOR "TheBrokenRail & Mojang AB")
set(CPACK_PACKAGE_NAME "${MCPI_APP_NAME}")
set(CPACK_PACKAGE_VENDOR "${MCPI_AUTHOR} & Mojang AB")
set(CPACK_VERBATIM_VARIABLES TRUE)
set(CPACK_MONOLITHIC_INSTALL TRUE)
set(CPACK_PACKAGE_FILE_NAME "${MCPI_VARIANT_NAME}-${MCPI_VERSION}-${CPACK_MCPI_ARCH}")
set(CPACK_PACKAGE_FILE_NAME_ZSYNC "${MCPI_VARIANT_NAME}-latest-${CPACK_MCPI_ARCH}")
get_package_file_name(CPACK_PACKAGE_FILE_NAME "${MCPI_VERSION}")
get_package_file_name(CPACK_PACKAGE_FILE_NAME_ZSYNC "latest")
# Version
string(REPLACE "." ";" VERSION_LIST "${MCPI_VERSION}")
@ -32,6 +17,15 @@ if(MCPI_IS_APPIMAGE_BUILD)
set(CPACK_GENERATOR "External")
set(CPACK_EXTERNAL_ENABLE_STAGING TRUE)
set(CPACK_EXTERNAL_PACKAGE_SCRIPT "${CMAKE_CURRENT_LIST_DIR}/appimage.cmake")
# Pass Variable To CPack
macro(pass_to_cpack var)
set("CPACK_MCPI_${var}" "${MCPI_${var}}")
endmacro()
pass_to_cpack(VERSION)
pass_to_cpack(ARCH)
pass_to_cpack(REPO)
pass_to_cpack(APPIMAGE_EXT)
pass_to_cpack(APPIMAGE_ZSYNC_EXT)
endif()
# Package

View File

View File

@ -29,13 +29,9 @@ else()
set(BUILD_MEDIA_LAYER_CORE "${BUILD_ARM_COMPONENTS}")
endif()
# Specify Variant Name
set(MCPI_VARIANT_NAME "minecraft-pi-reborn")
# App ID
# App Information
mcpi_option(APP_NAME "App Name" STRING "minecraft-pi-reborn")
mcpi_option(APP_ID "App ID" STRING "com.thebrokenrail.MCPIReborn")
# App Title
mcpi_option(APP_TITLE "App Title" STRING "Minecraft: Pi Edition: Reborn")
# Skin Server
@ -53,5 +49,41 @@ set_property(
file(STRINGS "${CMAKE_CURRENT_LIST_DIR}/../../VERSION" MCPI_VERSION)
file(TIMESTAMP "${CMAKE_CURRENT_LIST_DIR}/../../VERSION" MCPI_VERSION_DATE "%Y-%m-%d" UTC)
# Author
mcpi_option(AUTHOR "Author" STRING "TheBrokenRail")
# Homepage
mcpi_option(REPO_HOST "Repository Host" STRING "https://gitea.thebrokenrail.com")
mcpi_option(REPO_PATH "Repository Path" STRING "minecraft-pi-reborn/minecraft-pi-reborn")
mcpi_option(REPO "Repository URL" STRING "${MCPI_REPO_HOST}/${MCPI_REPO_PATH}")
# Documentation URL
mcpi_option(DOCUMENTATION "Documentation URL" STRING "https://gitea.thebrokenrail.com/minecraft-pi-reborn/minecraft-pi-reborn/src/tag/${MCPI_VERSION}/docs/")
mcpi_option(DOCS "Documentation URL" STRING "${MCPI_REPO}/src/tag/${MCPI_VERSION}/docs/")
# Packaging
set(MCPI_ARCH "unknown")
include(CheckSymbolExists)
function(check_arch symbol name)
set(CMAKE_REQUIRED_QUIET TRUE)
check_symbol_exists("${symbol}" "" "IS_ARCH_${name}")
unset(CMAKE_REQUIRED_QUIET)
if("${IS_ARCH_${name}}")
set(MCPI_ARCH "${name}" PARENT_SCOPE)
endif()
endfunction()
check_arch("__arm__" "armhf")
check_arch("__aarch64__" "arm64")
check_arch("__x86_64__" "amd64")
macro(get_package_file_name out version)
set("${out}" "${MCPI_APP_NAME}-${version}-${MCPI_ARCH}")
endmacro()
# AppImage
if(MCPI_IS_APPIMAGE_BUILD)
mcpi_option(APPIMAGE_EXT "AppImage Extension" STRING ".AppImage")
mcpi_option(APPIMAGE_ZSYNC_EXT "AppImage Update Extension" STRING "${MCPI_APPIMAGE_EXT}.zsync")
mcpi_option(APPIMAGE_JSON_URL "AppImage Update Checker URL" STRING "${MCPI_REPO_HOST}/api/v1/repos/${MCPI_REPO_PATH}/releases/latest")
mcpi_option(APPIMAGE_VERSION_PLACEHOLDER "Version Placeholder In AppImage Download URL" STRING "%VERSION%")
get_package_file_name(appimage_package_file_name "${MCPI_APPIMAGE_VERSION_PLACEHOLDER}")
mcpi_option(APPIMAGE_DOWNLOAD_URL "AppImage Download URL" STRING "${MCPI_REPO}/releases/download/${MCPI_APPIMAGE_VERSION_PLACEHOLDER}/${appimage_package_file_name}${MCPI_APPIMAGE_EXT}")
endif()

View File

@ -1,5 +1,5 @@
# Specify Installation Paths
set(MCPI_INSTALL_DIR "lib/${MCPI_VARIANT_NAME}")
set(MCPI_INSTALL_DIR "lib/${MCPI_APP_NAME}")
set(MCPI_BIN_DIR "${MCPI_INSTALL_DIR}/bin")
set(MCPI_LEGAL_DIR "${MCPI_INSTALL_DIR}/legal") # For Software Licenses
set(MCPI_SDK_DIR "${MCPI_INSTALL_DIR}/sdk")
@ -28,6 +28,6 @@ if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
elseif(MCPI_IS_FLATPAK_BUILD)
set(DEFAULT_PREFIX "/app")
endif()
set(CMAKE_INSTALL_PREFIX "${DEFAULT_PREFIX}" CACHE PATH "" FORCE)
set(CMAKE_INSTALL_PREFIX "${DEFAULT_PREFIX}" CACHE PATH "Install Prefix" FORCE)
set(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT FALSE)
endif()

View File

@ -1,18 +1,20 @@
# Target
set(target "arm-none-linux-gnueabihf")
# Pick Archive
set(toolchain_version "13.3.rel1")
execute_process(COMMAND uname -m OUTPUT_VARIABLE arch OUTPUT_STRIP_TRAILING_WHITESPACE)
if(arch STREQUAL "x86_64")
set(toolchain_file "arm-gnu-toolchain-${toolchain_version}-x86_64-arm-none-linux-gnueabihf.tar.xz")
set(toolchain_file "arm-gnu-toolchain-${toolchain_version}-x86_64-${target}.tar.xz")
elseif(arch STREQUAL "aarch64" OR arch STREQUAL "armv8b" OR arch STREQUAL "armv8l")
set(toolchain_file "arm-gnu-toolchain-${toolchain_version}-aarch64-arm-none-linux-gnueabihf.tar.xz")
set(toolchain_file "arm-gnu-toolchain-${toolchain_version}-aarch64-${target}.tar.xz")
else()
message(FATAL_ERROR "Unable To Download Prebuilt ARMHF Toolchain")
endif()
# Download If Needed
include(FetchContent)
FetchContent_Declare(
prebuilt-armhf-toolchain
FetchContent_Declare(prebuilt-armhf-toolchain
URL "${CMAKE_CURRENT_LIST_DIR}/../../archives/${toolchain_file}"
)
FetchContent_MakeAvailable(prebuilt-armhf-toolchain)
@ -20,53 +22,67 @@ set(toolchain_dir "${prebuilt-armhf-toolchain_SOURCE_DIR}")
# Force Toolchain
file(WRITE "${toolchain_dir}/toolchain.cmake"
"set(CMAKE_C_COMPILER \"\${CMAKE_CURRENT_LIST_DIR}/bin/arm-none-linux-gnueabihf-gcc\")\n"
"set(CMAKE_CXX_COMPILER \"\${CMAKE_CURRENT_LIST_DIR}/bin/arm-none-linux-gnueabihf-g++\")\n"
"set(CMAKE_C_COMPILER \"\${CMAKE_CURRENT_LIST_DIR}/bin/${target}-gcc\")\n"
"set(CMAKE_CXX_COMPILER \"\${CMAKE_CURRENT_LIST_DIR}/bin/${target}-g++\")\n"
"set(CMAKE_SYSTEM_NAME \"Linux\")\n"
"set(CMAKE_SYSTEM_PROCESSOR \"arm\")\n"
"set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)\n"
)
set(MCPI_CMAKE_TOOLCHAIN_FILE "${toolchain_dir}/toolchain.cmake" CACHE FILEPATH "" FORCE)
force_set(MCPI_CMAKE_TOOLCHAIN_FILE "${toolchain_dir}/toolchain.cmake" FILEPATH)
# Build Sysroot
set(sysroot_dir "${CMAKE_CURRENT_BINARY_DIR}/bundled-armhf-sysroot")
if("${toolchain_dir}/bin/arm-none-linux-gnueabihf-gcc" IS_NEWER_THAN "${sysroot_dir}")
set(sysroot_dir_debug "${sysroot_dir}/debug")
set(sysroot_dir_release "${sysroot_dir}/release")
if("${toolchain_dir}/bin/${target}-gcc" IS_NEWER_THAN "${sysroot_dir}")
# Create Directory
file(REMOVE_RECURSE "${sysroot_dir}")
file(MAKE_DIRECTORY "${sysroot_dir}")
file(MAKE_DIRECTORY "${sysroot_dir_debug}")
file(MAKE_DIRECTORY "${sysroot_dir_release}")
# Copy Files From Toolchain
file(
COPY "${toolchain_dir}/arm-none-linux-gnueabihf/libc/"
DESTINATION "${sysroot_dir}"
COPY "${toolchain_dir}/${target}/libc/"
DESTINATION "${sysroot_dir_debug}"
USE_SOURCE_PERMISSIONS
FILES_MATCHING
PATTERN "*.so*"
)
# Delete Unneeded Files
file(REMOVE_RECURSE "${sysroot_dir}/usr/lib/audit")
file(REMOVE_RECURSE "${sysroot_dir}/usr/lib/gconv")
file(REMOVE_RECURSE "${sysroot_dir_debug}/usr/lib/audit")
file(REMOVE_RECURSE "${sysroot_dir_debug}/usr/lib/gconv")
# Strip Files
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
file(GLOB_RECURSE files LIST_DIRECTORIES FALSE "${sysroot_dir}/*")
foreach(file IN LISTS files)
execute_process(COMMAND "${toolchain_dir}/bin/arm-none-linux-gnueabihf-strip" "${file}" RESULT_VARIABLE ret)
# Check Result
if(NOT ret EQUAL 0)
# Delete Invalid Files
file(REMOVE "${file}")
endif()
endforeach()
endif()
file(COPY "${sysroot_dir_debug}/." DESTINATION "${sysroot_dir_release}")
file(GLOB_RECURSE files LIST_DIRECTORIES FALSE "${sysroot_dir_release}/*")
foreach(file IN LISTS files)
execute_process(
COMMAND "${toolchain_dir}/bin/${target}-strip" "${file}"
RESULT_VARIABLE ret
ERROR_QUIET
)
# Delete Invalid Files
if(NOT ret EQUAL 0)
file(REMOVE "${file}")
endif()
endforeach()
endif()
# Install Sysroot (Skipping Empty Directories)
function(install_arm_sysroot)
file(GLOB_RECURSE files LIST_DIRECTORIES FALSE RELATIVE "${sysroot_dir}" "${sysroot_dir}/*")
function(install_arm_sysroot_config config)
set(dir "${sysroot_dir_${config}}")
file(GLOB_RECURSE files LIST_DIRECTORIES FALSE RELATIVE "${dir}" "${dir}/*")
foreach(file IN LISTS files)
get_filename_component(parent "${file}" DIRECTORY)
install(PROGRAMS "${sysroot_dir}/${file}" DESTINATION "${MCPI_INSTALL_DIR}/sysroot/${parent}")
cmake_path(GET file PARENT_PATH parent)
install(
PROGRAMS "${dir}/${file}"
DESTINATION "${MCPI_INSTALL_DIR}/sysroot/${parent}"
CONFIGURATIONS "${config}"
)
endforeach()
endfunction()
function(install_arm_sysroot)
install_arm_sysroot_config(debug)
install_arm_sysroot_config(release)
endfunction()

View File

@ -1,12 +1,12 @@
# Read Hex Data
file(READ "${EMBED_IN}" data HEX)
# Convert Hex Data For C Compatibility
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," data "${data}")
# Get C Name
get_filename_component(name "${EMBED_IN}" NAME)
string(MAKE_C_IDENTIFIER "${name}" name)
cmake_path(GET EMBED_OUT STEM name)
# Write Data
file(WRITE "${EMBED_OUT}" "#include <stddef.h>\nconst unsigned char ${name}[] = {${data}};\nconst size_t ${name}_len = sizeof (${name});\n")
file(WRITE "${EMBED_OUT}"
"#include <stddef.h>\n"
"const unsigned char ${name}[] = {${data}};\n"
"const size_t ${name}_len = sizeof (${name});\n"
)

View File

@ -1,6 +1,6 @@
# Symlink Function
function(install_symlink target link)
get_filename_component(parent "${link}" DIRECTORY)
cmake_path(GET link PARENT_PATH parent)
if(parent STREQUAL "")
set(parent ".")
endif()
@ -13,16 +13,20 @@ endfunction()
set(util_list_dir "${CMAKE_CURRENT_LIST_DIR}")
function(embed_resource target file)
# Get C Name
get_filename_component(name "${file}" NAME)
cmake_path(GET file FILENAME name)
string(MAKE_C_IDENTIFIER "${name}" name)
# Add Command
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}.c"
set(in "${CMAKE_CURRENT_SOURCE_DIR}/${file}")
set(out "${CMAKE_CURRENT_BINARY_DIR}/${name}.c")
set(script "${util_list_dir}/embed-resource.cmake")
add_custom_command(OUTPUT "${out}"
COMMAND "${CMAKE_COMMAND}"
ARGS "-DEMBED_IN=${CMAKE_CURRENT_SOURCE_DIR}/${file}" "-DEMBED_OUT=${CMAKE_CURRENT_BINARY_DIR}/${name}.c" "-P" "${util_list_dir}/embed-resource.cmake"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${file}" "${util_list_dir}/embed-resource.cmake"
ARGS "-DEMBED_IN=${in}" "-DEMBED_OUT=${out}" "-P" "${script}"
DEPENDS "${in}" "${script}"
VERBATIM
)
# Add To Target
target_sources("${target}" PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/${name}.c")
target_sources("${target}" PRIVATE "${out}")
endfunction()
# Nicer Output
@ -31,3 +35,57 @@ function(message log_level)
_message("${log_level}" ${ARGN})
endif()
endfunction()
# Exporting Targets And Headers
macro(_get_sdk_header_dir target)
set(sdk_dir "${MCPI_SDK_INCLUDE_DIR}/${target}")
endmacro()
function(setup_header_dirs target)
_get_sdk_header_dir("${target}")
# Get Header Type
set(header_type "PUBLIC")
get_target_property(type "${target}" TYPE)
if ("${type}" STREQUAL "INTERFACE_LIBRARY")
set(header_type "INTERFACE")
endif()
# Loop
foreach(dir IN LISTS ARGN)
# Add To Target
target_include_directories("${target}" "${header_type}" "$<BUILD_INTERFACE:${dir}>")
# Add To SDK
if(BUILD_ARM_COMPONENTS)
install(
DIRECTORY "${dir}/"
DESTINATION "${sdk_dir}"
FILES_MATCHING
PATTERN "*.h"
)
endif()
endforeach()
# Add SDK Headers To Target
if(BUILD_ARM_COMPONENTS)
target_include_directories("${target}" "${header_type}" "$<INSTALL_INTERFACE:${sdk_dir}>")
endif()
endfunction()
function(setup_library target should_install should_export)
# Install
if(should_install)
install(TARGETS "${target}" DESTINATION "${MCPI_LIB_DIR}")
endif()
# SDK
if(should_export AND BUILD_ARM_COMPONENTS)
install(TARGETS "${target}" EXPORT sdk DESTINATION "${MCPI_SDK_LIB_DIR}")
endif()
endfunction()
# Force Set Configuration Variable
function(force_set name value type)
set("${name}" "${value}" CACHE "${type}" "" FORCE)
mark_as_advanced(FORCE "${name}")
endfunction()
# Make Directory
function(set_and_mkdir name dir)
set("${name}" "${dir}" PARENT_SCOPE)
file(MAKE_DIRECTORY "${dir}")
endfunction()

View File

@ -8,10 +8,6 @@ endif()
if(BUILD_ARM_COMPONENTS AND NOT MCPI_OPEN_SOURCE_ONLY)
add_subdirectory(minecraft-pi)
endif()
# Zenity (Minimal Build)
if(BUILD_NATIVE_COMPONENTS)
add_subdirectory(zenity)
endif()
# LIEF
if(BUILD_NATIVE_COMPONENTS OR BUILD_MEDIA_LAYER_CORE)
add_subdirectory(LIEF)
@ -19,12 +15,16 @@ endif()
# Extra Runtime
add_subdirectory(runtime)
# GLFW
if(BUILD_MEDIA_LAYER_CORE)
if(BUILD_NATIVE_COMPONENTS OR BUILD_MEDIA_LAYER_CORE)
add_subdirectory(glfw)
endif()
# ImGui
if(BUILD_NATIVE_COMPONENTS)
add_subdirectory(imgui)
endif()
# UTF8-CPP
add_subdirectory(utf8cpp)
# Symbol Prcoessor
# Symbol Processor
if(BUILD_ARM_COMPONENTS)
add_subdirectory(symbol-processor)
endif()

View File

@ -6,33 +6,38 @@ add_compile_options(-w -Wno-psabi)
## LIEF
# Options
set(BUILD_SHARED_LIBS TRUE CACHE BOOL "" FORCE)
set(LIEF_C_API FALSE CACHE BOOL "" FORCE)
set(LIEF_EXAMPLES FALSE CACHE BOOL "" FORCE)
set(LIEF_PYTHON_API FALSE CACHE BOOL "" FORCE)
set(LIEF_TESTS FALSE CACHE BOOL "" FORCE)
set(LIEF_USE_CCACHE FALSE CACHE BOOL "" FORCE)
set(LIEF_LOGGING FALSE CACHE BOOL "" FORCE)
set(LIEF_LOGGING_DEBUG FALSE CACHE BOOL "" FORCE)
set(LIEF_ENABLE_JSON FALSE CACHE BOOL "" FORCE)
set(LIEF_ELF TRUE CACHE BOOL "" FORCE)
set(LIEF_PE FALSE CACHE BOOL "" FORCE)
set(LIEF_MACHO FALSE CACHE BOOL "" FORCE)
set(LIEF_DEX FALSE CACHE BOOL "" FORCE)
set(LIEF_ART FALSE CACHE BOOL "" FORCE)
set(LIEF_OAT FALSE CACHE BOOL "" FORCE)
set(LIEF_VDEX FALSE CACHE BOOL "" FORCE)
force_set(LIEF_C_API FALSE BOOL)
force_set(LIEF_EXAMPLES FALSE BOOL)
force_set(LIEF_PYTHON_API FALSE BOOL)
force_set(LIEF_TESTS FALSE BOOL)
force_set(LIEF_USE_CCACHE FALSE BOOL)
force_set(LIEF_LOGGING FALSE BOOL)
force_set(LIEF_LOGGING_DEBUG FALSE BOOL)
force_set(LIEF_ENABLE_JSON FALSE BOOL)
force_set(LIEF_ELF TRUE BOOL)
force_set(LIEF_PE FALSE BOOL)
force_set(LIEF_MACHO FALSE BOOL)
force_set(LIEF_DEX FALSE BOOL)
force_set(LIEF_ART FALSE BOOL)
force_set(LIEF_OAT FALSE BOOL)
force_set(LIEF_VDEX FALSE BOOL)
# Download
set(MESSAGE_QUIET TRUE)
add_subdirectory(src EXCLUDE_FROM_ALL)
add_subdirectory(src EXCLUDE_FROM_ALL SYSTEM)
unset(MESSAGE_QUIET)
# Install
install(TARGETS LIB_LIEF DESTINATION "${MCPI_LIB_DIR}")
if(BUILD_ARM_COMPONENTS)
install(TARGETS LIB_LIEF EXPORT sdk DESTINATION "${MCPI_SDK_LIB_DIR}")
endif()
setup_library(LIB_LIEF TRUE TRUE)
# License
install(FILES src/LICENSE DESTINATION "${MCPI_LEGAL_DIR}/LIEF")
# Fix Flags
function(fix_flags property)
get_target_property(flags LIB_LIEF "${property}")
list(REMOVE_ITEM flags "_GLIBCXX_USE_CXX11_ABI=1")
set_target_properties(LIB_LIEF PROPERTIES "${property}" "${flags}")
endfunction()
fix_flags(COMPILE_DEFINITIONS)
fix_flags(INTERFACE_COMPILE_DEFINITIONS)

@ -1 +1 @@
Subproject commit bae887e095d87e756d1bf4aa4f95a97693a97b62
Subproject commit d4900dab6a9eea864fb14ed1ff7ea5b9f8678e04

View File

@ -6,22 +6,21 @@ add_compile_options(-w)
## GLFW
# Download
set(BUILD_SHARED_LIBS TRUE CACHE BOOL "" FORCE)
set(GLFW_BUILD_EXAMPLES FALSE CACHE BOOL "" FORCE)
set(GLFW_BUILD_TESTS FALSE CACHE BOOL "" FORCE)
set(GLFW_BUILD_DOCS FALSE CACHE BOOL "" FORCE)
set(GLFW_INSTALL FALSE CACHE BOOL "" FORCE)
set(GLFW_BUILD_WIN32 FALSE CACHE BOOL "" FORCE)
set(GLFW_BUILD_COCOA FALSE CACHE BOOL "" FORCE)
set(GLFW_BUILD_X11 TRUE CACHE BOOL "" FORCE)
set(GLFW_BUILD_WAYLAND TRUE CACHE BOOL "" FORCE)
set(GLFW_LIBRARY_TYPE "SHARED" CACHE BOOL "" FORCE)
force_set(GLFW_BUILD_EXAMPLES FALSE BOOL)
force_set(GLFW_BUILD_TESTS FALSE BOOL)
force_set(GLFW_BUILD_DOCS FALSE BOOL)
force_set(GLFW_INSTALL FALSE BOOL)
force_set(GLFW_BUILD_WIN32 FALSE BOOL)
force_set(GLFW_BUILD_COCOA FALSE BOOL)
force_set(GLFW_BUILD_X11 TRUE BOOL)
force_set(GLFW_BUILD_WAYLAND TRUE BOOL)
force_set(GLFW_LIBRARY_TYPE "SHARED" STRING)
set(MESSAGE_QUIET TRUE)
add_subdirectory(src EXCLUDE_FROM_ALL)
add_subdirectory(src EXCLUDE_FROM_ALL SYSTEM)
unset(MESSAGE_QUIET)
# Install
install(TARGETS glfw DESTINATION "${MCPI_LIB_DIR}")
setup_library(glfw TRUE TRUE)
# License
install(FILES src/LICENSE.md DESTINATION "${MCPI_LEGAL_DIR}/glfw")
install(FILES src/LICENSE.md DESTINATION "${MCPI_LEGAL_DIR}/GLFW")

@ -1 +1 @@
Subproject commit 7b6aead9fb88b3623e3b3725ebb42670cbe4c579
Subproject commit 21fea01161e0d6b70c0c5c1f52dc8e7a7df14a50

52
dependencies/imgui/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,52 @@
project(imgui)
# Silence Warnings
add_compile_options(-w)
## ImGui
# Build
add_library(imgui SHARED
src/imgui.cpp
src/imgui_draw.cpp
src/imgui_tables.cpp
src/imgui_widgets.cpp
src/misc/cpp/imgui_stdlib.cpp
src/backends/imgui_impl_glfw.cpp
src/backends/imgui_impl_opengl2.cpp
)
setup_header_dirs(imgui
"${CMAKE_CURRENT_SOURCE_DIR}/src"
"${CMAKE_CURRENT_SOURCE_DIR}/src/backends"
"${CMAKE_CURRENT_SOURCE_DIR}/src/misc/cpp"
)
# OpenGL
add_subdirectory(glad)
target_link_libraries(imgui PUBLIC glfw glad)
# Fonts
embed_resource(imgui src/misc/fonts/Roboto-Medium.ttf)
embed_resource(imgui src/misc/fonts/Cousine-Regular.ttf)
# Configure
target_compile_definitions(imgui PUBLIC
IMGUI_DISABLE_DEMO_WINDOWS
IMGUI_DISABLE_DEBUG_TOOLS
IMGUI_DISABLE_DEFAULT_FONT
IMGUI_DISABLE_OBSOLETE_FUNCTIONS
)
# Patch
execute_process(
COMMAND "patch" "-p1" "--forward" "--reject-file=-"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/src"
INPUT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/fix-hidpi.patch"
OUTPUT_QUIET
)
# Install
setup_library(imgui TRUE FALSE)
# License
install(FILES src/LICENSE.txt src/docs/FONTS.md DESTINATION "${MCPI_LEGAL_DIR}/ImGui")

55
dependencies/imgui/fix-hidpi.patch vendored Normal file
View File

@ -0,0 +1,55 @@
--- a/backends/imgui_impl_glfw.cpp
+++ b/backends/imgui_impl_glfw.cpp
@@ -422,6 +422,21 @@ void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused)
io.AddFocusEvent(focused != 0);
}
+static void ImGui_ImplGlfw_ScaleMousePos(GLFWwindow* window, double &x, double &y) {
+ // Get Window Size
+ int window_width, window_height;
+ glfwGetWindowSize(window, &window_width, &window_height);
+ if (window_width <= 0 || window_height <= 0) {
+ return;
+ }
+ // Get Framebuffer Size
+ int framebuffer_width, framebuffer_height;
+ glfwGetFramebufferSize(window, &framebuffer_width, &framebuffer_height);
+ // Multiply
+ x *= double(framebuffer_width) / double(window_width);
+ y *= double(framebuffer_height) / double(window_height);
+}
+
void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y)
{
ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData();
@@ -429,6 +444,7 @@ void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y)
bd->PrevUserCallbackCursorPos(window, x, y);
ImGuiIO& io = ImGui::GetIO();
+ ImGui_ImplGlfw_ScaleMousePos(window, x, y);
io.AddMousePosEvent((float)x, (float)y);
bd->LastValidMousePos = ImVec2((float)x, (float)y);
}
@@ -738,6 +754,7 @@ static void ImGui_ImplGlfw_UpdateMouseData()
{
double mouse_x, mouse_y;
glfwGetCursorPos(window, &mouse_x, &mouse_y);
+ ImGui_ImplGlfw_ScaleMousePos(window, mouse_x, mouse_y);
bd->LastValidMousePos = ImVec2((float)mouse_x, (float)mouse_y);
io.AddMousePosEvent((float)mouse_x, (float)mouse_y);
}
@@ -831,13 +848,9 @@ void ImGui_ImplGlfw_NewFrame()
IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplGlfw_InitForXXX()?");
// Setup display size (every frame to accommodate for window resizing)
- int w, h;
int display_w, display_h;
- glfwGetWindowSize(bd->Window, &w, &h);
glfwGetFramebufferSize(bd->Window, &display_w, &display_h);
- io.DisplaySize = ImVec2((float)w, (float)h);
- if (w > 0 && h > 0)
- io.DisplayFramebufferScale = ImVec2((float)display_w / (float)w, (float)display_h / (float)h);
+ io.DisplaySize = ImVec2((float)display_w, (float)display_h);
// Setup time step
// (Accept glfwGetTime() not returning a monotonically increasing value. Seems to happens on disconnecting peripherals and probably on VMs and Emscripten, see #6491, #6189, #6114, #3644)

49
dependencies/imgui/glad/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,49 @@
project(imgui-glad)
# Directories
set_and_mkdir(GLAD_DIR "${CMAKE_CURRENT_BINARY_DIR}/glad")
set_and_mkdir(GLAD_SRC_DIR "${GLAD_DIR}/src")
set_and_mkdir(GLAD_INCLUDE_DIR "${GLAD_DIR}/include")
# Files
set(GLAD_SOURCES
"${GLAD_INCLUDE_DIR}/KHR/khrplatform.h"
"${GLAD_INCLUDE_DIR}/glad/glad.h"
"${GLAD_SRC_DIR}/glad.c"
)
# Find Python
find_package(Python REQUIRED QUIET)
# Generate
add_custom_command(
OUTPUT ${GLAD_SOURCES}
COMMAND "${Python_EXECUTABLE}"
ARGS "-m" "glad"
"--out" "${GLAD_DIR}"
"--api" "gl=1.1"
"--generator" "c"
"--reproducible"
"--quiet"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/src"
VERBATIM
)
# Build
add_library(glad SHARED ${GLAD_SOURCES})
target_compile_definitions(glad
PUBLIC GLAD_GLAPI_EXPORT
PRIVATE GLAD_GLAPI_EXPORT_BUILD
)
# Link
target_link_libraries(glad PRIVATE dl)
# Headers
setup_header_dirs(glad
"${GLAD_INCLUDE_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}/include"
)
# Install
setup_library(glad TRUE FALSE)

View File

@ -0,0 +1,2 @@
#pragma once
#include <glad/glad.h>

1
dependencies/imgui/glad/src vendored Submodule

@ -0,0 +1 @@
Subproject commit e86f90457371c6233053bacf0d6f486a51ddcd67

1
dependencies/imgui/src vendored Submodule

@ -0,0 +1 @@
Subproject commit 6982ce43f5b143c5dce5fab0ce07dd4867b705ae

View File

@ -5,11 +5,10 @@ include(FetchContent)
## Minecraft: Pi Edition
# Download
FetchContent_Declare(
minecraft-pi
FetchContent_Declare(minecraft-pi
URL "${CMAKE_CURRENT_SOURCE_DIR}/minecraft-pi-0.1.1.tar.gz"
)
FetchContent_Populate(minecraft-pi)
FetchContent_MakeAvailable(minecraft-pi)
# Install
install(

View File

@ -2,17 +2,11 @@ project(runtime)
## Extra Runtime
# QEMU
set(QEMU_VERSION "9.0.2")
set(RUNTIME_QEMU_ARCHIVE "${CMAKE_CURRENT_SOURCE_DIR}/../../archives/qemu-${QEMU_VERSION}.tar.xz")
if(NOT BUILD_NATIVE_COMPONENTS)
set(TRAMPOLINE_HEADERS_ONLY TRUE)
endif()
# Build
add_subdirectory(src)
# Install
if(COMMAND install_runtime)
install_runtime("${MCPI_BIN_DIR}" "${MCPI_LEGAL_DIR}")
# RPath
if(TARGET runtime)
set_target_properties(runtime PROPERTIES INSTALL_RPATH "$ORIGIN/../lib/native")
target_link_options(runtime PRIVATE "LINKER:--disable-new-dtags")
endif()

@ -1 +1 @@
Subproject commit 377f9ddbc4747ca3a640231d259c0e6fcc71b4b0
Subproject commit 84e37b572b55afb1eaa2ada1e37bc36de1584cfd

View File

@ -7,24 +7,12 @@ add_compile_options(-w)
# Build
add_library(stb_image SHARED src/stb_image_impl.c)
target_include_directories(
stb_image
PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:${MCPI_SDK_INCLUDE_DIR}/stb_image>"
)
target_link_libraries(stb_image PRIVATE m)
target_compile_definitions(stb_image PUBLIC STBI_ONLY_PNG)
setup_header_dirs(stb_image "${CMAKE_CURRENT_SOURCE_DIR}/include")
# Install
install(TARGETS stb_image DESTINATION "${MCPI_LIB_DIR}")
install(
DIRECTORY "include/"
DESTINATION "${MCPI_SDK_INCLUDE_DIR}/stb_image"
FILES_MATCHING
PATTERN "*.h"
)
install(TARGETS stb_image EXPORT sdk DESTINATION "${MCPI_SDK_LIB_DIR}")
setup_library(stb_image TRUE TRUE)
# License
install(FILES include/LICENSE DESTINATION "${MCPI_LEGAL_DIR}/stb_image")

@ -1 +1 @@
Subproject commit f72c4f0567c62897d74c734819c11705df0bf4ee
Subproject commit c803572e248998cc9d197f84661fea56bebf7346

View File

@ -9,4 +9,4 @@ add_compile_options(-w)
add_subdirectory(src EXCLUDE_FROM_ALL)
# License
install(FILES src/LICENSE DESTINATION "${MCPI_LEGAL_DIR}/utf8cpp")
install(FILES src/LICENSE DESTINATION "${MCPI_LEGAL_DIR}/UTF8-CPP")

View File

@ -1,20 +0,0 @@
project(zenity)
# Silence Warnings
add_compile_options(-w)
## Zenity
# Download
set(MESSAGE_QUIET TRUE)
add_subdirectory(src EXCLUDE_FROM_ALL)
unset(MESSAGE_QUIET)
# Ensure Build
add_custom_target(zenity-build ALL DEPENDS zenity)
# Install
install(TARGETS zenity DESTINATION "${MCPI_BIN_DIR}")
# License
install(FILES src/COPYING DESTINATION "${MCPI_LEGAL_DIR}/zenity")

@ -1 +0,0 @@
Subproject commit a7496461161c917878d58131711425e7c8e59436

View File

@ -51,7 +51,16 @@
* `Render Vignette` (Enabled By Default)
* `Increase Render Chunk Size` (Enabled By Default)
* `Proper Entity Shading` (Enabled By Default)
* `Implement RaspberryJuice API` (Enabled By Default)
* `Fix Sugar Position In Hand` (Enabled By Default)
* `Fix Reloading Textures On Resize` (Enabled By Default)
* `Improved UI Scaling` (Enabled By Default)
* `Text Rendering Fixes` (Enabled By Default)
* `Close Editor When Sign Is Destroyed` (Enabled By Default)
* `Remove Chest Placement Restrictions` (Enabled By Default)
* `Fix Hanging When No Valid Spawn Point Exists` (Enabled By Default)
* `Batch Font Rendering` (Enabled By Default)
* `Fix Furnace Screen Visual Bug` (Enabled By Default)
* `Fix Held Item Poking Through Screen Overlay` (Enabled By Default)
* Existing Functionality (All Enabled By Default)
* `Fix Screen Rendering When Hiding HUD`
* `Sanitize Usernames`
@ -59,8 +68,6 @@
* `Log RakNet Startup Errors`
* `Prevent Unnecessary Server Pinging`
* `Proper OpenGL Buffer Generation`
* `Fix Furnace Screen Visual Bug`
* `Fix Text Wrapping`
* `Fullscreen Support`
* `Always Save Chest Tile Entities`
* `Fix Transferring Durability When Using Items`
@ -70,14 +77,31 @@
* `Screenshot Support`
* `Fix Camera Functionality`
* `Property Scale Animated Textures`
* `Enable Text Input`
* `Update Default Options`
* `Fix options.txt Loading/Saving`
* `Extend Supported Keycodes`
* Split Up `Remove Creative Mode Restrictions` Feature Flag
* `Remove Creative Mode Restrictions` (Disabled By Default)
* `Display Slot Count In Creative Mode` (Disabled By Default)
* `Force Survival Mode Inventory UI` (Disabled By Default)
* `Force Survival Mode Inventory Behavior` (Disabled By Default)
* `Maximize Creative Mode Inventory Stack Size` (Disabled By Default)
* Rename `Disable Buggy Held Item Caching` Feature Flag To `Fix Held Item Caching`
* Rename `Disable 'gui_blocks' Atlas` Feature Flag To `Regenerate "gui_blocks" Atlas`
* Split Up `Miscellaneous Input Fixes` Feature Flag
* `Fix Escape Key Handling` (Enabled By Default)
* `Stop Locked Mouse From Interacting With HUD` (Enabled By Default)
* Rename Feature Flags
* `Disable Buggy Held Item Caching` To `Fix Held Item Caching`
* `Disable 'gui_blocks' Atlas` To `Regenerate "gui_blocks" Atlas`
* `Fix Sign Placement` To `Enable Sign Screen`
* `Force Touch GUI Inventory` To `Force Touch UI Inventory`
* `Full Touch GUI` To `Full Touch UI`
* `Force Touch GUI Button Behavior` To `Force Touch UI Button Behavior`
* `Remove Forced GUI Lag (Can Break Joining Servers)` To `Remove Forced UI Lag (Can Break Joining Servers)`
* `Hide Block Outline When GUI Is Hidden` To `Hide Block Outline When UI Is Hidden`
* `Fix Camera Functionality` To `Add Camera Functionality`
* `Fix Camera Rendering` To `Enable Camera Rendering`
* `Fix Camera Legs` To `Render Camera Legs`
* Add Milk Buckets
* Included In The `Add Buckets` Feature Flag
* Removed `Property Scale Animated Textures` Feature Flag
@ -89,6 +113,7 @@
* `overwrite_calls` Now Scans VTables
* Unify Server/Client Builds
* Controller Support Removed
* Brand New Launcher UI!
**2.5.3**
* Add `Replace Block Highlight With Outline` Feature Flag (Enabled By Default)

View File

@ -4,9 +4,8 @@
| [mhsjlw/mcpilauncher](https://github.com/mhsjlw/mcpilauncher/blob/master/trampoline/trampoline.c) | Information On Getting Minecraft: Pi Eiditon To Run On Desktop Linux |
| [Phirel's Survival Patch](https://www.minecraftforum.net/forums/minecraft-editions/minecraft-pi-edition/1960005-survival-mode-patch) | Information On Survival Mode Support |
| [zhuowei/MinecraftPEModWiki](https://github.com/zhuowei/MinecraftPEModWiki/wiki/How-some-unlocks-are-made) | Information On Smooth Lighting Support |
| [zhuowei/RaspberryJuice](https://github.com/zhuowei/RaspberryJuice) | Design Of RaspberryJuice Extended API |
| [Ghidra](https://ghidra-sre.org) | Used For Decompiling Minecraft: Pi Edition |
| [RetDec](https://retdec.com) | Used For Decompiling Minecraft: Pi Edition |
| [minecraft-linux/mcpelauncher-core](https://github.com/minecraft-linux/mcpelauncher-core/blob/6b5e17b5685a612143297ae4595bdd12327284f3/src/patch_utils.cpp#L42) | Original Function Overwrite Code |
| [Hooking C Functions at Runtime - Thomas Finch](http://thomasfinch.me/blog/2015/07/24/Hooking-C-Functions-At-Runtime.html) | Original Patching Code |
| [Bigjango](https://github.com/Bigjango13) | Misc programming contributions |
| [ReMinecraftPE](https://github.com/ReMinecraftPE/mcpe) | A Lot Of Decompiled Code |

View File

@ -27,7 +27,7 @@ The AppImage requires Debian Bullseye or higher. This is equivalent to Ubuntu 20
It also requires some additional packages. To install them, run:
```sh
sudo apt install -y libfuse2 libgtk-3-0 libopenal1 libglib2.0-0
sudo apt install -y libopenal1 libglib2.0-0
```
</details>

View File

@ -1,16 +0,0 @@
# Multiplayer
MCPI-Reborn supports two ways to play multiplayer.
## Local Network (LAN)
This is also supported by vanilla MCPI. Just load a world in MCPI and other devices on the network can join.
## External Servers
Unlike vanilla MCPI, MCPI-Reborn allows you to natively join a server outside of the local network. Just modify `~/.minecraft-pi/servers.txt` and it should show up in MCPI's server list.
### Example `~/.minecraft-pi/servers.txt`
```
# Default Port Is 19132
example.com
# Custom Port
example.com:19133
```

View File

@ -3,7 +3,6 @@
* [View Dedicated Server](DEDICATED_SERVER.md)
* [View Credits](CREDITS.md)
* [View Terminology](TERMINOLOGY.md)
* [View Multiplayer](MULTIPLAYER.md)
* [View In-Game Controls](CONTROLS.md)
* [View Custom Skins](CUSTOM_SKINS.md)
* [View Changelog](CHANGELOG.md)

View File

@ -1,6 +1,9 @@
// Headers
#include <libreborn/libreborn.h>
#include <libreborn/patch.h>
#include <libreborn/util/util.h>
#include <libreborn/util/string.h>
#include <symbols/minecraft.h>
#include <mods/chat/chat.h>
#include <mods/misc/misc.h>
#include <mods/server/server.h>
@ -17,7 +20,8 @@ HOOK(chat_handle_packet_send, void, (const Minecraft *minecraft, ChatPacket *pac
if (out.length() > 0 && out[out.length() - 1] == '\n') {
out[out.length() - 1] = '\0';
}
gui->addMessage(out);
std::string cp437_out = to_cp437(out);
gui->addMessage(cp437_out);
} else {
// Call Original Method
real_chat_handle_packet_send()(minecraft, packet);

View File

@ -1,628 +1,540 @@
// Headers
#include <libreborn/libreborn.h>
#include <libreborn/patch.h>
#include <symbols/minecraft.h>
#include <mods/misc/misc.h>
// The Actual Mod
static void Inventory_setupDefault_FillingContainer_addItem_call_injection(FillingContainer *filling_container) {
ItemInstance *fire_instance = new ItemInstance;
ALLOC_CHECK(fire_instance);
fire_instance->count = 255;
fire_instance->auxiliary = 0;
fire_instance->id = 51;
filling_container->addItem(fire_instance);
ItemInstance *mushroomStew_instance = new ItemInstance;
ALLOC_CHECK(mushroomStew_instance);
mushroomStew_instance->count = 255;
mushroomStew_instance->auxiliary = 0;
mushroomStew_instance->id = 282;
filling_container->addItem(mushroomStew_instance);
ItemInstance *steak_instance = new ItemInstance;
ALLOC_CHECK(steak_instance);
steak_instance->count = 255;
steak_instance->auxiliary = 0;
steak_instance->id = 364;
filling_container->addItem(steak_instance);
ItemInstance *cookedChicken_instance = new ItemInstance;
ALLOC_CHECK(cookedChicken_instance);
cookedChicken_instance->count = 255;
cookedChicken_instance->auxiliary = 0;
cookedChicken_instance->id = 366;
filling_container->addItem(cookedChicken_instance);
ItemInstance *porkCooked_instance = new ItemInstance;
ALLOC_CHECK(porkCooked_instance);
porkCooked_instance->count = 255;
porkCooked_instance->auxiliary = 0;
porkCooked_instance->id = 320;
filling_container->addItem(porkCooked_instance);
ItemInstance *apple_instance = new ItemInstance;
ALLOC_CHECK(apple_instance);
apple_instance->count = 255;
apple_instance->auxiliary = 0;
apple_instance->id = 260;
filling_container->addItem(apple_instance);
ItemInstance *tallGrass_instance = new ItemInstance;
ALLOC_CHECK(tallGrass_instance);
tallGrass_instance->count = 255;
tallGrass_instance->auxiliary = 0;
tallGrass_instance->id = 31;
filling_container->addItem(tallGrass_instance);
ItemInstance *crops_instance = new ItemInstance;
ALLOC_CHECK(crops_instance);
crops_instance->count = 255;
crops_instance->auxiliary = 0;
crops_instance->id = 59;
filling_container->addItem(crops_instance);
ItemInstance *farmland_instance = new ItemInstance;
ALLOC_CHECK(farmland_instance);
farmland_instance->count = 255;
farmland_instance->auxiliary = 0;
farmland_instance->id = 60;
filling_container->addItem(farmland_instance);
ItemInstance *activeFurnace_instance = new ItemInstance;
ALLOC_CHECK(activeFurnace_instance);
activeFurnace_instance->count = 255;
activeFurnace_instance->auxiliary = 0;
activeFurnace_instance->id = 62;
filling_container->addItem(activeFurnace_instance);
ItemInstance *ironDoor_instance = new ItemInstance;
ALLOC_CHECK(ironDoor_instance);
ironDoor_instance->count = 255;
ironDoor_instance->auxiliary = 0;
ironDoor_instance->id = 330;
filling_container->addItem(ironDoor_instance);
ItemInstance *activeRedstoneOre_instance = new ItemInstance;
ALLOC_CHECK(activeRedstoneOre_instance);
activeRedstoneOre_instance->count = 255;
activeRedstoneOre_instance->auxiliary = 0;
activeRedstoneOre_instance->id = 74;
filling_container->addItem(activeRedstoneOre_instance);
ItemInstance *pumkinStem_instance = new ItemInstance;
ALLOC_CHECK(pumkinStem_instance);
pumkinStem_instance->count = 255;
pumkinStem_instance->auxiliary = 0;
pumkinStem_instance->id = 105;
filling_container->addItem(pumkinStem_instance);
ItemInstance *newGrass_instance = new ItemInstance;
ALLOC_CHECK(newGrass_instance);
newGrass_instance->count = 255;
newGrass_instance->auxiliary = 0;
newGrass_instance->id = 253;
filling_container->addItem(newGrass_instance);
ItemInstance *reserved6_instance = new ItemInstance;
ALLOC_CHECK(reserved6_instance);
reserved6_instance->count = 255;
reserved6_instance->auxiliary = 0;
reserved6_instance->id = 1;
filling_container->addItem(reserved6_instance);
ItemInstance *doubleStoneSlab_instance = new ItemInstance;
ALLOC_CHECK(doubleStoneSlab_instance);
doubleStoneSlab_instance->count = 255;
doubleStoneSlab_instance->auxiliary = 0;
doubleStoneSlab_instance->id = 43;
filling_container->addItem(doubleStoneSlab_instance);
ItemInstance *arrow_instance = new ItemInstance;
ALLOC_CHECK(arrow_instance);
arrow_instance->count = 255;
arrow_instance->auxiliary = 0;
arrow_instance->id = 262;
filling_container->addItem(arrow_instance);
ItemInstance *coal_instance = new ItemInstance;
ALLOC_CHECK(coal_instance);
coal_instance->count = 255;
coal_instance->auxiliary = 0;
coal_instance->id = 263;
filling_container->addItem(coal_instance);
ItemInstance *diamond_instance = new ItemInstance;
ALLOC_CHECK(diamond_instance);
diamond_instance->count = 255;
diamond_instance->auxiliary = 0;
diamond_instance->id = 264;
filling_container->addItem(diamond_instance);
ItemInstance *ironIngot_instance = new ItemInstance;
ALLOC_CHECK(ironIngot_instance);
ironIngot_instance->count = 255;
ironIngot_instance->auxiliary = 0;
ironIngot_instance->id = 265;
filling_container->addItem(ironIngot_instance);
ItemInstance *goldIngot_instance = new ItemInstance;
ALLOC_CHECK(goldIngot_instance);
goldIngot_instance->count = 255;
goldIngot_instance->auxiliary = 0;
goldIngot_instance->id = 266;
filling_container->addItem(goldIngot_instance);
ItemInstance *woodSword_instance = new ItemInstance;
ALLOC_CHECK(woodSword_instance);
woodSword_instance->count = 255;
woodSword_instance->auxiliary = 0;
woodSword_instance->id = 268;
filling_container->addItem(woodSword_instance);
ItemInstance *woodShovel_instance = new ItemInstance;
ALLOC_CHECK(woodShovel_instance);
woodShovel_instance->count = 255;
woodShovel_instance->auxiliary = 0;
woodShovel_instance->id = 269;
filling_container->addItem(woodShovel_instance);
ItemInstance *woodPickaxe_instance = new ItemInstance;
ALLOC_CHECK(woodPickaxe_instance);
woodPickaxe_instance->count = 255;
woodPickaxe_instance->auxiliary = 0;
woodPickaxe_instance->id = 270;
filling_container->addItem(woodPickaxe_instance);
ItemInstance *woodAxe_instance = new ItemInstance;
ALLOC_CHECK(woodAxe_instance);
woodAxe_instance->count = 255;
woodAxe_instance->auxiliary = 0;
woodAxe_instance->id = 271;
filling_container->addItem(woodAxe_instance);
ItemInstance *stoneSword_instance = new ItemInstance;
ALLOC_CHECK(stoneSword_instance);
stoneSword_instance->count = 255;
stoneSword_instance->auxiliary = 0;
stoneSword_instance->id = 272;
filling_container->addItem(stoneSword_instance);
ItemInstance *stoneShovel_instance = new ItemInstance;
ALLOC_CHECK(stoneShovel_instance);
stoneShovel_instance->count = 255;
stoneShovel_instance->auxiliary = 0;
stoneShovel_instance->id = 273;
filling_container->addItem(stoneShovel_instance);
ItemInstance *stonePickaxe_instance = new ItemInstance;
ALLOC_CHECK(stonePickaxe_instance);
stonePickaxe_instance->count = 255;
stonePickaxe_instance->auxiliary = 0;
stonePickaxe_instance->id = 274;
filling_container->addItem(stonePickaxe_instance);
ItemInstance *stoneAxe_instance = new ItemInstance;
ALLOC_CHECK(stoneAxe_instance);
stoneAxe_instance->count = 255;
stoneAxe_instance->auxiliary = 0;
stoneAxe_instance->id = 275;
filling_container->addItem(stoneAxe_instance);
ItemInstance *shovelIron_instance = new ItemInstance;
ALLOC_CHECK(shovelIron_instance);
shovelIron_instance->count = 255;
shovelIron_instance->auxiliary = 0;
shovelIron_instance->id = 256;
filling_container->addItem(shovelIron_instance);
ItemInstance *ironPick_instance = new ItemInstance;
ALLOC_CHECK(ironPick_instance);
ironPick_instance->count = 255;
ironPick_instance->auxiliary = 0;
ironPick_instance->id = 257;
filling_container->addItem(ironPick_instance);
ItemInstance *ironAxe_instance = new ItemInstance;
ALLOC_CHECK(ironAxe_instance);
ironAxe_instance->count = 255;
ironAxe_instance->auxiliary = 0;
ironAxe_instance->id = 258;
filling_container->addItem(ironAxe_instance);
ItemInstance *diamondSword_instance = new ItemInstance;
ALLOC_CHECK(diamondSword_instance);
diamondSword_instance->count = 255;
diamondSword_instance->auxiliary = 0;
diamondSword_instance->id = 276;
filling_container->addItem(diamondSword_instance);
ItemInstance *diamondShovel_instance = new ItemInstance;
ALLOC_CHECK(diamondShovel_instance);
diamondShovel_instance->count = 255;
diamondShovel_instance->auxiliary = 0;
diamondShovel_instance->id = 277;
filling_container->addItem(diamondShovel_instance);
ItemInstance *diamondPickaxe_instance = new ItemInstance;
ALLOC_CHECK(diamondPickaxe_instance);
diamondPickaxe_instance->count = 255;
diamondPickaxe_instance->auxiliary = 0;
diamondPickaxe_instance->id = 278;
filling_container->addItem(diamondPickaxe_instance);
ItemInstance *diamondAxe_instance = new ItemInstance;
ALLOC_CHECK(diamondAxe_instance);
diamondAxe_instance->count = 255;
diamondAxe_instance->auxiliary = 0;
diamondAxe_instance->id = 279;
filling_container->addItem(diamondAxe_instance);
ItemInstance *magicWand_instance = new ItemInstance;
ALLOC_CHECK(magicWand_instance);
magicWand_instance->count = 255;
magicWand_instance->auxiliary = 0;
magicWand_instance->id = 280;
filling_container->addItem(magicWand_instance);
ItemInstance *bowl_instance = new ItemInstance;
ALLOC_CHECK(bowl_instance);
bowl_instance->count = 255;
bowl_instance->auxiliary = 0;
bowl_instance->id = 281;
filling_container->addItem(bowl_instance);
ItemInstance *goldSword_instance = new ItemInstance;
ALLOC_CHECK(goldSword_instance);
goldSword_instance->count = 255;
goldSword_instance->auxiliary = 0;
goldSword_instance->id = 283;
filling_container->addItem(goldSword_instance);
ItemInstance *goldShovel_instance = new ItemInstance;
ALLOC_CHECK(goldShovel_instance);
goldShovel_instance->count = 255;
goldShovel_instance->auxiliary = 0;
goldShovel_instance->id = 284;
filling_container->addItem(goldShovel_instance);
ItemInstance *goldPickaxe_instance = new ItemInstance;
ALLOC_CHECK(goldPickaxe_instance);
goldPickaxe_instance->count = 255;
goldPickaxe_instance->auxiliary = 0;
goldPickaxe_instance->id = 285;
filling_container->addItem(goldPickaxe_instance);
ItemInstance *goldAxe_instance = new ItemInstance;
ALLOC_CHECK(goldAxe_instance);
goldAxe_instance->count = 255;
goldAxe_instance->auxiliary = 0;
goldAxe_instance->id = 286;
filling_container->addItem(goldAxe_instance);
ItemInstance *string_instance = new ItemInstance;
ALLOC_CHECK(string_instance);
string_instance->count = 255;
string_instance->auxiliary = 0;
string_instance->id = 287;
filling_container->addItem(string_instance);
ItemInstance *feather_instance = new ItemInstance;
ALLOC_CHECK(feather_instance);
feather_instance->count = 255;
feather_instance->auxiliary = 0;
feather_instance->id = 288;
filling_container->addItem(feather_instance);
ItemInstance *gunpowder_instance = new ItemInstance;
ALLOC_CHECK(gunpowder_instance);
gunpowder_instance->count = 255;
gunpowder_instance->auxiliary = 0;
gunpowder_instance->id = 289;
filling_container->addItem(gunpowder_instance);
ItemInstance *woodHoe_instance = new ItemInstance;
ALLOC_CHECK(woodHoe_instance);
woodHoe_instance->count = 255;
woodHoe_instance->auxiliary = 0;
woodHoe_instance->id = 290;
filling_container->addItem(woodHoe_instance);
ItemInstance *stoneHoe_instance = new ItemInstance;
ALLOC_CHECK(stoneHoe_instance);
stoneHoe_instance->count = 255;
stoneHoe_instance->auxiliary = 0;
stoneHoe_instance->id = 291;
filling_container->addItem(stoneHoe_instance);
ItemInstance *flint1_instance = new ItemInstance;
ALLOC_CHECK(flint1_instance);
flint1_instance->count = 255;
flint1_instance->auxiliary = 0;
flint1_instance->id = 292;
filling_container->addItem(flint1_instance);
ItemInstance *diamondHoe_instance = new ItemInstance;
ALLOC_CHECK(diamondHoe_instance);
diamondHoe_instance->count = 255;
diamondHoe_instance->auxiliary = 0;
diamondHoe_instance->id = 293;
filling_container->addItem(diamondHoe_instance);
ItemInstance *goldHoe_instance = new ItemInstance;
ALLOC_CHECK(goldHoe_instance);
goldHoe_instance->count = 255;
goldHoe_instance->auxiliary = 0;
goldHoe_instance->id = 294;
filling_container->addItem(goldHoe_instance);
ItemInstance *seeds_instance = new ItemInstance;
ALLOC_CHECK(seeds_instance);
seeds_instance->count = 255;
seeds_instance->auxiliary = 0;
seeds_instance->id = 295;
filling_container->addItem(seeds_instance);
ItemInstance *wheat_instance = new ItemInstance;
ALLOC_CHECK(wheat_instance);
wheat_instance->count = 255;
wheat_instance->auxiliary = 0;
wheat_instance->id = 296;
filling_container->addItem(wheat_instance);
ItemInstance *bread_instance = new ItemInstance;
ALLOC_CHECK(bread_instance);
bread_instance->count = 255;
bread_instance->auxiliary = 0;
bread_instance->id = 297;
filling_container->addItem(bread_instance);
ItemInstance *diamondHelm_instance = new ItemInstance;
ALLOC_CHECK(diamondHelm_instance);
diamondHelm_instance->count = 255;
diamondHelm_instance->auxiliary = 0;
diamondHelm_instance->id = 310;
filling_container->addItem(diamondHelm_instance);
ItemInstance *diamondChest_instance = new ItemInstance;
ALLOC_CHECK(diamondChest_instance);
diamondChest_instance->count = 255;
diamondChest_instance->auxiliary = 0;
diamondChest_instance->id = 311;
filling_container->addItem(diamondChest_instance);
ItemInstance *diamondLeg_instance = new ItemInstance;
ALLOC_CHECK(diamondLeg_instance);
diamondLeg_instance->count = 255;
diamondLeg_instance->auxiliary = 0;
diamondLeg_instance->id = 312;
filling_container->addItem(diamondLeg_instance);
ItemInstance *diamondBoot_instance = new ItemInstance;
ALLOC_CHECK(diamondBoot_instance);
diamondBoot_instance->count = 255;
diamondBoot_instance->auxiliary = 0;
diamondBoot_instance->id = 313;
filling_container->addItem(diamondBoot_instance);
ItemInstance *leatherCap_instance = new ItemInstance;
ALLOC_CHECK(leatherCap_instance);
leatherCap_instance->count = 255;
leatherCap_instance->auxiliary = 0;
leatherCap_instance->id = 298;
filling_container->addItem(leatherCap_instance);
ItemInstance *leatherShirt_instance = new ItemInstance;
ALLOC_CHECK(leatherShirt_instance);
leatherShirt_instance->count = 255;
leatherShirt_instance->auxiliary = 0;
leatherShirt_instance->id = 299;
filling_container->addItem(leatherShirt_instance);
ItemInstance *leatherPants_instance = new ItemInstance;
ALLOC_CHECK(leatherPants_instance);
leatherPants_instance->count = 255;
leatherPants_instance->auxiliary = 0;
leatherPants_instance->id = 300;
filling_container->addItem(leatherPants_instance);
ItemInstance *leatherBoots_instance = new ItemInstance;
ALLOC_CHECK(leatherBoots_instance);
leatherBoots_instance->count = 255;
leatherBoots_instance->auxiliary = 0;
leatherBoots_instance->id = 301;
filling_container->addItem(leatherBoots_instance);
ItemInstance *chainHelm_instance = new ItemInstance;
ALLOC_CHECK(chainHelm_instance);
chainHelm_instance->count = 255;
chainHelm_instance->auxiliary = 0;
chainHelm_instance->id = 302;
filling_container->addItem(chainHelm_instance);
ItemInstance *chainShirt_instance = new ItemInstance;
ALLOC_CHECK(chainShirt_instance);
chainShirt_instance->count = 255;
chainShirt_instance->auxiliary = 0;
chainShirt_instance->id = 303;
filling_container->addItem(chainShirt_instance);
ItemInstance *chainLegs_instance = new ItemInstance;
ALLOC_CHECK(chainLegs_instance);
chainLegs_instance->count = 255;
chainLegs_instance->auxiliary = 0;
chainLegs_instance->id = 304;
filling_container->addItem(chainLegs_instance);
ItemInstance *chainBoots_instance = new ItemInstance;
ALLOC_CHECK(chainBoots_instance);
chainBoots_instance->count = 255;
chainBoots_instance->auxiliary = 0;
chainBoots_instance->id = 305;
filling_container->addItem(chainBoots_instance);
ItemInstance *goldHelm_instance = new ItemInstance;
ALLOC_CHECK(goldHelm_instance);
goldHelm_instance->count = 255;
goldHelm_instance->auxiliary = 0;
goldHelm_instance->id = 314;
filling_container->addItem(goldHelm_instance);
ItemInstance *goldChest_instance = new ItemInstance;
ALLOC_CHECK(goldChest_instance);
goldChest_instance->count = 255;
goldChest_instance->auxiliary = 0;
goldChest_instance->id = 315;
filling_container->addItem(goldChest_instance);
ItemInstance *goldLegs_instance = new ItemInstance;
ALLOC_CHECK(goldLegs_instance);
goldLegs_instance->count = 255;
goldLegs_instance->auxiliary = 0;
goldLegs_instance->id = 316;
filling_container->addItem(goldLegs_instance);
ItemInstance *goldBoots_instance = new ItemInstance;
ALLOC_CHECK(goldBoots_instance);
goldBoots_instance->count = 255;
goldBoots_instance->auxiliary = 0;
goldBoots_instance->id = 317;
filling_container->addItem(goldBoots_instance);
ItemInstance *ironHelm_instance = new ItemInstance;
ALLOC_CHECK(ironHelm_instance);
ironHelm_instance->count = 255;
ironHelm_instance->auxiliary = 0;
ironHelm_instance->id = 306;
filling_container->addItem(ironHelm_instance);
ItemInstance *ironChest_instance = new ItemInstance;
ALLOC_CHECK(ironChest_instance);
ironChest_instance->count = 255;
ironChest_instance->auxiliary = 0;
ironChest_instance->id = 307;
filling_container->addItem(ironChest_instance);
ItemInstance *ironLegs_instance = new ItemInstance;
ALLOC_CHECK(ironLegs_instance);
ironLegs_instance->count = 255;
ironLegs_instance->auxiliary = 0;
ironLegs_instance->id = 308;
filling_container->addItem(ironLegs_instance);
ItemInstance *ironBoots_instance = new ItemInstance;
ALLOC_CHECK(ironBoots_instance);
ironBoots_instance->count = 255;
ironBoots_instance->auxiliary = 0;
ironBoots_instance->id = 309;
filling_container->addItem(ironBoots_instance);
ItemInstance *flint2_instance = new ItemInstance;
ALLOC_CHECK(flint2_instance);
flint2_instance->count = 255;
flint2_instance->auxiliary = 0;
flint2_instance->id = 318;
filling_container->addItem(flint2_instance);
ItemInstance *porkRaw_instance = new ItemInstance;
ALLOC_CHECK(porkRaw_instance);
porkRaw_instance->count = 255;
porkRaw_instance->auxiliary = 0;
porkRaw_instance->id = 319;
filling_container->addItem(porkRaw_instance);
ItemInstance *leather_instance = new ItemInstance;
ALLOC_CHECK(leather_instance);
leather_instance->count = 255;
leather_instance->auxiliary = 0;
leather_instance->id = 334;
filling_container->addItem(leather_instance);
ItemInstance *clayBrick_instance = new ItemInstance;
ALLOC_CHECK(clayBrick_instance);
clayBrick_instance->count = 255;
clayBrick_instance->auxiliary = 0;
clayBrick_instance->id = 336;
filling_container->addItem(clayBrick_instance);
ItemInstance *clay_instance = new ItemInstance;
ALLOC_CHECK(clay_instance);
clay_instance->count = 255;
clay_instance->auxiliary = 0;
clay_instance->id = 337;
filling_container->addItem(clay_instance);
ItemInstance *notepad_instance = new ItemInstance;
ALLOC_CHECK(notepad_instance);
notepad_instance->count = 255;
notepad_instance->auxiliary = 0;
notepad_instance->id = 339;
filling_container->addItem(notepad_instance);
ItemInstance *book_instance = new ItemInstance;
ALLOC_CHECK(book_instance);
book_instance->count = 255;
book_instance->auxiliary = 0;
book_instance->id = 340;
filling_container->addItem(book_instance);
ItemInstance *slimeball_instance = new ItemInstance;
ALLOC_CHECK(slimeball_instance);
slimeball_instance->count = 255;
slimeball_instance->auxiliary = 0;
slimeball_instance->id = 341;
filling_container->addItem(slimeball_instance);
ItemInstance *compass_instance = new ItemInstance;
ALLOC_CHECK(compass_instance);
compass_instance->count = 255;
compass_instance->auxiliary = 0;
compass_instance->id = 345;
filling_container->addItem(compass_instance);
ItemInstance *clock_instance = new ItemInstance;
ALLOC_CHECK(clock_instance);
clock_instance->count = 255;
clock_instance->auxiliary = 0;
clock_instance->id = 347;
filling_container->addItem(clock_instance);
ItemInstance *glowDust_instance = new ItemInstance;
ALLOC_CHECK(glowDust_instance);
glowDust_instance->count = 255;
glowDust_instance->auxiliary = 0;
glowDust_instance->id = 348;
filling_container->addItem(glowDust_instance);
ItemInstance *bone_instance = new ItemInstance;
ALLOC_CHECK(bone_instance);
bone_instance->count = 255;
bone_instance->auxiliary = 0;
bone_instance->id = 352;
filling_container->addItem(bone_instance);
ItemInstance *sugar_instance = new ItemInstance;
ALLOC_CHECK(sugar_instance);
sugar_instance->count = 255;
sugar_instance->auxiliary = 0;
sugar_instance->id = 353;
filling_container->addItem(sugar_instance);
ItemInstance *melon_instance = new ItemInstance;
ALLOC_CHECK(melon_instance);
melon_instance->count = 255;
melon_instance->auxiliary = 0;
melon_instance->id = 360;
filling_container->addItem(melon_instance);
ItemInstance *beefRaw_instance = new ItemInstance;
ALLOC_CHECK(beefRaw_instance);
beefRaw_instance->count = 255;
beefRaw_instance->auxiliary = 0;
beefRaw_instance->id = 363;
filling_container->addItem(beefRaw_instance);
ItemInstance *chickenRaw_instance = new ItemInstance;
ALLOC_CHECK(chickenRaw_instance);
chickenRaw_instance->count = 255;
chickenRaw_instance->auxiliary = 0;
chickenRaw_instance->id = 365;

View File

@ -1,6 +1,7 @@
// Headers
#include <libreborn/libreborn.h>
#include <libreborn/patch.h>
#include <symbols/minecraft.h>
#include <mods/misc/misc.h>
// Custom Crafting Recipes

View File

@ -19,7 +19,7 @@ install(
DESTINATION "${MCPI_INSTALL_DIR}/data/images/item"
)
install(
FILES "mojang/shadow.png" "mojang/vignette.png"
FILES "mojang/shadow.png" "mojang/vignette.png" "mojang/grasscolor.png"
DESTINATION "${MCPI_INSTALL_DIR}/data/images/misc"
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 133 KiB

View File

@ -2,27 +2,41 @@ project(launcher)
# Launcher
add_executable(launcher
src/bootstrap.cpp
src/patchelf.cpp
src/util.cpp
src/crash-report.cpp
src/sdk.cpp
src/mods.cpp
src/bootstrap/bootstrap.cpp
src/bootstrap/mods.cpp
src/bootstrap/assets.cpp
src/bootstrap/patchelf.cpp
src/bootstrap/debug.cpp
src/util/util.cpp
src/util/sdk.cpp
src/util/env.cpp
src/logger/logger.cpp
src/logger/crash-report.cpp
src/options/parser.cpp
src/main.cpp
src/ui/frame.cpp
src/ui/color.cpp
src/client/configuration.cpp
src/client/cache.cpp
src/client/available-feature-flags # Show In IDE
src/client/ui.cpp
src/updater/updater.cpp
)
target_link_libraries(launcher
reborn-util
LIB_LIEF
imgui
trampoline-headers
pthread
)
embed_resource(launcher src/client/available-feature-flags)
target_link_libraries(launcher reborn-util LIB_LIEF trampoline-headers)
# RPath
set_target_properties(launcher PROPERTIES INSTALL_RPATH "$ORIGIN/lib/native")
target_link_options(launcher PRIVATE "LINKER:--disable-new-dtags")
# Files
target_compile_definitions(launcher PRIVATE _FILE_OFFSET_BITS=64)
# Install
install(TARGETS launcher DESTINATION "${MCPI_INSTALL_DIR}")
install_symlink("../${MCPI_INSTALL_DIR}/launcher" "bin/${MCPI_VARIANT_NAME}")
install_symlink("../${MCPI_INSTALL_DIR}/launcher" "bin/${MCPI_APP_NAME}")
# Install Desktop Entry
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/launcher.desktop"
@ -30,11 +44,9 @@ file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/launcher.desktop"
"Name=${MCPI_APP_TITLE}\n"
"Comment=Fun with Blocks\n"
"Icon=${MCPI_APP_ID}\n"
"Exec=${MCPI_VARIANT_NAME}\n"
"Exec=${MCPI_APP_NAME}\n"
"Type=Application\n"
"Categories=Game;\n"
)
file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/launcher.desktop"
"Terminal=false\n"
"StartupNotify=false\n"
"StartupWMClass=${MCPI_APP_ID}\n"
@ -57,13 +69,13 @@ file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/appstream.xml"
" <p>Minecraft: Pi Edition Modding Project.</p>\n"
" <p>NOTE: This is not verified by, affiliated with, or supported by Mojang or Microsoft.</p>\n"
" </description>\n"
" <url type=\"homepage\">https://gitea.thebrokenrail.com/TheBrokenRail/minecraft-pi-reborn</url>\n"
" <url type=\"homepage\">${MCPI_REPO}</url>\n"
" <launchable type=\"desktop-id\">${MCPI_APP_ID}.desktop</launchable>\n"
" <provides>\n"
" <id>com.thebrokenrail.MCPIRebornClient.desktop</id>\n"
" <id>${MCPI_APP_ID}.desktop</id>\n"
" </provides>\n"
" <project_license>LicenseRef-proprietary</project_license>\n"
" <developer_name>TheBrokenRail &amp; Mojang AB</developer_name>\n"
" <developer_name>${MCPI_AUTHOR} &amp; Mojang AB</developer_name>\n"
" <content_rating type=\"oars-1.0\">\n"
" <content_attribute id=\"violence-cartoon\">moderate</content_attribute>\n"
" <content_attribute id=\"violence-fantasy\">none</content_attribute>\n"
@ -91,7 +103,7 @@ file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/appstream.xml"
" </releases>\n"
" <screenshots>\n"
" <screenshot type=\"default\">\n"
" <image>https://gitea.thebrokenrail.com/TheBrokenRail/minecraft-pi-reborn/raw/branch/master/images/start.png</image>\n"
" <image>${MCPI_REPO}/raw/branch/master/images/start.png</image>\n"
" </screenshot>\n"
" </screenshots>\n"
"</component>\n"
@ -104,6 +116,8 @@ install(
# AppImage
if(MCPI_IS_APPIMAGE_BUILD)
install_symlink("bin/${MCPI_VARIANT_NAME}" "AppRun")
install_symlink("bin/${MCPI_APP_NAME}" "AppRun")
install_symlink("${MCPI_SHARE_DIR}/applications/${MCPI_APP_ID}.desktop" "${MCPI_APP_ID}.desktop")
# Updater
target_sources(launcher PRIVATE src/updater/appimage.cpp)
endif()

View File

@ -1,196 +0,0 @@
#define _FILE_OFFSET_BITS 64
#include <string>
#include <vector>
#include <libreborn/libreborn.h>
#include <trampoline/types.h>
#include "util.h"
#include "bootstrap.h"
#include "patchelf.h"
#define MCPI_BINARY "minecraft-pi"
#define REQUIRED_PAGE_SIZE 4096
// Debug Information
static void run_debug_command(const char *const command[], const char *prefix) {
int status = 0;
char *output = run_command(command, &status, nullptr);
if (output != nullptr) {
// Remove Newline
size_t length = strlen(output);
if (length > 0 && output[length - 1] == '\n') {
output[length - 1] = '\0';
}
// Print
DEBUG("%s: %s", prefix, output);
free(output);
}
if (!is_exit_status_success(status)) {
ERR("Unable To Gather Debug Information");
}
}
static void print_debug_information() {
// System Information
const char *const command[] = {"uname", "-a", nullptr};
run_debug_command(command, "System Information");
// Version
DEBUG("Reborn Version: v%s", MCPI_VERSION);
// Architecture
const char *arch =
#ifdef __x86_64__
"AMD64"
#elif defined(__aarch64__)
"ARM64"
#elif defined(__arm__)
"ARM32"
#else
"Unknown"
#endif
;
DEBUG("Reborn Target Architecture: %s", arch);
}
// Bootstrap
void bootstrap(const options_t &options) {
// Debug Information
print_debug_information();
// Check Page Size (Not Needed When Using QEMU)
long page_size = sysconf(_SC_PAGESIZE);
if (page_size != REQUIRED_PAGE_SIZE) {
CONDITIONAL_ERR(!options.skip_pagesize_check, "Invalid page size! A page size of %ld bytes is required, but the system size is %ld bytes.", (long) REQUIRED_PAGE_SIZE, page_size);
}
// Get Binary Directory
const std::string binary_directory = get_binary_directory();
DEBUG("Binary Directory: %s", binary_directory.c_str());
// Copy SDK
if (!reborn_is_server()) {
copy_sdk(binary_directory, true);
}
// Set MCPI_REBORN_ASSETS_PATH
{
std::string assets_path = safe_realpath("/proc/self/exe");
chop_last_component(assets_path);
assets_path += "/data";
set_and_print_env(_MCPI_REBORN_ASSETS_PATH_ENV, assets_path.c_str());
}
// Resolve Binary Path & Set MCPI_DIRECTORY
std::string original_game_binary;
std::string game_binary;
{
// Log
DEBUG("Resolving File Paths...");
// Resolve Full Binary Path
const std::string full_path = binary_directory + ("/" MCPI_BINARY);
original_game_binary = safe_realpath(full_path);
const char *custom_binary = getenv(MCPI_BINARY_ENV);
if (custom_binary != nullptr) {
game_binary = safe_realpath(custom_binary);
} else {
game_binary = original_game_binary;
}
}
// Configure Preloaded Objects
std::vector<std::string> mcpi_ld_preload;
{
// Log
DEBUG("Locating Mods...");
// ARM Components
mcpi_ld_preload = bootstrap_mods(binary_directory);
}
// Configure Library Search Path
std::vector<std::string> mcpi_ld_path;
{
// Log
DEBUG("Setting Linker Search Paths...");
// Library Search Path For ARM Components
{
// Add ARM Library Directory
mcpi_ld_path.push_back("lib/arm");
// Add ARM Sysroot Libraries (Ensure Priority) (Ignore On Actual ARM System)
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
mcpi_ld_path.push_back("sysroot/lib");
mcpi_ld_path.push_back("sysroot/lib/arm-linux-gnueabihf");
mcpi_ld_path.push_back("sysroot/usr/lib");
mcpi_ld_path.push_back("sysroot/usr/lib/arm-linux-gnueabihf");
#endif
// Fix Paths
for (std::string &path : mcpi_ld_path) {
path = binary_directory + '/' + path;
}
}
}
// Fix MCPI Dependencies
char new_mcpi_exe_path[] = MCPI_PATCHED_DIR "/XXXXXX";
{
// Log
DEBUG("Patching ELF...");
// Find Linker
std::string linker = "/lib/ld-linux-armhf.so.3";
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
// Use ARM Sysroot Linker
linker = binary_directory + "/sysroot" + linker;
#endif
// Patch
patch_mcpi_elf_dependencies(game_binary, new_mcpi_exe_path, linker, mcpi_ld_path, mcpi_ld_preload);
// Verify
if (!starts_with(new_mcpi_exe_path, MCPI_PATCHED_DIR)) {
IMPOSSIBLE();
}
}
// Set MCPI_VANILLA_ASSETS_PATH
{
std::string assets_path = original_game_binary;
chop_last_component(assets_path);
assets_path += "/data";
set_and_print_env(_MCPI_VANILLA_ASSETS_PATH_ENV, assets_path.c_str());
}
// Start Game
INFO("Starting Game...");
// Arguments
std::vector<std::string> args;
// Use Extra If Needed
#ifdef MCPI_BUILD_RUNTIME
args.push_back("runtime");
#endif
// Fix QEMU Bug
#ifdef MCPI_RUNTIME_IS_QEMU
args.push_back("-B");
args.push_back(std::to_string(QEMU_GUEST_BASE));
#endif
// Specify MCPI Binary
args.push_back(new_mcpi_exe_path);
// Run
const char *new_argv[args.size() + 1];
for (std::vector<std::string>::size_type i = 0; i < args.size(); i++) {
new_argv[i] = args[i].c_str();
}
new_argv[args.size()] = nullptr;
safe_execvpe(new_argv, environ);
}

View File

@ -1,10 +0,0 @@
#pragma once
#include <string>
#include <vector>
#include "options/parser.h"
void bootstrap(const options_t &options);
void copy_sdk(const std::string &binary_directory, bool log_with_debug);
std::vector<std::string> bootstrap_mods(const std::string &binary_directory);

View File

@ -0,0 +1,15 @@
#include <libreborn/env/env.h>
#include "bootstrap.h"
#include "../util/util.h"
// Setup Asset Paths
static void setup_path(const char *env_name, std::string assets_path) {
chop_last_component(assets_path);
assets_path += "/data";
set_and_print_env(env_name, assets_path.c_str());
}
void bootstrap_assets(const std::string &original_game_binary) {
setup_path(_MCPI_REBORN_ASSETS_PATH_ENV, safe_realpath("/proc/self/exe"));
setup_path(_MCPI_VANILLA_ASSETS_PATH_ENV, original_game_binary);
}

View File

@ -0,0 +1,78 @@
#include <string>
#include <vector>
#include <libreborn/log.h>
#include <libreborn/env/env.h>
#include <libreborn/config.h>
#include <libreborn/util/exec.h>
#include "../util/util.h"
#include "bootstrap.h"
#define MCPI_BINARY "minecraft-pi"
#define REQUIRED_PAGE_SIZE 4096
// Bootstrap
void bootstrap(const options_t &options) {
// Debug Information
print_debug_information();
// Check Page Size
const long page_size = sysconf(_SC_PAGESIZE);
if (page_size != REQUIRED_PAGE_SIZE) {
CONDITIONAL_ERR(!options.skip_pagesize_check, "Invalid page size! A page size of %ld bytes is required, but the system size is %ld bytes.", (long) REQUIRED_PAGE_SIZE, page_size);
}
// Get Binary Directory
const std::string binary_directory = get_binary_directory();
DEBUG("Binary Directory: %s", binary_directory.c_str());
// Copy SDK
if (!reborn_is_server()) {
copy_sdk(binary_directory, false);
}
// Resolve Binary Path
DEBUG("Resolving File Paths...");
std::string original_game_binary = binary_directory + ("/" MCPI_BINARY);
original_game_binary = safe_realpath(original_game_binary);
const char *custom_binary = getenv(MCPI_BINARY_ENV);
const std::string game_binary = custom_binary ? safe_realpath(custom_binary) : original_game_binary;
// Configure Preloaded Objects
DEBUG("Locating Mods...");
const std::vector<std::string> mcpi_ld_preload = bootstrap_mods(binary_directory);
// Configure Library Search Path
DEBUG("Setting Linker Search Paths...");
const std::vector<std::string> mcpi_ld_path = get_ld_path(binary_directory);
// Assets
DEBUG("Finding Assets...");
bootstrap_assets(original_game_binary);
// Patch Binary
char new_mcpi_exe_path[] = MCPI_PATCHED_DIR "/XXXXXX";
DEBUG("Patching ELF...");
patch_mcpi_elf_dependencies(game_binary, new_mcpi_exe_path, get_new_linker(binary_directory), mcpi_ld_path, mcpi_ld_preload);
// Start Game
INFO("Starting Game...");
// Arguments
const std::vector<std::string> args {
#ifdef MCPI_BUILD_RUNTIME
"runtime",
#endif
new_mcpi_exe_path
};
// Run
const char *new_argv[args.size() + 1];
for (std::vector<std::string>::size_type i = 0; i < args.size(); i++) {
new_argv[i] = args[i].c_str();
}
new_argv[args.size()] = nullptr;
safe_execvpe(new_argv, environ);
}

View File

@ -0,0 +1,20 @@
#pragma once
#include <string>
#include <vector>
#include "../options/parser.h"
#define MCPI_PATCHED_DIR "/tmp/.minecraft-pi-patched"
void bootstrap(const options_t &options);
// Debugging
void print_debug_information();
// Mods
std::vector<std::string> bootstrap_mods(const std::string &binary_directory);
// Assets
void bootstrap_assets(const std::string &original_game_binary);
// ELF
std::string get_new_linker(const std::string &binary_directory);
std::vector<std::string> get_ld_path(const std::string &binary_directory);
void patch_mcpi_elf_dependencies(const std::string &original_path, char *new_path, const std::string &interpreter, const std::vector<std::string> &rpath, const std::vector<std::string> &mods);

View File

@ -0,0 +1,38 @@
#include <libreborn/log.h>
#include <libreborn/util/exec.h>
#include <libreborn/config.h>
#include "bootstrap.h"
// Debug Information
static void run_debug_command(const char *const command[], const char *prefix) {
int status = 0;
const std::vector<unsigned char> *output = run_command(command, &status);
if (!is_exit_status_success(status)) {
ERR("Unable To Gather Debug Information");
}
std::string output_str = (const char *) output->data();
delete output;
// Trim
const std::string::size_type length = output_str.length();
if (length > 0 && output_str[length - 1] == '\n') {
output_str.pop_back();
}
// Print
DEBUG("%s: %s", prefix, output_str.c_str());
}
void print_debug_information() {
// System Information
constexpr const char *const command[] = {"uname", "-a", nullptr};
run_debug_command(command, "System Information");
// Version
DEBUG("Reborn Version: v%s", MCPI_VERSION);
// Architecture
std::string arch = MCPI_ARCH;
for (char &c : arch) {
c = char(std::toupper(c));
}
DEBUG("Reborn Target Architecture: %s", arch.c_str());
}

View File

@ -2,10 +2,13 @@
#include <cerrno>
#include <sys/stat.h>
#include <unistd.h>
#include <cstring>
#include <libreborn/libreborn.h>
#include <libreborn/log.h>
#include <libreborn/util/util.h>
#include "bootstrap.h"
#include "../util/util.h"
// Get All Mods In Folder
static void load(std::vector<std::string> &ld_preload, const std::string &folder, int recursion_limit = 128);
@ -18,10 +21,8 @@ static void handle_file(std::vector<std::string> &ld_preload, const std::string
load(ld_preload, std::string(file) + '/', recursion_limit - 1);
} else if (S_ISLNK(file_stat.st_mode)) {
// Resolve Symlink
char *resolved_file = realpath(file.c_str(), nullptr);
ALLOC_CHECK(resolved_file);
const std::string resolved_file = safe_realpath(file);
handle_file(ld_preload, resolved_file, recursion_limit);
free(resolved_file);
} else if (S_ISREG(file_stat.st_mode)) {
// Check If File Is Accessible
const int result = access(file.c_str(), R_OK);
@ -41,36 +42,15 @@ static void load(std::vector<std::string> &ld_preload, const std::string &folder
if (recursion_limit <= 0) {
ERR("Reached Recursion Limit While Loading Mods");
}
// Open Folder
// Make Directory
ensure_directory(folder.c_str());
DIR *dp = opendir(folder.c_str());
if (dp == nullptr) {
// Unable To Open Folder
ERR("Error Opening Directory: %s: %s", folder.c_str(), strerror(errno));
}
// Loop Through Folder
while (true) {
errno = 0;
const dirent *entry = readdir(dp);
if (entry != nullptr) {
// Block Pseudo-Directories
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
// Get Full Name
std::string name = folder + entry->d_name;
// Handle
handle_file(ld_preload, name, recursion_limit);
} else if (errno != 0) {
// Error Reading Contents Of Folder
ERR("Error Reading Directory: %s: %s", folder.c_str(), strerror(errno));
} else {
// Done!
break;
}
}
// Close Folder
closedir(dp);
// Read
read_directory(folder, [&folder, &ld_preload, &recursion_limit](const dirent *entry) {
// Get Full Name
const std::string name = folder + entry->d_name;
// Handle
handle_file(ld_preload, name, recursion_limit);
});
}
// Bootstrap Mods
@ -79,19 +59,13 @@ std::vector<std::string> bootstrap_mods(const std::string &binary_directory) {
// Prepare
std::vector<std::string> preload;
// ~/.minecraft-pi/mods
{
// Get Mods Folder
const std::string mods_folder = std::string(getenv(_MCPI_HOME_ENV)) + get_home_subdirectory_for_game_data() + SUBDIRECTORY_FOR_MODS;
// Load Mods From ./mods
load(preload, mods_folder);
}
// Built-In Mods
{
// Get Mods Folder
const std::string mods_folder = binary_directory + SUBDIRECTORY_FOR_MODS;
// Load Mods From ./mods
// Load
const std::vector folders = {
home_get(),
binary_directory
};
for (std::string mods_folder : folders) {
mods_folder += SUBDIRECTORY_FOR_MODS;
load(preload, mods_folder);
}

View File

@ -4,12 +4,10 @@
#include <LIEF/ELF.hpp>
#include <dlfcn.h>
#include <link.h>
#include <libreborn/util/util.h>
#include <libreborn/config.h>
#include <libreborn/libreborn.h>
#include "patchelf.h"
#include "bootstrap.h"
// Duplicate MCPI Executable Into /tmp
static void duplicate_mcpi_executable(char *new_path) {
@ -43,12 +41,10 @@ void patch_mcpi_elf_dependencies(const std::string &original_path, char *new_pat
duplicate_mcpi_executable(new_path);
// Load Binary
std::unique_ptr<LIEF::ELF::Binary> binary = LIEF::ELF::Parser::parse(original_path);
const std::unique_ptr<LIEF::ELF::Binary> binary = LIEF::ELF::Parser::parse(original_path);
// Set Interpreter
if (!interpreter.empty()) {
binary->interpreter(interpreter);
}
binary->interpreter(interpreter);
// Remove Existing Needed Libraries
std::vector<std::string> to_remove;
@ -96,3 +92,34 @@ void patch_mcpi_elf_dependencies(const std::string &original_path, char *new_pat
ERR("Unable To Set File Permissions: %s: %s", new_path, strerror(errno));
}
}
// Linker
std::string get_new_linker(const std::string &binary_directory) {
std::string linker = "/lib/ld-linux-armhf.so.3";
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
linker = binary_directory + "/sysroot" + linker;
#else
(void) binary_directory;
#endif
return linker;
}
std::vector<std::string> get_ld_path(const std::string &binary_directory) {
std::vector<std::string> mcpi_ld_path = {
// ARM Sysroot
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
"sysroot/lib",
"sysroot/lib/arm-linux-gnueabihf",
"sysroot/usr/lib",
"sysroot/usr/lib/arm-linux-gnueabihf",
#endif
// Libraries
"lib/arm"
};
// Fix Paths
for (std::string &path : mcpi_ld_path) {
path.insert(0, 1, '/');
path.insert(0, binary_directory);
}
// Return
return mcpi_ld_path;
}

View File

@ -1,115 +0,0 @@
FALSE Full Touch GUI
TRUE Fix Bow & Arrow
TRUE Fix Attacking
FALSE Force Mob Spawning
TRUE Disable Autojump By Default
TRUE Display Nametags By Default
TRUE Fix Sign Placement
TRUE Show Block Outlines
FALSE Expand Creative Mode Inventory
FALSE Remove Creative Mode Restrictions
FALSE Display Slot Count In Creative Mode
FALSE Force Survival Mode Inventory UI
FALSE Force Survival Mode Inventory Behavior
FALSE Maximize Creative Mode Inventory Stack Size
TRUE Animated Water
TRUE Animated Lava
TRUE Animated Fire
TRUE Regenerate "gui_blocks" Atlas
TRUE Fix Camera Rendering
TRUE Implement Chat
FALSE Hide Chat Messages
TRUE Implement Death Messages
TRUE Implement Game-Mode Switching
TRUE Allow Joining Survival Mode Servers
TRUE Miscellaneous Input Fixes
TRUE Bind "Q" Key To Item Dropping
TRUE Bind Common Toggleable Options To Function Keys
TRUE Render Selected Item Text
TRUE External Server Support
TRUE Load Language Files
TRUE Implement Sound Engine
TRUE Close Current Screen On Death
FALSE Disable Raw Mouse Motion (Not Recommended)
TRUE Fix Furnace Not Checking Item Auxiliary
TRUE Improved Cursor Rendering
TRUE Disable V-Sync
TRUE Fix Options Screen
TRUE Force Touch GUI Inventory
TRUE Fix Pause Menu
TRUE Add Title Screen Background
TRUE Force Touch GUI Button Behavior
TRUE Improved Button Hover Behavior
TRUE Implement Create World Dialog
FALSE Remove Forced GUI Lag (Can Break Joining Servers)
TRUE Add Buckets
TRUE Classic HUD
TRUE Translucent Toolbar
FALSE Force EGL
TRUE Improved Classic Title Screen
FALSE Disable Speed Bridging
FALSE Disable Creative Mode Mining Delay
FALSE Add Biome Colors To Grass
TRUE Generate Caves
FALSE Disable Block Tinting
TRUE Disable Hostile AI In Creative Mode
TRUE Load Custom Skins
TRUE 3D Chest Model
TRUE Replace Block Highlight With Outline
TRUE Add Cake
TRUE Use Java Beta 1.3 Light Ramp
TRUE Send Full Level When Hosting Game
FALSE Food Overlay
TRUE Add Splashes
TRUE Display Date In Select World Screen
TRUE Optimized Chunk Sorting
TRUE Fix Held Item Caching
TRUE Add Reborn Info To Options
FALSE Log FPS
TRUE Add Welcome Screen
TRUE F3 Debug Information
TRUE Multidraw Rendering
TRUE Add Missing Language Strings
TRUE Fix Pigmen Burning In The Sun
TRUE Fix Carried Grass's Bottom Texture
TRUE Hide Crosshair In Third-Person
TRUE Fix Camera Legs
TRUE Implement Crafting Remainders
TRUE Fix Door Duplication
TRUE Fix Cobweb Lighting
TRUE Fix Sneaking Syncing
TRUE Fix Fire Immunity
TRUE Fix Fire Syncing
TRUE Fix Sunlight Not Properly Setting Mobs On Fire
TRUE Stop Creative Players From Burning
TRUE Render Fire In Third-Person
TRUE Improved Water Rendering
TRUE Classic Item Count UI
TRUE Fix Screen Rendering When Hiding HUD
TRUE Sanitize Usernames
TRUE Patch RakNet Security Bug
TRUE Log RakNet Startup Errors
TRUE Prevent Unnecessary Server Pinging
TRUE Proper OpenGL Buffer Generation
TRUE Fix Furnace Screen Visual Bug
TRUE Fix Text Wrapping
TRUE Fullscreen Support
TRUE Always Save Chest Tile Entities
TRUE Fix Transferring Durability When Using Items
TRUE Fix Switching Perspective While Sneaking
TRUE Log Chat Messages
TRUE Log Game Status
TRUE Screenshot Support
TRUE Fix Camera Functionality
TRUE Allow High-Resolution Title
TRUE Improved Classic Title Positioning
TRUE Use Updated Title
TRUE Hide Block Outline When GUI Is Hidden
TRUE Fix Crash When Generating Certain Seeds
TRUE Click Buttons On Mouse Down
TRUE 3D Dropped Items
TRUE Render Entity Shadows
TRUE Render Vignette
TRUE Implement RaspberryJuice API
TRUE Increase Render Chunk Size
TRUE Proper Entity Shading

View File

@ -3,87 +3,105 @@
#include <fstream>
#include <unordered_map>
#include <sstream>
#include <sys/stat.h>
#include <unistd.h>
#include <libreborn/libreborn.h>
#include <libreborn/log.h>
#include <libreborn/util/util.h>
#include <libreborn/util/io.h>
#include "configuration.h"
#include "cache.h"
#include "configuration.h"
// Get Cache Path
static std::string get_cache_path() {
const char *home = getenv(_MCPI_HOME_ENV);
if (home == nullptr) {
IMPOSSIBLE();
}
return std::string(home) + get_home_subdirectory_for_game_data() + "/.launcher-cache";
return home_get() + "/.launcher-cache";
}
// Load
launcher_cache empty_cache = {
.username = DEFAULT_USERNAME,
.render_distance = DEFAULT_RENDER_DISTANCE,
.feature_flags = {}
};
launcher_cache load_cache() {
template <typename T>
static T simple_read(std::ifstream &stream) {
T out;
stream.read((char *) &out, sizeof(T));
return out;
}
template <>
std::string simple_read<std::string>(std::ifstream &stream) {
std::string out;
if (!std::getline(stream, out, '\0')) {
out = "";
}
return out;
}
static void read_cache(std::ifstream &stream, State &ret) {
// Cache Version
const unsigned char cache_version = simple_read<unsigned char>(stream);
if (stream.eof()) {
// Unable To Read Version
WARN("Unable To Read Launcher Cache Version");
return;
}
// Support Older Versions
bool load_new_fields = true;
if (cache_version == 0) {
// Pre-v3.0.0 Cache
load_new_fields = false;
} else if (cache_version != (unsigned char) CACHE_VERSION) {
// Invalid Version
WARN("Invalid Launcher Cache Version (Expected: %i, Actual: %i)", CACHE_VERSION, (int) cache_version);
return;
}
// Load Username And Render Distance
State state;
state.username = simple_read<std::string>(stream);
state.render_distance = simple_read<std::string>(stream);
if (load_new_fields) {
state.gui_scale = simple_read<float>(stream);
state.servers.load(simple_read<std::string>(stream));
}
// Load Feature Flags
std::unordered_map<std::string, bool> flags;
while (!stream.eof()) {
std::string flag = simple_read<std::string>(stream);
flags[flag] = simple_read<bool>(stream);
stream.peek();
}
state.flags.from_cache(flags);
// Check For Error
if (!stream) {
WARN("Failure While Loading Launcher Cache");
return;
}
// Success
ret = state;
}
State load_cache() {
// Log
DEBUG("Loading Launcher Cache...");
// Return Value
launcher_cache ret = empty_cache;
State ret;
// Open File
std::ifstream stream(get_cache_path(), std::ios::in | std::ios::binary);
if (!stream) {
// Fail
struct stat s;
// No Warning If File Doesn't Exist
if (stat(get_cache_path().c_str(), &s) == 0) {
if (errno != ENOENT) {
WARN("Unable To Open Launcher Cache For Loading");
}
} else {
// Lock File
int lock_fd = lock_file(get_cache_path().c_str());
// Check Version
unsigned char cache_version;
stream.read((char *) &cache_version, 1);
if (stream.eof() || cache_version != (unsigned char) CACHE_VERSION) {
// Fail
if (!stream.eof()) {
WARN("Invalid Launcher Cache Version (Expected: %i, Actual: %i)", (int) CACHE_VERSION, (int) cache_version);
} else {
WARN("Unable To Read Launcher Cache Version");
}
stream.close();
} else {
// Load Username And Render Distance
launcher_cache cache;
std::getline(stream, cache.username, '\0');
std::getline(stream, cache.render_distance, '\0');
// Load
read_cache(stream, ret);
// Load Feature Flags
std::string flag;
while (!stream.eof() && std::getline(stream, flag, '\0')) {
if (flag.length() > 0) {
unsigned char is_enabled = 0;
stream.read((char *) &is_enabled, 1);
cache.feature_flags[flag] = is_enabled != (unsigned char) 0;
}
stream.peek();
}
// Finish
stream.close();
if (!stream) {
// Fail
WARN("Failure While Loading Launcher Cache");
} else {
// Success
ret = cache;
}
}
// Close
stream.close();
// Unlock File
unlock_file(get_cache_path().c_str(), lock_fd);
@ -94,15 +112,33 @@ launcher_cache load_cache() {
}
// Save
#define write_env_to_stream(stream, env) \
{ \
const char *env_value = getenv(env); \
if (env == NULL) { \
IMPOSSIBLE(); \
} \
stream.write(env_value, strlen(env_value) + 1); \
template <typename T>
static void simple_write(std::ostream &stream, const T &val) {
stream.write((const char *) &val, sizeof(T));
}
template <>
void simple_write<std::string>(std::ostream &stream, const std::string &val) {
stream.write(val.c_str(), int(val.size()) + 1);
}
void write_cache(std::ostream &stream, const State &state) {
// Save Cache Version
constexpr unsigned char cache_version = CACHE_VERSION;
simple_write(stream, cache_version);
// Save Username And Render Distance
simple_write(stream, state.username);
simple_write(stream, state.render_distance);
simple_write(stream, state.gui_scale);
simple_write(stream, state.servers.to_string());
// Save Feature Flags
const std::unordered_map<std::string, bool> flags_cache = state.flags.to_cache();
for (const std::pair<const std::string, bool> &it : flags_cache) {
simple_write(stream, it.first);
simple_write(stream, it.second);
}
void save_cache() {
}
void save_cache(const State &state) {
// Log
DEBUG("Saving Launcher Cache...");
@ -113,44 +149,14 @@ void save_cache() {
WARN("Unable To Open Launcher Cache For Saving");
} else {
// Lock File
int lock_fd = lock_file(get_cache_path().c_str());
const int lock_fd = lock_file(get_cache_path().c_str());
// Save Cache Version
unsigned char cache_version = (unsigned char) CACHE_VERSION;
stream.write((const char *) &cache_version, 1);
// Save Username And Render Distance
write_env_to_stream(stream, MCPI_USERNAME_ENV);
write_env_to_stream(stream, MCPI_RENDER_DISTANCE_ENV);
// Save Feature Flags
std::unordered_map<std::string, bool> flags;
load_available_feature_flags([&flags](std::string flag) {
std::string stripped_flag = strip_feature_flag_default(flag, nullptr);
flags[stripped_flag] = false;
});
{
const char *enabled_flags = getenv(MCPI_FEATURE_FLAGS_ENV);
if (enabled_flags == nullptr) {
IMPOSSIBLE();
}
std::istringstream enabled_flags_stream(enabled_flags);
std::string flag;
while (std::getline(enabled_flags_stream, flag, '|')) {
if (flag.length() > 0) {
flags[flag] = true;
}
}
}
for (auto &it : flags) {
stream.write(it.first.c_str(), it.first.size() + 1);
unsigned char val = it.second ? (unsigned char) 1 : (unsigned char) 0;
stream.write((const char *) &val, 1);
}
// Write
write_cache(stream, state);
// Finish
stream.close();
if (!stream.good()) {
if (!stream) {
WARN("Failure While Saving Launcher Cache");
}

View File

@ -1,22 +1,17 @@
#pragma once
#include <string>
#include <unordered_map>
#include <ostream>
// Cache Version
#define CACHE_VERSION 0
#define CACHE_VERSION 1
// Load Cache
typedef struct {
std::string username;
std::string render_distance;
std::unordered_map<std::string, bool> feature_flags;
} launcher_cache;
extern launcher_cache empty_cache;
launcher_cache load_cache();
struct State;
State load_cache();
// Save Cache
void save_cache();
void write_cache(std::ostream &stream, const State &state);
void save_cache(const State &state);
// Wipe Cache
void wipe_cache();

View File

@ -1,147 +1,49 @@
#include <sstream>
#include <cstring>
#include <cerrno>
#include <sys/wait.h>
#include <sys/stat.h>
#include <vector>
#include <functional>
#include <algorithm>
#include <libreborn/libreborn.h>
#include <libreborn/env/env.h>
#include "../util.h"
#include "configuration.h"
#include "cache.h"
// Strip Feature Flag Default
std::string strip_feature_flag_default(const std::string &flag, bool *default_ret) {
// Valid Values
std::string true_str = "TRUE ";
std::string false_str = "FALSE ";
// Test
if (flag.rfind(true_str, 0) == 0) {
// Enabled By Default
if (default_ret != nullptr) {
*default_ret = true;
}
return flag.substr(true_str.length(), std::string::npos);
} else if (flag.rfind(false_str, 0) == 0) {
// Disabled By Default
if (default_ret != nullptr) {
*default_ret = false;
}
return flag.substr(false_str.length(), std::string::npos);
// State
State::State(): flags("") {
username = DEFAULT_USERNAME;
render_distance = DEFAULT_RENDER_DISTANCE;
gui_scale = AUTO_GUI_SCALE;
flags = Flags::get();
}
template <typename T>
static void update_from_env(const char *env, T &value, const bool save) {
if (save) {
set_and_print_env(env, obj_to_env_value(value).c_str());
} else {
// Invalid
ERR("Invalid Feature Flag Default");
}
}
// Load Available Feature Flags
extern unsigned char available_feature_flags[];
extern size_t available_feature_flags_len;
void load_available_feature_flags(const std::function<void(std::string)> &callback) {
// Load Data
const std::string data(available_feature_flags, available_feature_flags + available_feature_flags_len);
std::stringstream stream(data);
// Store Lines
std::vector<std::string> lines;
// Read File
{
std::string line;
while (std::getline(stream, line)) {
if (!line.empty()) {
// Verify Line
if (line.find('|') == std::string::npos) {
lines.push_back(line);
} else {
// Invalid Line
ERR("Feature Flag Contains Invalid '|'");
}
}
}
}
// Sort
std::sort(lines.begin(), lines.end(), [](const std::string &a, const std::string &b) {
// Strip Defaults
const std::string stripped_a = strip_feature_flag_default(a, nullptr);
const std::string stripped_b = strip_feature_flag_default(b, nullptr);
// Sort
return stripped_a < stripped_b;
});
// Run Callbacks
for (const std::string &line : lines) {
callback(line);
}
}
// Run Command And Set Environmental Variable
static void run_command_and_set_env(const char *env_name, const char *command[]) {
// Only Run If Environmental Variable Is NULL
if (getenv(env_name) == nullptr) {
// Check $DISPLAY
reborn_check_display();
// Run
int return_code;
char *output = run_command(command, &return_code, nullptr);
if (output != nullptr) {
// Trim
const size_t length = strlen(output);
if (output[length - 1] == '\n') {
output[length - 1] = '\0';
}
// Set
set_and_print_env(env_name, output);
// Free
free(output);
}
// Check Return Code
if (!is_exit_status_success(return_code)) {
// Launch Interrupted
exit(EXIT_SUCCESS);
const char *env_value = getenv(env);
if (env_value != nullptr) {
env_value_to_obj(value, env_value);
}
}
}
// Use Zenity To Set Environmental Variable
#define DIALOG_TITLE "Launcher"
static void run_zenity_and_set_env(const char *env_name, std::vector<std::string> command) {
// Create Full Command
std::vector<std::string> full_command;
full_command.push_back("zenity");
full_command.push_back("--title");
full_command.push_back(DIALOG_TITLE);
full_command.push_back("--name");
full_command.push_back(MCPI_APP_ID);
full_command.insert(full_command.end(), command.begin(), command.end());
// Convert To C Array
const char *full_command_array[full_command.size() + 1];
for (std::vector<std::string>::size_type i = 0; i < full_command.size(); i++) {
full_command_array[i] = full_command[i].c_str();
}
full_command_array[full_command.size()] = nullptr;
// Run
run_command_and_set_env(env_name, full_command_array);
void State::update(const bool save) {
update_from_env(MCPI_FEATURE_FLAGS_ENV, flags, save);
update_from_env(MCPI_USERNAME_ENV, username, save);
update_from_env(MCPI_RENDER_DISTANCE_ENV, render_distance, save);
update_from_env(MCPI_GUI_SCALE_ENV, gui_scale, save);
update_from_env(MCPI_SERVER_LIST_ENV, servers, save);
}
// Set Variable If Not Already Set
static void set_env_if_unset(const char *env_name, const std::function<std::string()> &callback) {
if (getenv(env_name) == nullptr) {
char *value = strdup(callback().c_str());
ALLOC_CHECK(value);
set_and_print_env(env_name, value);
free(value);
}
bool State::operator==(const State &other) const {
std::ostringstream one;
write_cache(one, *this);
std::ostringstream two;
write_cache(two, other);
return one.str() == two.str();
}
// Handle Non-Launch Commands
void handle_non_launch_client_only_commands(const options_t &options) {
// Print Available Feature Flags
if (options.print_available_feature_flags) {
load_available_feature_flags([](const std::string &line) {
printf("%s\n", line.c_str());
fflush(stdout);
});
const Flags flags = Flags::get();
flags.print();
exit(EXIT_SUCCESS);
}
// Wipe Cache If Needed
@ -152,115 +54,33 @@ void handle_non_launch_client_only_commands(const options_t &options) {
}
// Configure Client Options
#define LIST_DIALOG_SIZE "400"
void configure_client(const options_t &options) {
// Load Cache
launcher_cache cache = options.no_cache ? empty_cache : load_cache();
// --default
if (options.use_default) {
// Use Default Feature Flags
set_env_if_unset(MCPI_FEATURE_FLAGS_ENV, [&cache]() {
std::string feature_flags = "";
load_available_feature_flags([&feature_flags, &cache](const std::string &flag) {
bool value;
// Strip Default Value
std::string stripped_flag = strip_feature_flag_default(flag, &value);
// Use Cache
if (cache.feature_flags.count(stripped_flag) > 0) {
value = cache.feature_flags[stripped_flag];
}
// Specify Default Value
if (value) {
// Enabled By Default
feature_flags += stripped_flag + '|';
}
});
if (!feature_flags.empty() && feature_flags[feature_flags.length() - 1] == '|') {
feature_flags.pop_back();
}
return feature_flags;
});
set_env_if_unset(MCPI_RENDER_DISTANCE_ENV, [&cache]() {
return cache.render_distance;
});
set_env_if_unset(MCPI_USERNAME_ENV, [&cache]() {
return cache.username;
});
State state;
bool save_settings = !options.no_cache;
if (save_settings) {
state = load_cache();
}
// Setup MCPI_FEATURE_FLAGS
{
std::vector<std::string> command;
command.push_back("--list");
command.push_back("--checklist");
command.push_back("--width");
command.push_back(LIST_DIALOG_SIZE);
command.push_back("--height");
command.push_back(LIST_DIALOG_SIZE);
command.push_back("--column");
command.push_back("Enabled");
command.push_back("--column");
command.push_back("Feature");
load_available_feature_flags([&command, &cache](const std::string &flag) {
bool value;
// Strip Default Value
std::string stripped_flag = strip_feature_flag_default(flag, &value);
// Use Cache
if (cache.feature_flags.count(stripped_flag) > 0) {
value = cache.feature_flags[stripped_flag];
}
// Specify Default Value
if (value) {
// Enabled By Default
command.push_back("TRUE");
} else {
// Disabled By Default
command.push_back("FALSE");
}
// Specify Name
command.push_back(stripped_flag);
});
// Run
run_zenity_and_set_env(MCPI_FEATURE_FLAGS_ENV, command);
}
// Setup MCPI_RENDER_DISTANCE
{
std::vector<std::string> command;
command.push_back("--list");
command.push_back("--radiolist");
command.push_back("--width");
command.push_back(LIST_DIALOG_SIZE);
command.push_back("--height");
command.push_back(LIST_DIALOG_SIZE);
command.push_back("--text");
command.push_back("Select Minecraft Render Distance:");
command.push_back("--column");
command.push_back("Selected");
command.push_back("--column");
command.push_back("Name");
std::string render_distances[] = {"Far", "Normal", "Short", "Tiny"};
for (std::string &render_distance : render_distances) {
command.push_back(render_distance == cache.render_distance ? "TRUE" : "FALSE");
command.push_back(render_distance);
// Read From Environment
state.update(false);
// Show UI
if (!options.use_default) {
ConfigurationUI *ui = new ConfigurationUI(state, save_settings);
const int ret = ui->run();
delete ui;
if (ret <= 0) {
// Cancel Launch
exit(EXIT_SUCCESS);
}
// Run
run_zenity_and_set_env(MCPI_RENDER_DISTANCE_ENV, command);
}
// Setup MCPI_USERNAME
{
std::vector<std::string> command;
command.push_back("--entry");
command.push_back("--text");
command.push_back("Enter Minecraft Username:");
command.push_back("--entry-text");
command.push_back(cache.username);
// Run
run_zenity_and_set_env(MCPI_USERNAME_ENV, command);
}
// Save Cache
if (!options.no_cache) {
save_cache();
if (save_settings) {
save_cache(state);
}
// Update Environment
state.update(true);
}

View File

@ -1,17 +1,57 @@
#pragma once
#include <string>
#include <functional>
#include "../options/parser.h"
#include "../ui/frame.h"
// Defaults
#include <libreborn/env/flags.h>
#include <libreborn/env/servers.h>
// Default Configuration
#define DEFAULT_USERNAME "StevePi"
#define DEFAULT_RENDER_DISTANCE "Short"
#define AUTO_GUI_SCALE 0
// Feature Flags
std::string strip_feature_flag_default(const std::string& flag, bool *default_ret);
void load_available_feature_flags(const std::function<void(std::string)> &callback);
// State
struct State {
State();
// Methods
void update(bool save);
bool operator==(const State &other) const;
// Properties
std::string username;
std::string render_distance;
ServerList servers;
float gui_scale;
Flags flags;
};
// UI
struct ConfigurationUI final : Frame {
explicit ConfigurationUI(State &state_, bool &save_settings_);
int render() override;
private:
// Bottom Row
[[nodiscard]] int get_render_distance_index() const;
[[nodiscard]] int draw_bottom() const;
// General
void draw_main() const;
// Advanced
void draw_advanced() const;
static void draw_category(FlagNode &category);
// Server List
void draw_servers() const;
void draw_server_list() const;
// About
static void draw_centered_text(const std::string &str);
static void draw_links(const std::vector<std::pair<std::string, std::string>> &links);
static void draw_about();
// State
const State original_state;
State &state;
bool &save_settings;
};
// Handle Non-Launch Commands
void handle_non_launch_client_only_commands(const options_t &options);

302
launcher/src/client/ui.cpp Normal file
View File

@ -0,0 +1,302 @@
#include <vector>
#include <limits>
#include <ranges>
#include <libreborn/util/util.h>
#include <libreborn/config.h>
#include <libreborn/util/exec.h>
#include "configuration.h"
#include "../updater/updater.h"
#include <imgui_stdlib.h>
// Render Distances
static constexpr std::array render_distances = {
"Far",
"Normal",
"Short",
"Tiny"
};
// Construct
static constexpr int size = 400;
ConfigurationUI::ConfigurationUI(State &state_, bool &save_settings_):
Frame("Launcher", size, size),
original_state(state_),
state(state_),
save_settings(save_settings_) {}
// Render
int ConfigurationUI::render() {
if (ImGui::BeginChild("Main", ImVec2(0, -ImGui::GetFrameHeightWithSpacing() /* Leave Room For Bottom Row */), ImGuiChildFlags_None, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
// Tabs
if (ImGui::BeginTabBar("TabBar")) {
// Main Tab
if (ImGui::BeginTabItem("General")) {
draw_main();
ImGui::EndTabItem();
}
// Advanced Tab
if (ImGui::BeginTabItem("Advanced")) {
draw_advanced();
ImGui::EndTabItem();
}
// Servers Tab
if (ImGui::BeginTabItem("Servers")) {
draw_servers();
ImGui::EndTabItem();
}
// About Tab
if (ImGui::BeginTabItem("About")) {
draw_about();
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
}
ImGui::EndChild();
// Bottom Row
return draw_bottom();
}
// Bottom Row
int ConfigurationUI::draw_bottom() const {
// Reset Settings
const State default_state;
std::vector<std::tuple<std::string, std::string, const State *>> reset_options = {
{"Revert", "Last Saved", &original_state},
{"Reset", "Default", &default_state},
};
for (const std::tuple<std::string, std::string, const State *> &option : reset_options) {
const State &new_state = *std::get<2>(option);
ImGui::BeginDisabled(state == new_state);
if (ImGui::Button(std::get<0>(option).c_str())) {
state = new_state;
}
ImGui::SetItemTooltip("Use %s Settings", std::get<1>(option).c_str());
ImGui::EndDisabled();
ImGui::SameLine();
}
// Right-Align Buttons
int ret = 0;
draw_right_aligned_buttons({quit_text, "Launch"}, [&ret](const int id, const bool was_clicked) {
if (id == 0) {
// Quit
if (was_clicked) {
ret = -1;
}
ImGui::SetItemTooltip("Changes Will Not Be Saved!");
} else if (was_clicked) {
// Launch
ret = 1;
}
});
// Return
return ret;
}
// Main Tab
int ConfigurationUI::get_render_distance_index() const {
int render_distance_index = 0;
for (std::vector<std::string>::size_type i = 0; i < render_distances.size(); i++) {
if (std::string(render_distances[i]) == state.render_distance) {
render_distance_index = int(i);
break;
}
}
return render_distance_index;
}
void ConfigurationUI::draw_main() const {
const ImGuiStyle &style = ImGui::GetStyle();
const char *labels[] = {"Username", "Render Distance", "UI Scale"};
// Calculate Label Size
float label_size = 0;
for (const char *label : labels) {
label_size = std::max(label_size, ImGui::CalcTextSize(label).x + style.ItemInnerSpacing.x);
}
ImGui::PushItemWidth(-label_size);
// Username
ImGui::InputText(labels[0], &state.username);
// Render Distance
int render_distance_index = get_render_distance_index();
if (ImGui::Combo(labels[1], &render_distance_index, render_distances.data(), int(render_distances.size()))) {
state.render_distance = render_distances[render_distance_index];
}
// UI Scale
int gui_scale_int = int(state.gui_scale); // Fractional GUI Scales Are Messy
std::string scale_format = "%ix";
if (gui_scale_int <= AUTO_GUI_SCALE) {
scale_format = "Automatic";
}
if (ImGui::SliderInt(labels[2], &gui_scale_int, 0, 8, scale_format.c_str())) {
state.gui_scale = float(gui_scale_int);
if (state.gui_scale < AUTO_GUI_SCALE) {
state.gui_scale = AUTO_GUI_SCALE;
}
}
ImGui::PopItemWidth();
// Launcher Cache
ImGui::Checkbox("Save Settings On Launch", &save_settings);
}
// Advanced Tab
static std::string get_label_for_flag_node(const FlagNode &node) {
return node.name + "##FlagNode" + std::to_string(node.id);
}
void ConfigurationUI::draw_advanced() const {
if (ImGui::BeginChild("Features", ImVec2(0, 0), ImGuiChildFlags_Borders, ImGuiWindowFlags_HorizontalScrollbar)) {
// Categories
for (FlagNode &category : state.flags.root.children) {
const std::string label = get_label_for_flag_node(category);
if (ImGui::CollapsingHeader(label.c_str())) {
draw_category(category);
}
}
}
ImGui::EndChild();
}
void ConfigurationUI::draw_category(FlagNode &category) {
for (FlagNode &child : category.children) {
const std::string label = get_label_for_flag_node(child);
if (!child.children.empty()) {
// Sub-Category
if (ImGui::TreeNode(label.c_str())) {
draw_category(child);
ImGui::TreePop();
}
} else {
// Flag
ImGui::Checkbox(label.c_str(), &child.value);
}
}
}
// Servers
void ConfigurationUI::draw_servers() const {
// Add
bool scroll_to_bottom = false;
if (ImGui::Button("Add")) {
state.servers.entries.emplace_back("", DEFAULT_MULTIPLAYER_PORT);
scroll_to_bottom = true;
}
ImGui::SameLine();
// Clear
bool should_clear = false;
ImGui::BeginDisabled(state.servers.entries.empty());
draw_right_aligned_buttons({"Clear"}, [&should_clear](__attribute__((unused)) const int id, const bool was_clicked) {
should_clear = was_clicked;
});
ImGui::EndDisabled();
if (should_clear) {
state.servers.entries.clear();
}
// List
if (ImGui::BeginChild("ServerList", ImVec2(0, 0), ImGuiChildFlags_Borders)) {
draw_server_list();
if (scroll_to_bottom) {
ImGui::SetScrollHereY(1.0f);
}
}
ImGui::EndChild();
}
static int server_list_address_filter(ImGuiInputTextCallbackData *data) {
// Lowercase
constexpr std::pair lower_alpha = {'a', 'z'};
constexpr std::pair upper_alpha = {'A', 'Z'};
ImWchar &x = data->EventChar;
if (x >= upper_alpha.first && x <= upper_alpha.second) {
x += lower_alpha.first - upper_alpha.first;
}
// Check Characters
return (x >= lower_alpha.first && x <= lower_alpha.second) || x == '.' ? 0 : 1;
}
static int server_list_port_filter(ImGuiInputTextCallbackData *data) {
// Only Allow Integers
const ImWchar &x = data->EventChar;
return x >= '0' && x <= '9' ? 0 : 1;
}
void ConfigurationUI::draw_server_list() const {
for (std::vector<ServerList::Entry>::size_type i = 0; i < state.servers.entries.size(); ++i) {
ServerList::Entry &entry = state.servers.entries[i];
// Calculate Item Widths
const ImGuiStyle &style = ImGui::GetStyle();
const std::string port_width_text = std::to_string(int(std::numeric_limits<ServerList::port_t>::max()) * 2); // Should Comfortably Fit All Port Numbers
const std::string delete_text = "Delete";
const float port_width = get_frame_width(port_width_text.c_str());
const float width_needed = (style.ItemSpacing.x * 2.0f) + port_width + get_frame_width(delete_text.c_str());
// Labels
const std::string base_label = "##ServerEntry" + std::to_string(i);
// Hints
const char *address_hint = "Address";
const char *port_hint = "Port";
// Address
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x - width_needed);
ImGui::InputTextWithHint((base_label + address_hint).c_str(), address_hint, &entry.first, ImGuiInputTextFlags_CallbackCharFilter, server_list_address_filter);
ImGui::PopItemWidth();
// Port
ServerList::port_t &port = entry.second;
std::string port_str = port > 0 ? std::to_string(port) : "";
ImGui::SameLine();
ImGui::PushItemWidth(port_width);
if (ImGui::InputTextWithHint((base_label + port_hint).c_str(), port_hint, &port_str, ImGuiInputTextFlags_CallbackCharFilter | ImGuiInputTextFlags_NoHorizontalScroll, server_list_port_filter)) {
port = ServerList::parse_port(port_str);
}
ImGui::PopItemWidth();
// Delete
ImGui::SameLine();
if (ImGui::Button((delete_text + base_label).c_str())) {
state.servers.entries.erase(state.servers.entries.begin() + int(i));
i--;
}
}
}
// About
void ConfigurationUI::draw_centered_text(const std::string &str) {
const float width = ImGui::GetWindowSize().x;
const float text_width = ImGui::CalcTextSize(str.c_str()).x;
ImGui::SetCursorPosX((width - text_width) / 2.0f);
ImGui::Text("%s", str.c_str());
}
void ConfigurationUI::draw_links(const std::vector<std::pair<std::string, std::string>> &links) {
std::vector<const char *> buttons;
for (const std::string &text : links | std::views::keys) {
buttons.push_back(text.c_str());
}
draw_right_aligned_buttons(buttons, [&links](const int id, const bool was_clicked) {
if (was_clicked) {
open_url(links[id].second);
}
}, true);
}
void ConfigurationUI::draw_about() {
// Text
draw_centered_text("By " MCPI_AUTHOR);
draw_centered_text("Version " MCPI_VERSION);
// Links
ImGui::Separator();
draw_links({
{"Home", MCPI_REPO},
{"Changelog", MCPI_DOCS_CHANGELOG},
{"Credits", MCPI_DOCS "CREDITS.md"}
});
// Updater
Updater *updater = Updater::instance;
if (updater) {
ImGui::Separator();
ImGui::BeginDisabled(!updater->can_start());
draw_right_aligned_buttons({updater->get_status().c_str()}, [&updater](__attribute__((unused)) int id, const bool was_clicked) {
if (was_clicked) {
updater->start();
}
}, true);
ImGui::EndDisabled();
}
}

View File

@ -1,259 +0,0 @@
#include <unistd.h>
#include <cstring>
#include <cerrno>
#include <cstdlib>
#include <cstdio>
#include <cstdint>
#include <csignal>
#include <poll.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/prctl.h>
#include <ctime>
#include <string>
#include <libreborn/libreborn.h>
#include "crash-report.h"
// Show Crash Report Dialog
#define DIALOG_TITLE "Crash Report"
#define CRASH_REPORT_DIALOG_WIDTH "640"
#define CRASH_REPORT_DIALOG_HEIGHT "480"
static void show_report(const char *log_filename) {
// Fork
pid_t pid = fork();
if (pid == 0) {
// Child
setsid();
ALLOC_CHECK(freopen("/dev/null", "w", stdout));
ALLOC_CHECK(freopen("/dev/null", "w", stderr));
ALLOC_CHECK(freopen("/dev/null", "r", stdin));
const char *command[] = {
"zenity",
"--title", DIALOG_TITLE,
"--name", MCPI_APP_ID,
"--width", CRASH_REPORT_DIALOG_WIDTH,
"--height", CRASH_REPORT_DIALOG_HEIGHT,
"--text-info",
"--text", MCPI_APP_TITLE " has crashed!\n\nNeed help? Consider asking on the <a href=\"" MCPI_DISCORD_INVITE "\">Discord server</a>! <i>If you believe this is a problem with " MCPI_APP_TITLE " itself, please upload this crash report to the #bugs Discord channel.</i>",
"--filename", log_filename,
"--no-wrap",
"--font", "Monospace",
"--save-filename", MCPI_VARIANT_NAME "-crash-report.log",
"--ok-label", "Exit",
NULL
};
safe_execvpe(command, (const char *const *) environ);
}
}
// Exit Handler
static pid_t child_pid = -1;
static void exit_handler(__attribute__((unused)) int signal) {
// Murder
kill(child_pid, SIGTERM);
}
// Log File
static std::string log_filename;
static int log_fd;
static void setup_log_file() {
// Get Log Directory
const std::string home = std::string(getenv(_MCPI_HOME_ENV)) + get_home_subdirectory_for_game_data();
ensure_directory(home.c_str());
const std::string logs = home + "/logs";
ensure_directory(logs.c_str());
// Get Timestamp
time_t raw_time;
time(&raw_time);
const tm *time_info = localtime(&raw_time);
char time[512];
strftime(time, 512, "%Y-%m-%d", time_info);
// Get Log Filename
std::string file;
int num = 1;
do {
file = std::string(time) + '-' + std::to_string(num) + ".log";
log_filename = logs + '/' + file;
num++;
} while (access(log_filename.c_str(), F_OK) != -1);
// Create latest.log Symlink
const std::string latest_log = logs + "/latest.log";
unlink(latest_log.c_str());
if (symlink(file.c_str(), latest_log.c_str()) != 0) {
WARN("Unable To Create Latest Log Symlink: %s", strerror(errno));
}
// Create File
log_fd = open(log_filename.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (log_fd == -1) {
ERR("Unable To Create Log File: %s", strerror(errno));
}
reborn_set_log(log_fd);
}
// Setup
#define PIPE_READ 0
#define PIPE_WRITE 1
#define BUFFER_SIZE 1024
static void safe_write(int fd, const void *buf, size_t size) {
const ssize_t bytes_written = write(fd, buf, size);
if (bytes_written < 0) {
ERR("Unable To Write Data: %s", strerror(errno));
}
}
void setup_crash_report() {
// Setup Logging
setup_log_file();
// Store Output
int output_pipe[2];
safe_pipe2(output_pipe, 0);
int error_pipe[2];
safe_pipe2(error_pipe, 0);
int input_pipe[2];
safe_pipe2(input_pipe, 0);
// Fork
pid_t ret = fork();
if (ret == -1) {
ERR("Unable To Fork: %s", strerror(errno));
} else if (ret == 0) {
// Child Process
// Pipe stdio
dup2(output_pipe[PIPE_WRITE], STDOUT_FILENO);
close(output_pipe[PIPE_READ]);
close(output_pipe[PIPE_WRITE]);
dup2(error_pipe[PIPE_WRITE], STDERR_FILENO);
close(error_pipe[PIPE_READ]);
close(error_pipe[PIPE_WRITE]);
dup2(input_pipe[PIPE_READ], STDIN_FILENO);
close(input_pipe[PIPE_READ]);
close(input_pipe[PIPE_WRITE]);
// Create New Process Group
setpgid(0, 0);
// Kill Child If Parent Exits First
prctl(PR_SET_PDEATHSIG, SIGKILL);
// Continue Execution
} else {
// Install Signal Handlers
child_pid = ret;
struct sigaction act_sigint = {};
act_sigint.sa_flags = SA_RESTART;
act_sigint.sa_handler = &exit_handler;
sigaction(SIGINT, &act_sigint, nullptr);
struct sigaction act_sigterm = {};
act_sigterm.sa_flags = SA_RESTART;
act_sigterm.sa_handler = &exit_handler;
sigaction(SIGTERM, &act_sigterm, nullptr);
// Close Unneeded File Descriptors
close(output_pipe[PIPE_WRITE]);
close(error_pipe[PIPE_WRITE]);
close(input_pipe[PIPE_READ]);
// Set Debug Tag
reborn_debug_tag = "(Crash Reporter) ";
// Setup Polling
const int number_fds = 3;
pollfd poll_fds[number_fds];
poll_fds[0].fd = output_pipe[PIPE_READ];
poll_fds[1].fd = error_pipe[PIPE_READ];
poll_fds[2].fd = STDIN_FILENO;
for (pollfd &poll_fd : poll_fds) {
poll_fd.events = POLLIN;
}
// Poll Data
int status;
while (waitpid(ret, &status, WNOHANG) != ret) {
const int poll_ret = poll(poll_fds, number_fds, -1);
if (poll_ret == -1) {
if (errno == EINTR) {
continue;
} else {
ERR("Unable To Poll Data: %s", strerror(errno));
}
}
// Handle Data
for (pollfd &poll_fd : poll_fds) {
if (poll_fd.revents != 0) {
if (poll_fd.revents & POLLIN) {
char buf[BUFFER_SIZE];
if (poll_fd.fd == STDIN_FILENO) {
// Data Available From stdin
int bytes_available;
if (ioctl(fileno(stdin), FIONREAD, &bytes_available) == -1) {
bytes_available = 0;
}
// Read
const ssize_t bytes_read = read(poll_fd.fd, buf, BUFFER_SIZE);
if (bytes_read == -1) {
ERR("Unable To Read Input: %s", strerror(errno));
}
// Write To Child
safe_write(input_pipe[PIPE_WRITE], buf, bytes_read);
} else {
// Data Available From Child's stdout/stderr
const ssize_t bytes_read = read(poll_fd.fd, buf, BUFFER_SIZE);
if (bytes_read == -1) {
ERR("Unable To Read Log Data: %s", strerror(errno));
}
// Print To Terminal
safe_write(poll_fd.fd == output_pipe[PIPE_READ] ? STDOUT_FILENO : STDERR_FILENO, buf, bytes_read);
// Write To log
safe_write(reborn_get_log_fd(), buf, bytes_read);
}
} else {
// File Descriptor No Longer Accessible
poll_fd.events = 0;
}
}
}
}
// Close Pipes
close(output_pipe[PIPE_READ]);
close(error_pipe[PIPE_READ]);
close(input_pipe[PIPE_WRITE]);
// Check If Is Crash
const bool is_crash = !is_exit_status_success(status);
// Log Exit Code To log If Crash
if (is_crash) {
// Create Exit Code Log Line
char *exit_status = nullptr;
get_exit_status_string(status, &exit_status);
const std::string exit_code_line = "[CRASH]: Terminated" + std::string(exit_status) + '\n';
free(exit_status);
// Print Exit Code Log Line
safe_write(STDERR_FILENO, exit_code_line.c_str(), strlen(exit_code_line.c_str()));
// Write Exit Code Log Line
safe_write(reborn_get_log_fd(), exit_code_line.c_str(), strlen(exit_code_line.c_str()));
}
// Close Log File
close(log_fd);
unsetenv(_MCPI_LOG_FD_ENV);
// Show Crash Log
if (is_crash && !reborn_is_headless()) {
show_report(log_filename.c_str());
}
// Exit
exit(WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE);
}
}

View File

@ -1,11 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void setup_crash_report();
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,92 @@
#include <fstream>
#include <libreborn/util/util.h>
#include <libreborn/config.h>
#include <libreborn/util/exec.h>
#include "logger.h"
#include "../ui/frame.h"
// UI
struct CrashReport final : Frame {
explicit CrashReport(const char *filename): Frame("Crash Report", 640, 480) {
// Open File
std::ifstream stream(filename, std::ios::binary | std::ios::ate);
if (stream) {
// Read File
const std::streamoff size = stream.tellg();
stream.seekg(0, std::ifstream::beg);
log.resize(size);
stream.read(log.data(), size);
// Close File
stream.close();
}
}
bool first_render = true;
int render() override {
// Text
ImGui::TextWrapped("%s", MCPI_APP_TITLE " has crashed!");
ImGui::Spacing();
ImGui::TextWrapped("Need help? Consider asking on the Discord server!");
ImGui::Spacing();
ImGui::TextWrapped("If you believe this is a problem with " MCPI_APP_TITLE " itself, please upload this crash report to the #bugs Discord channel.");
// Log
if (ImGui::BeginChild("Log", ImVec2(0, -ImGui::GetFrameHeightWithSpacing() /* Leave Room For Bottom Row */), ImGuiChildFlags_Borders, ImGuiWindowFlags_HorizontalScrollbar)) {
ImGui::PushFont(monospace);
ImGui::TextUnformatted(log.data(), log.data() + log.size());
ImGui::PopFont();
if (first_render) {
ImGui::SetScrollHereY(1.0f);
first_render = false;
}
}
ImGui::EndChild();
// Buttons
if (ImGui::Button("Join Discord")) {
open_url(MCPI_DISCORD_INVITE);
}
ImGui::SameLine();
if (ImGui::Button("View All Logs")) {
open_url("file://" + get_logs_folder());
}
ImGui::SameLine();
// Right-Aligned
int ret = 0;
const std::string &log_ref = log;
draw_right_aligned_buttons({"Copy", quit_text}, [&ret, &log_ref](const int id, const bool was_clicked) {
if (was_clicked) {
if (id == 0) {
// Copy Log
ImGui::SetClipboardText(log_ref.c_str());
} else {
// Exit
ret = 1;
}
}
});
return ret;
}
std::string log;
};
// Show Crash Report Dialog
static void redirect_file(FILE *file, const char *mode) {
const FILE *ret = freopen("/dev/null", mode, file);
if (!ret) {
IMPOSSIBLE();
}
}
void show_report(const char *log_filename) {
// Fork
const pid_t pid = fork();
if (pid == 0) {
// Child
setsid();
redirect_file(stdout, "w");
redirect_file(stderr, "w");
redirect_file(stdin, "r");
CrashReport ui(log_filename);
ui.run();
exit(EXIT_SUCCESS);
}
}

View File

@ -0,0 +1,143 @@
#include <unistd.h>
#include <cstring>
#include <cerrno>
#include <cstdlib>
#include <cstdio>
#include <csignal>
#include <sys/stat.h>
#include <string>
#include <fcntl.h>
#include <libreborn/util/exec.h>
#include <libreborn/log.h>
#include <libreborn/util/util.h>
#include <libreborn/util/string.h>
#include <libreborn/util/io.h>
#include <libreborn/config.h>
#include "logger.h"
// Exit Handler
static pid_t child_pid = -1;
static void exit_handler(__attribute__((unused)) int signal) {
// Murder
kill(child_pid, SIGTERM);
}
// Log File
static std::string log_filename;
static int log_fd;
std::string get_logs_folder() {
const std::string home = home_get();
ensure_directory(home.c_str());
const std::string logs = home + "/logs";
ensure_directory(logs.c_str());
return logs;
}
static void setup_log_file() {
// Get Log Directory
const std::string logs = get_logs_folder();
// Get Timestamp
const std::string time = format_time("%Y-%m-%d");
// Get Log Filename
std::string file;
int num = 1;
do {
file = time + '-' + std::to_string(num) + ".log";
log_filename = logs + '/' + file;
num++;
} while (access(log_filename.c_str(), F_OK) != -1);
// Create latest.log Symlink
const std::string latest_log = logs + "/latest.log";
unlink(latest_log.c_str());
if (symlink(file.c_str(), latest_log.c_str()) != 0) {
WARN("Unable To Create Latest Log Symlink: %s", strerror(errno));
}
// Create File
log_fd = open(log_filename.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (log_fd == -1) {
ERR("Unable To Create Log File: %s", strerror(errno));
}
reborn_set_log(log_fd);
}
// Setup
void setup_logger() {
// Setup Logging
setup_log_file();
// Fork
std::optional<Process> child = fork_with_stdio();
if (!child) {
// Child Process
// Create New Process Group
setpgid(0, 0);
// Continue Execution
} else {
// Set Debug Tag
reborn_debug_tag = "(Logger) ";
DEBUG("Writing To: %s", log_filename.c_str());
// Install Signal Handlers
child_pid = child->pid;
struct sigaction act_sigint = {};
act_sigint.sa_flags = SA_RESTART;
act_sigint.sa_handler = &exit_handler;
sigaction(SIGINT, &act_sigint, nullptr);
struct sigaction act_sigterm = {};
act_sigterm.sa_flags = SA_RESTART;
act_sigterm.sa_handler = &exit_handler;
sigaction(SIGTERM, &act_sigterm, nullptr);
// Poll
poll_fds({child->fds[0], child->fds[1], STDIN_FILENO}, [&child](const int i, const size_t size, unsigned char *buf) {
if (i == 0 || i == 1) {
// stdout/stderr
// Print To Terminal
safe_write(i == 0 ? STDOUT_FILENO : STDERR_FILENO, buf, size);
// Write To log
safe_write(reborn_get_log_fd(), buf, size);
} else {
// stdin
// Write To Child
safe_write(child->fds[2], buf, size);
}
});
// Get Exit Status
const int status = child->close();
const bool is_crash = !is_exit_status_success(status);
// Log Exit Code To log If Crash
if (is_crash) {
// Create Exit Code Log Line
const std::string exit_status = get_exit_status_string(status);
const std::string exit_code_line = "[CRASH]: Terminated" + exit_status + '\n';
// Print Exit Code Log Line
safe_write(STDERR_FILENO, exit_code_line.c_str(), exit_code_line.size());
// Write Exit Code Log Line
safe_write(reborn_get_log_fd(), exit_code_line.c_str(), exit_code_line.size());
}
// Close Log File
close(log_fd);
reborn_set_log(-1);
// Show Crash Log
if (is_crash && !reborn_is_headless()) {
show_report(log_filename.c_str());
}
// Exit
exit(WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE);
}
}

View File

@ -0,0 +1,7 @@
#pragma once
#include <string>
std::string get_logs_folder();
void setup_logger();
void show_report(const char *log_filename);

View File

@ -1,12 +1,15 @@
#include <cstdlib>
#include <libreborn/libreborn.h>
#include <sys/stat.h>
#include "bootstrap.h"
#include <libreborn/env/env.h>
#include <libreborn/util/util.h>
#include <libreborn/config.h>
#include "bootstrap/bootstrap.h"
#include "options/parser.h"
#include "crash-report.h"
#include "util.h"
#include "logger/logger.h"
#include "util/util.h"
#include "client/configuration.h"
#include "updater/updater.h"
// Bind Options To Environmental Variable
static void bind_to_env(const char *env, const bool value) {
@ -25,56 +28,43 @@ static void setup_environment(const options_t &options) {
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
set_and_print_env("GTK_THEME", "Adwaita:dark");
// Configure PATH
{
// Get Binary Directory
const std::string binary_directory = get_binary_directory();
std::string new_path = binary_directory + "/bin";
// Add Existing PATH
{
const char *value = getenv("PATH");
if (value != nullptr && strlen(value) > 0) {
new_path += std::string(":") + value;
}
}
// Set And Free
set_and_print_env("PATH", new_path.c_str());
}
setup_path();
// Setup MCPI_HOME
if (const char *custom_profile_directory = getenv(MCPI_PROFILE_DIRECTORY_ENV); custom_profile_directory != nullptr) {
// Custom Directory
custom_profile_directory = realpath(custom_profile_directory, nullptr);
ALLOC_CHECK(custom_profile_directory);
set_and_print_env(_MCPI_HOME_ENV, custom_profile_directory);
free((void *) custom_profile_directory);
} else if (!reborn_is_server()) {
// Ensure $HOME
const char *home = getenv("HOME");
if (home == nullptr) {
ERR("$HOME Is Not Set");
}
set_and_print_env(_MCPI_HOME_ENV, home);
} else {
// Set Home To Current Directory, So World Data Is Stored There
char *launch_directory = getcwd(nullptr, 0);
ALLOC_CHECK(launch_directory);
set_and_print_env(_MCPI_HOME_ENV, launch_directory);
free(launch_directory);
}
setup_home();
// Create If Needed
const std::string minecraft_folder = std::string(getenv(_MCPI_HOME_ENV)) + get_home_subdirectory_for_game_data();
const std::string minecraft_folder = home_get();
ensure_directory(minecraft_folder.c_str());
}
// Non-Launch Commands
static void handle_non_launch_commands(const options_t &options) {
// SDK
if (options.copy_sdk) {
const std::string binary_directory = get_binary_directory();
copy_sdk(binary_directory, false);
copy_sdk(binary_directory, true);
fflush(stdout);
exit(EXIT_SUCCESS);
}
// Updater
if (options.run_update) {
Updater *updater = Updater::instance;
if (updater) {
updater->update();
if (updater->status == ERROR) {
ERR("Unable To Update");
} else if (updater->status == UP_TO_DATE) {
INFO("Already Up-To-Date");
} else {
if (updater->status != RESTART_NEEDED) {
IMPOSSIBLE();
}
INFO("Update Completed");
}
} else {
ERR("Built-In Updater Unavailable, Use System Package Manager");
}
fflush(stdout);
exit(EXIT_SUCCESS);
}
@ -85,30 +75,30 @@ static void start_game(const options_t &options) {
// Disable stdout Buffering
setvbuf(stdout, nullptr, _IONBF, 0);
// Setup Crash Reporting
if (!options.disable_crash_report) {
setup_crash_report();
}
// Configure Client Options
if (!reborn_is_server()) {
configure_client(options);
}
// Start Logging
if (!options.disable_logger) {
setup_logger();
}
// Bootstrap
bootstrap(options);
}
// Main
int main(int argc, char *argv[]) {
int main(const int argc, char *argv[]) {
// Parse Options
options_t options = parse_options(argc, argv);
const options_t options = parse_options(argc, argv);
// Set Debug Tag
reborn_debug_tag = "(Launcher) ";
// Debug Logging
unsetenv(_MCPI_LOG_FD_ENV);
reborn_set_log(-1);
bind_to_env(MCPI_DEBUG_ENV, options.debug);
// Setup Environment

View File

@ -1,6 +1,6 @@
OPTION(debug, "debug", 'd', "Enable Debug Logging")
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_logger, "disable-logger", -1, "Disable Logger (And Crash Report Dialog)")
OPTION(use_default, "default", -3, "Skip Client-Mode Configuration Dialogs")
OPTION(no_cache, "no-cache", -4, "Disable Client-Mode Configuration Cache")
OPTION(wipe_cache, "wipe-cache", -5, "Wipe Cached Client-Mode Configuration And Exit")
@ -10,4 +10,5 @@ OPTION(only_generate, "only-generate", -8, "Generate World And Exit (Server-Mode
OPTION(force_headless, "force-headless", -9, "Force Disable Game Rendering")
OPTION(force_non_headless, "force-non-headless", -10, "Force Enable Game Rendering")
OPTION(server_mode, "server", -11, "Run In Server-Mode")
OPTION(skip_pagesize_check, "skip-pagesize-check", -12, "Skip Page-Size Check (Not Recommended)")
OPTION(skip_pagesize_check, "skip-pagesize-check", -12, "Skip Page-Size Check (Not Recommended)")
OPTION(run_update, "update", -13, "Run Updater (If Available)")

View File

@ -1,5 +1,9 @@
#include <argp.h>
#include <libreborn/config.h>
#include <libreborn/env/env.h>
#include <trampoline/types.h>
#include "parser.h"
// Globals
@ -16,7 +20,10 @@ static argp_option options_data[] = {
#undef OPTION
{nullptr, 0, nullptr, 0, "Environmental Variables:", 0},
#define ENV(name, doc) {#name, env_key--, nullptr, OPTION_DOC | OPTION_NO_USAGE | (is_env_var_internal(name##_ENV) ? OPTION_HIDDEN : 0), doc, 0},
#include <libreborn/env-list.h>
#include <libreborn/env/list.h>
#ifdef MCPI_BUILD_RUNTIME
#include <trampoline/env-list.h>
#endif
#undef ENV
{nullptr, 0, nullptr, 0, "Help Options:", -1},
{nullptr, 0, nullptr, 0, nullptr, 0}
@ -27,7 +34,7 @@ static argp_option options_data[] = {
case key: \
options->name = true; \
break;
static error_t parse_opt(int key, __attribute__((unused)) char *arg, argp_state *state) {
static error_t parse_opt(const int key, __attribute__((unused)) char *arg, argp_state *state) {
options_t *options = (options_t *) state->input;
switch (key) {
#include "option-list.h"
@ -38,7 +45,7 @@ static error_t parse_opt(int key, __attribute__((unused)) char *arg, argp_state
}
#undef OPTION
static argp argp = {options_data, parse_opt, nullptr, doc, nullptr, nullptr, nullptr};
options_t parse_options(int argc, char *argv[]) {
options_t parse_options(const int argc, char *argv[]) {
options_t options = {};
argp_parse(&argp, argc, argv, 0, nullptr, &options);
return options;

View File

@ -1,7 +1,5 @@
#pragma once
#include <libreborn/libreborn.h>
#define OPTION(name, ...) bool name;
struct options_t {
#include "option-list.h"

View File

@ -1,8 +0,0 @@
#pragma once
#include <string>
#include <vector>
#define MCPI_PATCHED_DIR "/tmp/.minecraft-pi-patched"
void patch_mcpi_elf_dependencies(const std::string &original_path, char *new_path, const std::string &interpreter, const std::vector<std::string> &rpath, const std::vector<std::string> &mods);

View File

@ -1,59 +0,0 @@
#include <libreborn/libreborn.h>
#include "bootstrap.h"
#include "util.h"
// Log
#define LOG(is_debug, ...) \
{ \
if (is_debug) { \
DEBUG(__VA_ARGS__); \
} else { \
INFO(__VA_ARGS__); \
} \
}
// Copy SDK Into ~/.minecraft-pi
#define HOME_SUBDIRECTORY_FOR_SDK (std::string(get_home_subdirectory_for_game_data()) + "/sdk")
void copy_sdk(const std::string &binary_directory, const bool log_with_debug) {
// Ensure SDK Directory
std::string sdk_path;
{
sdk_path = std::string(getenv(_MCPI_HOME_ENV)) + HOME_SUBDIRECTORY_FOR_SDK;
const char *const command[] = {"mkdir", "-p", sdk_path.c_str(), nullptr};
run_simple_command(command, "Unable To Create SDK Directory");
}
// Lock File
const std::string lock_file_path = sdk_path + "/.lock";
const int lock_file_fd = lock_file(lock_file_path.c_str());
// Output Directory
const std::string output = sdk_path + "/" MCPI_SDK_DIR;
// Source Directory
const std::string source = binary_directory + "/sdk/.";
// Clean
{
const char *const command[] = {"rm", "-rf", output.c_str(), nullptr};
run_simple_command(command, "Unable To Clean SDK Output Directory");
}
// Make Directory
{
const char *const command[] = {"mkdir", "-p", output.c_str(), nullptr};
run_simple_command(command, "Unable To Create SDK Output Directory");
}
// Copy
{
const char *const command[] = {"cp", "-ar", source.c_str(), output.c_str(), nullptr};
run_simple_command(command, "Unable To Copy SDK");
}
// Log
LOG(log_with_debug, "Copied SDK To: %s", output.c_str());
// Unlock File
unlock_file(lock_file_path.c_str(), lock_file_fd);
}

83
launcher/src/ui/color.cpp Normal file
View File

@ -0,0 +1,83 @@
#include "frame.h"
// Calculate Perceived Brightness Of Color
// See: https://en.wikipedia.org/w/index.php?title=Relative_luminance&useskin=vector
static float compute_luma(const ImVec4 &color) {
return (0.2126f * color.x) + (0.7152f * color.y) + (0.0722f * color.z);
}
// Apply Luma To RGB
static float clamp(const float value) {
return std::max(0.0f, std::min(value, 1.0f));
}
static ImVec4 apply_luma_to_color(const float target_luma, const ImVec4 &color) {
const float current_luma = compute_luma(color);
const float luma_ratio = (current_luma != 0) ? target_luma / current_luma : 0;
ImVec4 out = color;
for (float *x : {&out.x, &out.y, &out.z}) {
*x = clamp(*x * luma_ratio);
}
return out;
}
// Blend Color
static ImVec4 blend_color(const ImVec4 &top, const ImVec4 &bottom) {
const float luma = compute_luma(bottom);
ImVec4 out = apply_luma_to_color(luma, top);
out.w = bottom.w;
return out;
}
static ImVec4 blend_with_primary(const ImVec4 &color) {
static constexpr ImVec4 primary_color = {1.0f, (69.0f / 255.0f), 0.0f, 1.0f};
return blend_color(primary_color, color);
}
// Modify Colors
void Frame::patch_colors(ImGuiStyle &style) {
// Blend Colors
static int target_colors_blend[] = {
ImGuiCol_FrameBg,
ImGuiCol_FrameBgHovered,
ImGuiCol_FrameBgActive,
ImGuiCol_TitleBgActive,
ImGuiCol_CheckMark,
ImGuiCol_SliderGrab,
ImGuiCol_SliderGrabActive,
ImGuiCol_Button,
ImGuiCol_ButtonHovered,
ImGuiCol_ButtonActive,
ImGuiCol_Header,
ImGuiCol_HeaderHovered,
ImGuiCol_HeaderActive,
ImGuiCol_SeparatorHovered,
ImGuiCol_SeparatorActive,
ImGuiCol_ResizeGrip,
ImGuiCol_ResizeGripHovered,
ImGuiCol_ResizeGripActive,
ImGuiCol_TabHovered,
ImGuiCol_Tab,
ImGuiCol_TabSelected,
ImGuiCol_TabSelectedOverline,
ImGuiCol_TabDimmed,
ImGuiCol_TabDimmedSelected,
ImGuiCol_TextLink,
ImGuiCol_TextSelectedBg,
ImGuiCol_NavCursor
};
for (const int target_color : target_colors_blend) {
ImVec4 &color = style.Colors[target_color];
color = blend_with_primary(color);
}
// Remove Blue Accent From Colors
static int target_colors_modify[] = {
ImGuiCol_Separator,
ImGuiCol_Border,
ImGuiCol_TableHeaderBg,
ImGuiCol_TableBorderStrong,
ImGuiCol_TableBorderLight
};
for (const int target_color : target_colors_modify) {
ImVec4 &color = style.Colors[target_color];
color.y = color.z = color.x;
}
}

135
launcher/src/ui/frame.cpp Normal file
View File

@ -0,0 +1,135 @@
#include <cmath>
#include "frame.h"
#include <imgui_impl_glfw.h>
#include <imgui_impl_opengl2.h>
#include <libreborn/log.h>
#include <libreborn/util/glfw.h>
#include <libreborn/util/util.h>
// Init/Cleanup
Frame::Frame(const char *title, const int width, const int height) {
// Create Window
init_glfw();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 1);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
window = create_glfw_window(title, width, height);
// Load OpenGL
if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) {
ERR("Unable To Load GLAD");
}
// Disable V-Sync
// (On Wayland, This Fixes Issues With The Clipboard)
glfwSwapInterval(0);
// Setup ImGui Context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO &io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
io.IniFilename = nullptr;
io.LogFilename = nullptr;
// Setup Platform/Renderer Backends
ImGui_ImplGlfw_InitForOpenGL(window, true);
ImGui_ImplOpenGL2_Init();
}
Frame::~Frame() {
// Shutdown ImGui
ImGui_ImplOpenGL2_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
// Cleanup GLFW
cleanup_glfw(window);
}
// Run Loop
int Frame::run() {
int ret = 0;
while (!glfwWindowShouldClose(window) && ret == 0) {
glfwPollEvents();
// Update Style
static float last_scale = -1.0f;
float scale;
get_glfw_scale(window, &scale, nullptr);
if (scale != last_scale) {
last_scale = scale;
setup_style(scale);
}
// Start Frame
ImGui_ImplOpenGL2_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
// Main Window
ImGui::SetNextWindowPos({0, 0});
int width, height;
glfwGetFramebufferSize(window, &width, &height);
ImGui::SetNextWindowSize({float(width), float(height)});
if (ImGui::Begin("##Frame", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoScrollWithMouse)) {
ret = render();
}
ImGui::End();
// Render To OpenGL
ImGui::Render();
ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData());
glfwSwapBuffers(window);
}
return ret;
}
// Style
EMBEDDED_RESOURCE(Roboto_Medium_ttf);
EMBEDDED_RESOURCE(Cousine_Regular_ttf);
void Frame::setup_style(const float scale) {
// Fonts
const ImGuiIO &io = ImGui::GetIO();
io.Fonts->Clear();
ImFontConfig font_cfg;
font_cfg.FontDataOwnedByAtlas = false;
io.Fonts->AddFontFromMemoryTTF(Roboto_Medium_ttf, int(Roboto_Medium_ttf_len), std::floor(20.0f * scale), &font_cfg);
monospace = io.Fonts->AddFontFromMemoryTTF(Cousine_Regular_ttf, int(Cousine_Regular_ttf_len), std::floor(18.0f * scale), &font_cfg);
ImGui_ImplOpenGL2_DestroyFontsTexture();
// Style
ImGuiStyle &style = ImGui::GetStyle();
style = ImGuiStyle();
style.WindowBorderSize = 0;
ImGui::StyleColorsDark(&style);
style.ScaleAllSizes(scale);
patch_colors(style);
}
// Right-Aligned Buttons
float Frame::get_frame_width(const char *str) {
const ImGuiStyle &style = ImGui::GetStyle();
return ImGui::CalcTextSize(str).x + style.FramePadding.x * 2.0f;
}
void Frame::draw_right_aligned_buttons(const std::vector<const char *> &buttons, const std::function<void(int, bool)> &callback, const bool should_actually_center) {
// Calculate Position
const ImGuiStyle &style = ImGui::GetStyle();
float width_needed = 0;
for (const char *text : buttons) {
if (width_needed > 0) {
width_needed += style.ItemSpacing.x;
}
width_needed += get_frame_width(text);
}
float cursor_pos;
if (should_actually_center) {
cursor_pos = ImGui::GetWindowSize().x - width_needed;
cursor_pos /= 2.0f;
} else {
cursor_pos = ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - width_needed;
}
ImGui::SetCursorPosX(cursor_pos);
// Draw
for (std::vector<const char *>::size_type id = 0; id < buttons.size(); id++) {
if (id > 0) {
ImGui::SameLine();
}
callback(int(id), ImGui::Button(buttons[id]));
}
}

32
launcher/src/ui/frame.h Normal file
View File

@ -0,0 +1,32 @@
#pragma once
#include <vector>
#include <functional>
#include <imgui.h>
#include <GLFW/glfw3.h>
// UI Frame
struct Frame {
Frame(const char *title, int width, int height);
virtual ~Frame();
// Prevent Copying
Frame(const Frame &) = delete;
Frame &operator=(const Frame &) = delete;
// Run
int run();
virtual int render() = 0;
protected:
// API For Sub-Classes
ImFont *monospace = nullptr;
static float get_frame_width(const char *str);
static void draw_right_aligned_buttons(const std::vector<const char *> &buttons, const std::function<void(int, bool)> &callback, bool should_actually_center = false);
static constexpr const char *quit_text = "Quit";
private:
// Properties
GLFWwindow *window = nullptr;
// Internal Methods
float get_scale();
void setup_style(float scale);
static void patch_colors(ImGuiStyle &style);
};

View File

@ -0,0 +1,122 @@
#include <cstdlib>
#include <optional>
#include <utility>
#include <unistd.h>
#include <sys/stat.h>
#include <libreborn/log.h>
#include <libreborn/util/exec.h>
#include <libreborn/config.h>
#include "updater.h"
// Implement
struct AppImageUpdater final : Updater {
void update() override;
void restart() override;
static AppImageUpdater instance;
};
AppImageUpdater AppImageUpdater::instance;
// Update
template <typename... Args>
static std::optional<std::string> run_wget(Args... args) {
int status = 0;
const char *const command[] = {"wget", "-O", std::forward<Args>(args)..., nullptr};
const std::vector<unsigned char> *output = run_command(command, &status);
std::string output_str = (const char *) output->data();
delete output;
if (!is_exit_status_success(status)) {
return std::nullopt;
} else {
return output_str;
}
}
static std::string extract_from_json(const std::string &json_str, const std::string &key) {
std::string::size_type pos = json_str.find(key);
std::array<std::string::size_type, 3> indices = {};
unsigned int i = 0;
while (true) {
if (pos == std::string::npos) {
return "";
}
if (i >= indices.size()) {
break;
}
pos = json_str.find('"', pos) + 1;
indices[i++] = pos;
}
const std::string::size_type start = indices[1];
const std::string::size_type end = indices[2];
return json_str.substr(start, end - start - 1);
}
static const char *get_appimage_path() {
const char *path = getenv("APPIMAGE");
if (path == nullptr) {
IMPOSSIBLE();
}
return path;
}
void AppImageUpdater::update() {
// Check
if (status != CHECKING) {
IMPOSSIBLE();
}
const std::optional<std::string> json = run_wget("-", MCPI_APPIMAGE_JSON_URL);
if (!json.has_value()) {
status = ERROR;
return;
}
const std::string tag_name = extract_from_json(json.value(), "tag_name");
// Check Version
if (tag_name == MCPI_VERSION) {
status = UP_TO_DATE;
return;
}
// Get URL
std::string url = MCPI_APPIMAGE_DOWNLOAD_URL;
while (true) {
const std::string placeholder = MCPI_APPIMAGE_VERSION_PLACEHOLDER;
const std::string::size_type pos = url.find(placeholder);
if (pos == std::string::npos) {
break;
}
url.replace(pos, placeholder.size(), tag_name);
}
// Get Path
const char *appimage_path = get_appimage_path();
const std::string new_appimage_path_base = std::string(appimage_path) + ".new";
std::string new_appimage_path = new_appimage_path_base;
int num = 1;
while (access(new_appimage_path.c_str(), F_OK) != -1) {
new_appimage_path = new_appimage_path_base + '.' + std::to_string(num++);
}
// Download
status = DOWNLOADING;
const std::optional<std::string> out = run_wget(new_appimage_path.c_str(), url.c_str());
bool ret = out.has_value();
if (ret) {
ret = chmod(new_appimage_path.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0;
}
if (ret) {
ret = rename(new_appimage_path.c_str(), appimage_path) == 0;
}
if (!ret) {
unlink(new_appimage_path.c_str());
status = ERROR;
return;
}
// Done
status = RESTART_NEEDED;
}
// Restart
void AppImageUpdater::restart() {
const char *const command[] = {get_appimage_path(), nullptr};
safe_execvpe(command, environ);
}

View File

@ -0,0 +1,48 @@
#include <pthread.h>
#include <libreborn/log.h>
#include "updater.h"
// Instance
Updater *Updater::instance = nullptr;
Updater::Updater() {
instance = this;
}
// Check Status
bool Updater::can_start() const {
return status == NOT_STARTED || status == RESTART_NEEDED;
}
std::string Updater::get_status() const {
switch (status) {
case NOT_STARTED: return "Update";
case RESTART_NEEDED: return "Restart!";
case CHECKING: return "Checking...";
case UP_TO_DATE: return "Up-To-Date";
case DOWNLOADING: return "Downloading...";
case ERROR: return "Error";
default: return "";
}
}
// Run
static void *update_thread(void *data) {
Updater *updater = (Updater *) data;
updater->update();
return nullptr;
}
void Updater::start() {
switch (status) {
case NOT_STARTED: {
status = CHECKING;
pthread_t thread;
pthread_create(&thread, nullptr, update_thread, this);
break;
}
case RESTART_NEEDED: {
restart();
break;
}
default: IMPOSSIBLE();
}
}

View File

@ -0,0 +1,31 @@
#pragma once
#include <string>
// Update Status
enum UpdateStatus {
NOT_STARTED,
CHECKING,
UP_TO_DATE,
DOWNLOADING,
RESTART_NEEDED,
ERROR
};
// Updater
struct Updater {
// Instance
static Updater *instance;
// Constructor
Updater();
virtual ~Updater() = default;
// Implementation
virtual void update() = 0;
virtual void restart() = 0;
// Methods
[[nodiscard]] std::string get_status() const;
[[nodiscard]] bool can_start() const;
void start();
// Properties
UpdateStatus status = NOT_STARTED;
};

View File

@ -1,43 +0,0 @@
#include <libreborn/libreborn.h>
#include "util.h"
// Simpler Version Of run_command()
void run_simple_command(const char *const command[], const char *error) {
int status = 0;
char *output = run_command(command, &status, nullptr);
if (output != nullptr) {
free(output);
}
if (!is_exit_status_success(status)) {
ERR("%s", error);
}
}
// Chop Off Last Component
void chop_last_component(std::string &str) {
const std::string::size_type pos = str.find_last_of('/');
if (pos == std::string::npos) {
return;
}
str = str.substr(0, pos);
}
// Get Binary Directory (Remember To Free)
std::string safe_realpath(const std::string &path) {
char *raw = realpath(path.c_str(), nullptr);
if (raw == nullptr) {
ERR("Unable To Resolve: %s", path.c_str());
}
std::string str = raw;
free(raw);
return str;
}
std::string get_binary_directory() {
// Get Path To Current Executable
std::string exe = safe_realpath("/proc/self/exe");
// Chop Off Last Component
chop_last_component(exe);
// Return
return exe;
}

View File

@ -1,9 +0,0 @@
#pragma once
#include <string>
void run_simple_command(const char *const command[], const char *error);
void chop_last_component(std::string &str);
std::string safe_realpath(const std::string &path);
std::string get_binary_directory();

52
launcher/src/util/env.cpp Normal file
View File

@ -0,0 +1,52 @@
#include <cstring>
#include "util.h"
#include <libreborn/log.h>
#include <libreborn/env/env.h>
#include <libreborn/config.h>
// $PATH
void setup_path() {
// Get Binary Directory
const std::string binary_directory = get_binary_directory();
std::string new_path = binary_directory + "/bin";
// Add Existing PATH
const char *value = getenv("PATH");
if (value != nullptr && strlen(value) > 0) {
new_path += std::string(":") + value;
}
// Set And Free
set_and_print_env("PATH", new_path.c_str());
}
// Profile Directory
void setup_home() {
const char *custom_profile_directory = getenv(MCPI_PROFILE_DIRECTORY_ENV);
std::string home;
if (custom_profile_directory != nullptr) {
// Custom Directory
home = safe_realpath(custom_profile_directory);
} else if (!reborn_is_server()) {
// Ensure $HOME
const char *value = getenv("HOME");
if (value == nullptr) {
ERR("$HOME Is Not Set");
}
home = value;
// Flatpak
#ifdef MCPI_IS_FLATPAK_BUILD
home += "/.var/app/" MCPI_APP_ID;
#endif
} else {
// Set Home To Current Directory, So World Data Is Stored There
char *launch_directory = getcwd(nullptr, 0);
if (launch_directory == nullptr) {
IMPOSSIBLE();
}
home = launch_directory;
free(launch_directory);
}
// Set
set_and_print_env(_MCPI_HOME_ENV, home.c_str());
}

167
launcher/src/util/sdk.cpp Normal file
View File

@ -0,0 +1,167 @@
#include <optional>
#include <fstream>
#include <sstream>
#include <sys/stat.h>
#include <dirent.h>
#include <cstring>
#include <libreborn/log.h>
#include <libreborn/util/util.h>
#include <libreborn/util/io.h>
#include <libreborn/config.h>
#include "util.h"
// Utility Functions
static constexpr char path_separator = '/';
static void make_directory(std::string path /* Must Be Absolute */) {
path += path_separator;
std::stringstream stream(path);
path = "";
std::string path_segment;
while (std::getline(stream, path_segment, path_separator)) {
path += path_segment;
ensure_directory(path.c_str());
path += path_separator;
}
}
static void delete_recursively(const std::string &path, const bool allow_nonexistent_dir) {
// Loop Through Children
const bool success = read_directory(path, [&path](const dirent *entry) {
// Handle
const std::string child = path + path_separator + entry->d_name;
if (entry->d_type == DT_DIR) {
delete_recursively(child, false);
} else if (unlink(child.c_str()) != 0) {
ERR("Unable To Delete File: %s: %s", child.c_str(), strerror(errno));
}
}, allow_nonexistent_dir);
// Delete
if (success && rmdir(path.c_str()) != 0) {
ERR("Unable To Delete Directory: %s: %s", path.c_str(), strerror(errno));
}
}
static void copy_file(const std::string &src, const std::string &dst) {
std::ifstream in(src, std::ios::binary);
if (!in) {
ERR("Unable To Open Source File: %s", src.c_str());
}
std::ofstream out(dst, std::ios::binary);
if (!out) {
ERR("Unable To Create Destination File: %s", dst.c_str());
}
out << in.rdbuf();
out.close();
in.close();
}
static void copy_directory(const std::string &src, const std::string &dst) {
read_directory(src, [&src, &dst](const dirent *entry) {
const std::string name = path_separator + std::string(entry->d_name);
const std::string in = src + name;
const std::string out = dst + name;
if (entry->d_type == DT_DIR) {
ensure_directory(out.c_str());
copy_directory(in, out);
} else {
copy_file(in, out);
}
});
}
// Path
static std::string get_sdk_root(const std::string &home) {
return home + path_separator + "sdk";
}
static std::string get_sdk_path_home() {
return get_sdk_root(home_get()) + path_separator + MCPI_SDK_DIR;
}
static std::string get_sdk_path_bundled(const std::string &binary_directory) {
return get_sdk_root(binary_directory);
}
// Test Whether SDK Should Be Copied
static std::optional<std::string> get_sdk_hash(const std::string &sdk) {
const std::string path = sdk + path_separator + ".hash";
// Open File
std::ifstream stream(path, std::ios::binary | std::ios::ate);
if (stream) {
std::string hash;
// Read File
const std::streamoff size = stream.tellg();
stream.seekg(0, std::ifstream::beg);
hash.resize(size);
stream.read(hash.data(), size);
// Close File
stream.close();
// Return
return hash;
} else {
// Unable To Read
return std::nullopt;
}
}
static bool should_copy_sdk(const std::string &binary_directory) {
// Read Hashes
const std::optional<std::string> home_hash = get_sdk_hash(get_sdk_path_home());
if (!home_hash.has_value()) {
return true;
}
const std::optional<std::string> bundled_hash = get_sdk_hash(get_sdk_path_bundled(binary_directory));
if (!home_hash.has_value()) {
IMPOSSIBLE();
}
const bool should_copy = home_hash.value() != bundled_hash.value();
if (!should_copy) {
DEBUG("Skipped Unnecessary SDK Copy");
}
return should_copy;
}
// Log
#define LOG(is_debug, ...) \
({ \
if ((is_debug)) { \
DEBUG(__VA_ARGS__); \
} else { \
INFO(__VA_ARGS__); \
} \
})
// Copy SDK Into ~/.minecraft-pi
static void do_copy_sdk(const std::string &binary_directory, const bool force) {
// Check If Copy Is Needed
bool should_copy = force;
if (!should_copy) {
should_copy = should_copy_sdk(binary_directory);
}
if (!should_copy) {
return;
}
// Get Paths
const std::string src_sdk = get_sdk_path_bundled(binary_directory);
const std::string dst_sdk = get_sdk_path_home();
// Create Output Directory
delete_recursively(dst_sdk, true);
make_directory(dst_sdk);
// Copy Directory
copy_directory(src_sdk, dst_sdk);
// Log
LOG(!force, "Copied SDK To: %s", dst_sdk.c_str());
}
void copy_sdk(const std::string &binary_directory, const bool force) {
// Lock File
const std::string root = get_sdk_root(home_get());
ensure_directory(root.c_str());
const std::string lock_file_path = root + path_separator + ".lock";
const int lock_file_fd = lock_file(lock_file_path.c_str());
// Do
do_copy_sdk(binary_directory, force);
// Unlock File
unlock_file(lock_file_path.c_str(), lock_file_fd);
}

View File

@ -0,0 +1,60 @@
#include <dirent.h>
#include <cstring>
#include <libreborn/log.h>
#include <libreborn/util/exec.h>
#include "util.h"
// Chop Off Last Component
void chop_last_component(std::string &str) {
const std::string::size_type pos = str.find_last_of('/');
if (pos == std::string::npos) {
return;
}
str = str.substr(0, pos);
}
// Get Binary Directory (Remember To Free)
std::string safe_realpath(const std::string &path) {
char *raw = realpath(path.c_str(), nullptr);
if (raw == nullptr) {
ERR("Unable To Resolve: %s", path.c_str());
}
std::string str = raw;
free(raw);
return str;
}
std::string get_binary_directory() {
// Get Path To Current Executable
std::string exe = safe_realpath("/proc/self/exe");
// Chop Off Last Component
chop_last_component(exe);
// Return
return exe;
}
// Read Directory
bool read_directory(const std::string &path, const std::function<void(const dirent *)> &callback, const bool allow_nonexistent_dir) {
// Open Directory
DIR *dp = opendir(path.c_str());
if (dp == nullptr) {
if (allow_nonexistent_dir) {
return false;
}
ERR("Unable To Open Directory: %s: %s", path.c_str(), strerror(errno));
}
// Read
const dirent *entry;
while ((entry = readdir(dp)) != nullptr) {
// Block Pseudo-Directories
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
// Run
callback(entry);
}
// Close
closedir(dp);
return true;
}

15
launcher/src/util/util.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <string>
#include <functional>
void chop_last_component(std::string &str);
std::string safe_realpath(const std::string &path);
std::string get_binary_directory();
void copy_sdk(const std::string &binary_directory, bool force);
void setup_path();
void setup_home();
bool read_directory(const std::string &path, const std::function<void(const struct dirent *)> &callback, bool allow_nonexistent_dir = false);

View File

@ -6,29 +6,29 @@ configure_file(include/libreborn/config.h.in "${CMAKE_CURRENT_BINARY_DIR}/includ
# Util
add_library(reborn-util SHARED
src/util/exec.c
src/util/string.c
src/util/util.c
src/util/log.c
src/util/exec.cpp
src/util/string.cpp
src/util/util.cpp
src/util/log.cpp
src/util/cp437.cpp
src/util/env.c
)
target_include_directories(
reborn-util
PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>"
"$<INSTALL_INTERFACE:${MCPI_SDK_INCLUDE_DIR}/libreborn>"
src/util/env/env.cpp
src/util/config.cpp
src/util/env/flags/node.cpp
src/util/env/flags/flags.cpp
src/util/env/flags/available-feature-flags # Show In IDE
src/util/env/servers.cpp
)
embed_resource(reborn-util src/util/env/flags/available-feature-flags)
target_link_libraries(reborn-util PRIVATE utf8cpp)
# Install
install(TARGETS reborn-util DESTINATION "${MCPI_LIB_DIR}")
# SDK
if(BUILD_ARM_COMPONENTS)
install(TARGETS reborn-util EXPORT sdk DESTINATION "${MCPI_SDK_LIB_DIR}")
install(DIRECTORY "include/" DESTINATION "${MCPI_SDK_INCLUDE_DIR}/libreborn")
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include/" DESTINATION "${MCPI_SDK_INCLUDE_DIR}/libreborn")
if(TARGET glfw)
target_sources(reborn-util PRIVATE src/util/glfw.cpp)
target_link_libraries(reborn-util PRIVATE glfw)
endif()
setup_header_dirs(reborn-util
"${CMAKE_CURRENT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_BINARY_DIR}/include"
)
setup_library(reborn-util TRUE TRUE)
# Patch
if(BUILD_ARM_COMPONENTS)
@ -39,11 +39,9 @@ if(BUILD_ARM_COMPONENTS)
src/patch/instruction.cpp
)
target_link_libraries(reborn-patch dl pthread reborn-util)
target_compile_definitions(reborn-patch PUBLIC -DREBORN_HAS_PATCH_CODE)
target_compile_definitions(reborn-patch PUBLIC REBORN_HAS_PATCH_CODE)
# Install
install(TARGETS reborn-patch DESTINATION "${MCPI_LIB_DIR}")
# SDK
install(TARGETS reborn-patch EXPORT sdk DESTINATION "${MCPI_SDK_LIB_DIR}")
setup_library(reborn-patch TRUE TRUE)
endif()
# Fake LibPNG To Satisfy Symbol Versioning Requirement
@ -55,5 +53,5 @@ if(BUILD_ARM_COMPONENTS)
)
target_link_options(fake-libpng PRIVATE "LINKER:--version-script=${CMAKE_CURRENT_SOURCE_DIR}/src/fake-libpng/empty.vers")
# Install
install(TARGETS fake-libpng DESTINATION "${MCPI_LIB_DIR}")
setup_library(fake-libpng TRUE FALSE)
endif()

View File

@ -1,14 +1,39 @@
#pragma once
#cmakedefine MCPI_IS_APPIMAGE_BUILD
#cmakedefine MCPI_IS_FLATPAK_BUILD
#cmakedefine MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
#cmakedefine MCPI_APP_BASE_TITLE "@MCPI_APP_BASE_TITLE@"
// General
#cmakedefine MCPI_VERSION "@MCPI_VERSION@"
#cmakedefine MCPI_AUTHOR "@MCPI_AUTHOR@"
#cmakedefine MCPI_ARCH "@MCPI_ARCH@"
// App Information
#cmakedefine MCPI_APP_TITLE "@MCPI_APP_TITLE@"
#cmakedefine MCPI_APP_ID "@MCPI_APP_ID@"
#cmakedefine MCPI_VERSION "@MCPI_VERSION@"
#cmakedefine MCPI_VARIANT_NAME "@MCPI_VARIANT_NAME@"
#cmakedefine MCPI_SDK_DIR "@MCPI_SDK_DIR@"
#cmakedefine MCPI_APP_NAME "@MCPI_APP_NAME@"
// Extra Options
#cmakedefine MCPI_SKIN_SERVER "@MCPI_SKIN_SERVER@"
#cmakedefine MCPI_DISCORD_INVITE "@MCPI_DISCORD_INVITE@"
#cmakedefine MCPI_DOCUMENTATION "@MCPI_DOCUMENTATION@"
#cmakedefine MCPI_REPO "@MCPI_REPO@"
// Documentation
#cmakedefine MCPI_DOCS "@MCPI_DOCUMENTATION@"
#define MCPI_DOCS_CHANGELOG MCPI_DOCS "CHANGELOG.md"
#define MCPI_DOCS_GETTING_STARTED MCPI_DOCS "GETTING_STARTED.md"
// Internal
#cmakedefine MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
#cmakedefine MCPI_SDK_DIR "@MCPI_SDK_DIR@"
// AppImage
#cmakedefine MCPI_IS_APPIMAGE_BUILD
#cmakedefine MCPI_APPIMAGE_JSON_URL "@MCPI_APPIMAGE_JSON_URL@"
#cmakedefine MCPI_APPIMAGE_VERSION_PLACEHOLDER "@MCPI_APPIMAGE_VERSION_PLACEHOLDER@"
#cmakedefine MCPI_APPIMAGE_DOWNLOAD_URL "@MCPI_APPIMAGE_DOWNLOAD_URL@"
// Flatpak
#cmakedefine MCPI_IS_FLATPAK_BUILD
// Access Configuration At Runtime
const char *reborn_get_version();
bool reborn_is_headless();
bool reborn_is_server();

View File

@ -1,16 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#define ENV(name, ...) extern const char *const name##_ENV;
#include "env-list.h"
#undef ENV
int is_env_var_internal(const char *env);
void clear_internal_env_vars();
#ifdef __cplusplus
}
#endif

26
libreborn/include/libreborn/env/env.h vendored Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include <string>
#define ENV(name, ...) constexpr const char *name##_ENV = #name;
#include "list.h"
#undef ENV
bool is_env_var_internal(const char *env);
void clear_internal_env_vars();
// Set Environmental Variable
void setenv_safe(const char *name, const char *value);
void set_and_print_env(const char *name, const char *value);
// Convert Variable To Value And Vice-Versa
struct Flags;
struct ServerList;
#define overload(type) \
std::string obj_to_env_value(const type &obj); \
void env_value_to_obj(type &out, const char *value)
overload(std::string);
overload(float);
overload(Flags);
overload(ServerList);
#undef overload

49
libreborn/include/libreborn/env/flags.h vendored Normal file
View File

@ -0,0 +1,49 @@
#pragma once
#include <string>
#include <vector>
#include <unordered_map>
#include <functional>
// Seperator
#define FLAG_SEPERATOR_CHAR '|'
// Flag
struct FlagNode {
private:
explicit FlagNode(const std::string &name_);
public:
FlagNode();
// Methods
void sort();
void for_each(const std::function<void(FlagNode &)> &callback);
void for_each_const(const std::function<void(const FlagNode &)> &callback) const;
void add_flag(std::string line);
FlagNode &add_category(const std::string &new_name);
// Properties
std::string name;
bool value;
std::vector<FlagNode> children;
int id;
// Internal
static bool handle_line_prefix(const std::string &prefix, std::string &line);
static std::unordered_map<std::string, bool> flag_prefixes;
static void reset_id_counter();
};
// All Flags
struct Flags {
explicit Flags(const std::string &data);
static Flags get();
// To/From Strings
std::string to_string() const;
void from_string(const std::string &str);
bool operator==(const Flags &other) const;
// To/From Cache
[[nodiscard]] std::unordered_map<std::string, bool> to_cache() const;
void from_cache(const std::unordered_map<std::string, bool> &cache);
// Print
void print() const;
// Properties
FlagNode root;
};

View File

@ -2,6 +2,7 @@
ENV(MCPI_FEATURE_FLAGS, "Client-Mode Feature Flags")
ENV(MCPI_USERNAME, "Player Username")
ENV(MCPI_RENDER_DISTANCE, "Render Distance")
ENV(MCPI_SERVER_LIST, "Server List")
// Game Assets
ENV(_MCPI_REBORN_ASSETS_PATH, "")
ENV(_MCPI_VANILLA_ASSETS_PATH, "")

View File

@ -0,0 +1,19 @@
#pragma once
#include <utility>
#include <string>
#include <vector>
// Parse servers.txt
struct ServerList {
// Type
typedef unsigned short port_t;
typedef std::pair<std::string, port_t> Entry;
// Load
static port_t parse_port(const std::string &s);
void load(const std::string &str);
// Save
[[nodiscard]] std::string to_string() const;
// Entries
std::vector<Entry> entries;
};

View File

@ -1,37 +0,0 @@
#pragma once
#include <unistd.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <signal.h>
#include "log.h"
#include "string.h"
#include "util.h"
#ifdef __cplusplus
extern "C" {
#endif
// Set Environmental Variable
void set_and_print_env(const char *name, const char *value);
// Safe execvpe()
__attribute__((noreturn)) void safe_execvpe(const char *const argv[], const char *const envp[]);
// Debug Tag
#define CHILD_PROCESS_TAG "(Child Process) "
// Run Command And Get Output
char *run_command(const char *const command[], int *exit_status, size_t *output_size);
#define is_exit_status_success(status) (WIFEXITED(status) && WEXITSTATUS(status) == 0)
// Get Exit Status String
void get_exit_status_string(int status, char **out);
#ifdef __cplusplus
}
#endif

View File

@ -1,9 +0,0 @@
#pragma once
#include <libreborn/config.h>
#include "env.h"
#include "log.h"
#include "util.h"
#include "string.h"
#include "exec.h"
#include "patch.h"

View File

@ -1,11 +1,8 @@
#pragma once
#include <stdio.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
// Log File
int reborn_get_log_fd();
@ -17,19 +14,18 @@ int reborn_get_debug_fd();
// Logging
#define INFO(format, ...) fprintf(stderr, "[INFO]: " format "\n", ##__VA_ARGS__)
#define WARN(format, ...) fprintf(stderr, "[WARN]: " format "\n", ##__VA_ARGS__)
#define RAW_DEBUG(tag, format, ...) dprintf(reborn_get_debug_fd(), "[DEBUG]: %s" format "\n", tag, ##__VA_ARGS__)
#define DEBUG(format, ...) RAW_DEBUG(reborn_debug_tag, format, ##__VA_ARGS__)
#define ERR(format, ...) { fprintf(stderr, "[ERR]: (%s:%i): " format "\n", __FILE__, __LINE__, ##__VA_ARGS__); exit(EXIT_FAILURE); }
#define DEBUG(format, ...) dprintf(reborn_get_debug_fd(), "[DEBUG]: %s" format "\n", reborn_debug_tag, ##__VA_ARGS__)
#define ERR(format, ...) \
({ \
fprintf(stderr, "[ERR]: (%s:%i): " format "\n", __FILE__, __LINE__, ##__VA_ARGS__); \
_exit(EXIT_FAILURE); \
})
#define IMPOSSIBLE() ERR("This Should Never Be Called")
#define CONDITIONAL_ERR(is_error, ...) \
{ \
({ \
if ((is_error)) { \
ERR(__VA_ARGS__); \
} else { \
WARN(__VA_ARGS__); \
} \
}
#ifdef __cplusplus
}
#endif
})

View File

@ -1,24 +1,29 @@
#pragma once
#include "log.h"
// Patching Functions
#if defined(REBORN_HAS_PATCH_CODE) && defined(__cplusplus)
#include <string>
#ifndef REBORN_HAS_PATCH_CODE
#error "Missing Patching Functions"
#endif
// Init
void reborn_init_patch();
// Replace Call Located At start With A Call To target
void overwrite_call(void *start, void *target, bool force_b_instruction = false);
void overwrite_call_manual(void *addr, void *new_target, bool force_b_instruction = false);
template <typename T>
void overwrite_call(void *addr, __attribute__((unused)) T *target_type, typename T::ptr_type new_target, const bool force_b_instruction = false) {
overwrite_call_manual(addr, (void *) new_target, force_b_instruction);
}
// Replace All Calls To Method start With target
void *overwrite_calls_manual(void *start, void *target, bool allow_no_callsites = false);
void *overwrite_calls_manual(void *target, void *replacement, bool allow_no_callsites = false);
template <typename T>
void overwrite_calls(T *target, typename T::overwrite_type replacement) {
DEBUG("Overwriting Method: %s", target->name);
DEBUG("Overwriting Method: %s", target->name.c_str());
if (!target->overwrite(replacement)) {
ERR("Unable To Overwrite Method!");
ERR("Unable To Overwrite Method! Use patch_vtable() Instead!");
}
}
@ -26,30 +31,28 @@ void overwrite_calls(T *target, typename T::overwrite_type replacement) {
void *reborn_thunk_enabler(void *target, void *thunk);
// Replace All Calls To start With target Within [to, from)
void overwrite_calls_within_manual(void *from, void *to, void *start, void *target);
void overwrite_calls_within_manual(void *from, void *to, const void *target, void *replacement);
template <typename T>
void _overwrite_calls_within(void *from, void *to, T *start, typename T::ptr_type target) {
overwrite_calls_within_manual(from, to, (void *) start->get(), (void *) target);
void overwrite_calls_within(void *from, void *to, T *target, typename T::ptr_type replacement) {
overwrite_calls_within_manual(from, to, (void *) target->get(), (void *) replacement);
}
// Get Target Address From BL Instruction
void *extract_from_bl_instruction(unsigned char *from);
void *extract_from_bl_instruction(unsigned char *addr);
// Patch Instruction
void patch(void *start, unsigned char patch[4]);
void patch(void *addr, unsigned char patch[4]);
// Patch 4 Bytes Of Data
void patch_address(void *start, void *target);
void patch_address(void *addr, void *target);
// Patch VTable Entry
// This does not affect subclasses.
// IMPORTANT NOTE: This does not affect subclasses.
template <typename T>
void patch_vtable(const T *start, typename T::ptr_type target) {
DEBUG("Patching VTable: %s", start->name);
if (start->enabled) {
void patch_vtable(const T *target, typename T::ptr_type replacement) {
DEBUG("Patching VTable: %s", target->name.c_str());
if (target->enabled) {
WARN("Use overwrite_calls() Instead!");
}
patch_address((void *) start->get_vtable_addr(), (void *) target);
}
#endif
patch_address((void *) target->get_vtable_addr(), (void *) replacement);
}

View File

@ -1,19 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
// Sanitize String
void sanitize_string(char *str, int max_length, int allow_newlines);
// CP437
char *to_cp437(const char *input);
char *from_cp437(const char *input);
// Starts With
int starts_with(const char *str, const char *prefix);
#ifdef __cplusplus
}
#endif

View File

@ -1,101 +0,0 @@
#pragma once
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <dlfcn.h>
#include "log.h"
// Check Memory Allocation
#define ALLOC_CHECK(obj) \
{ \
if (obj == NULL) { \
ERR("Memory Allocation Failed"); \
} \
}
// Align Number
#define ALIGN_UP(x, alignment) \
({ \
int _align_x = (x); \
int _align_alignment = (alignment); \
int _align_diff = _align_x % _align_alignment; \
if (_align_diff > 0) { \
_align_x += (_align_alignment - _align_diff); \
} \
_align_x; \
})
// Hook Library Function
#define EXTERNAL_FUNC(name, return_type, args) \
typedef return_type (*real_##name##_t)args; \
__attribute__((__unused__)) static real_##name##_t real_##name() { \
static real_##name##_t func = NULL; \
if (!func) { \
dlerror(); \
func = (real_##name##_t) dlsym(RTLD_NEXT, #name); \
if (!func) { \
ERR("Error Resolving Symbol: " #name ": %s", dlerror()); \
} \
} \
return func; \
}
#ifdef __cplusplus
#define hooked_function_setup extern "C"
#else
#define hooked_function_setup
#endif
#define HOOK(name, return_type, args) \
EXTERNAL_FUNC(name, return_type, args) \
hooked_function_setup __attribute__((__used__)) return_type name args
#ifdef __cplusplus
extern "C" {
#endif
// Safe Version Of pipe()
void safe_pipe2(int pipefd[2], int flags);
// Check If Two Percentages Are Different Enough To Be Logged
int is_progress_difference_significant(int32_t new_val, int32_t old_val);
// Lock File
int lock_file(const char *file);
void unlock_file(const char *file, int fd);
// Access Configuration At Runtime
const char *reborn_get_version();
int reborn_is_headless();
int reborn_is_server();
// Check $DISPLAY
void reborn_check_display();
// Get Home Subdirectory
const char *get_home_subdirectory_for_game_data();
// Make Sure Directory Exists
void ensure_directory(const char *path);
// Customize VTable
#define CUSTOM_VTABLE(name, parent) \
void _setup_##name##_vtable(parent##_vtable *vtable); \
static parent##_vtable *get_##name##_vtable() { \
static parent##_vtable *vtable = NULL; \
/* Allocate VTable */ \
if (vtable == NULL) { \
/* Init */ \
vtable = dup_vtable(parent##_vtable_base); \
ALLOC_CHECK(vtable); \
/* Setup */ \
_setup_##name##_vtable(vtable); \
} \
/* Return */ \
return vtable; \
} \
/* User-Defined Setup Code */ \
void _setup_##name##_vtable(parent##_vtable *vtable)
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,31 @@
#pragma once
#include <string>
#include <optional>
#include <array>
#include <vector>
#include <functional>
// fork() With I/O
struct Process {
static constexpr int fd_count = 3;
Process(pid_t pid_, const std::array<int, fd_count> &fds_);
[[nodiscard]] int close() const;
const pid_t pid;
const std::array<int, fd_count> fds;
};
std::optional<Process> fork_with_stdio();
void poll_fds(const std::vector<int> &fds, const std::function<void(int, size_t, unsigned char *)> &on_data);
// Safe execvpe()
__attribute__((noreturn)) void safe_execvpe(const char *const argv[], const char *const envp[]);
// Run Command And Get Output
std::vector<unsigned char> *run_command(const char *const command[], int *exit_status);
bool is_exit_status_success(int status);
// Get Exit Status String
std::string get_exit_status_string(int status);
// Open URL
void open_url(const std::string &url);

View File

@ -0,0 +1,11 @@
#pragma once
// GLFW Helpers
#ifndef GLFW_VERSION_MAJOR
#error "Missing GLFW"
#endif
void init_glfw();
GLFWwindow *create_glfw_window(const char *title, int width, int height);
void cleanup_glfw(GLFWwindow *window);
void get_glfw_scale(GLFWwindow *window, float *x_scale, float *y_scale);

View File

@ -0,0 +1,17 @@
#pragma once
#include <cstddef>
// Safe Version Of pipe()
struct Pipe {
Pipe();
const int read;
const int write;
};
// Lock File
int lock_file(const char *file);
void unlock_file(const char *file, int fd);
// Safe write()
void safe_write(int fd, const void *buf, size_t size);

View File

@ -0,0 +1,14 @@
#pragma once
#include <string>
// Sanitize String
void sanitize_string(std::string &str, int max_length, bool allow_newlines);
// CP437
std::string to_cp437(const std::string &input);
std::string from_cp437(const std::string &input);
// Format Time
std::string format_time(const char *fmt);
std::string format_time(const char *fmt, int time);

View File

@ -0,0 +1,49 @@
#pragma once
#include <dlfcn.h>
#include <string>
#include "../log.h"
// Align Number
int align_up(int x, int alignment);
// Hook Library Function
#define HOOK(name, return_type, args) \
typedef return_type (*real_##name##_t)args; \
__attribute__((__unused__)) static real_##name##_t real_##name() { \
static real_##name##_t func = NULL; \
if (!func) { \
dlerror(); \
func = (real_##name##_t) dlsym(RTLD_NEXT, #name); \
if (!func) { \
ERR("Error Resolving Symbol: " #name ": %s", dlerror()); \
} \
} \
return func; \
} \
extern "C" __attribute__((__used__)) return_type name args
// Check If Two Percentages Are Different Enough To Be Logged
bool is_progress_difference_significant(int32_t new_val, int32_t old_val);
// Check $DISPLAY
void reborn_check_display();
// Get Home Subdirectory
const char *get_home_subdirectory_for_game_data();
// Make Sure Directory Exists
void ensure_directory(const char *path);
// embed_resource()
#define EMBEDDED_RESOURCE(name) \
extern unsigned char name[]; \
extern size_t name##_len
// Profile Directory
std::string home_get();
// Default MCPI Port
// This Macro DOES NOT Control MCPI
#define DEFAULT_MULTIPLAYER_PORT 19132

View File

@ -1,6 +1,8 @@
#include <sys/mman.h>
#include <cstring>
#include <cerrno>
#include <libreborn/libreborn.h>
#include <libreborn/patch.h>
#include "patch-internal.h"
// Limit Amount Of overwrite_calls() Calls

Some files were not shown because too many files have changed in this diff Show More