Compare commits
83 Commits
Author | SHA1 | Date | |
---|---|---|---|
6f5f405327 | |||
489615d47f | |||
00fee9c410 | |||
b055980103 | |||
b767b9d4ec | |||
c5f9a519c5 | |||
9428a5a1e1 | |||
013c1db336 | |||
a80b5fb5c2 | |||
bf24ace78e | |||
2568b05053 | |||
dcafeb5c44 | |||
3771666a14 | |||
b71c089fb3 | |||
71946d2087 | |||
b2ec2728e3 | |||
71042da861 | |||
5d8aa28113 | |||
aea31dd4c8 | |||
f0005ff002 | |||
587ba38ffe | |||
c6048ec4fb | |||
0ccf578478 | |||
814217a259 | |||
cfac7d0a12 | |||
c531e7ba7d | |||
bddc299664 | |||
cc060accaf | |||
09c8af0396 | |||
a740814354 | |||
890bd537b2 | |||
983e474b33 | |||
2c3bb41293 | |||
3d89fb691a | |||
4e1476bcfd | |||
8ed425392a | |||
b2c13c8257 | |||
2eb6a1c5be | |||
ef3292c5e0 | |||
67ceb4ad00 | |||
e1d9fc492b | |||
acec86b9b5 | |||
66d2e43f55 | |||
596ff01f75 | |||
57aed4d0b3 | |||
fd26000fd4 | |||
454734ab68 | |||
d3b70878be | |||
7f9d1d843e | |||
900169a728 | |||
633b165af0 | |||
86e8c0dd67 | |||
332acd49fb | |||
c2750bbaec | |||
70ef421780 | |||
ed59e19c52 | |||
dd760cc6f2 | |||
00f90afc2a | |||
4a91937b0a | |||
a6cc0b88b5 | |||
0b542701c5 | |||
fc7ecd528a | |||
2785e3f138 | |||
386f52a85f | |||
871288ee12 | |||
5d7056645d | |||
ecbbcef203 | |||
bbae01a471 | |||
6b5105e74d | |||
57503d6a31 | |||
58a6706cf9 | |||
f501f9a7c9 | |||
203d46c849 | |||
58efe19609 | |||
23b3cbe72f | |||
b339b53f42 | |||
a8d0962491 | |||
a63125f335 | |||
eeace9cf14 | |||
dd25805af9 | |||
a6dad72778 | |||
9ae6cd17cf | |||
644e9e421b |
@ -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
11
.gitmodules
vendored
@ -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
|
||||
|
103
CMakeLists.txt
103
CMakeLists.txt
@ -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
|
||||
|
2
LICENSE
2
LICENSE
@ -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
|
||||
|
2
archives
2
archives
@ -1 +1 @@
|
||||
Subproject commit 2f5953779674ec3a14114aa34b24c81005571ec4
|
||||
Subproject commit 5baa6b1948aeebb5e13af31ff62dc97f00a3b71e
|
@ -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}")
|
||||
|
@ -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
|
||||
|
0
cmake/options/appimage.cmake
Normal file
0
cmake/options/appimage.cmake
Normal 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()
|
@ -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()
|
||||
|
@ -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()
|
@ -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"
|
||||
)
|
||||
|
@ -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()
|
12
dependencies/CMakeLists.txt
vendored
12
dependencies/CMakeLists.txt
vendored
@ -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()
|
||||
|
47
dependencies/LIEF/CMakeLists.txt
vendored
47
dependencies/LIEF/CMakeLists.txt
vendored
@ -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)
|
2
dependencies/LIEF/src
vendored
2
dependencies/LIEF/src
vendored
@ -1 +1 @@
|
||||
Subproject commit bae887e095d87e756d1bf4aa4f95a97693a97b62
|
||||
Subproject commit d4900dab6a9eea864fb14ed1ff7ea5b9f8678e04
|
25
dependencies/glfw/CMakeLists.txt
vendored
25
dependencies/glfw/CMakeLists.txt
vendored
@ -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")
|
||||
|
2
dependencies/glfw/src
vendored
2
dependencies/glfw/src
vendored
@ -1 +1 @@
|
||||
Subproject commit 7b6aead9fb88b3623e3b3725ebb42670cbe4c579
|
||||
Subproject commit 21fea01161e0d6b70c0c5c1f52dc8e7a7df14a50
|
52
dependencies/imgui/CMakeLists.txt
vendored
Normal file
52
dependencies/imgui/CMakeLists.txt
vendored
Normal 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
55
dependencies/imgui/fix-hidpi.patch
vendored
Normal 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
49
dependencies/imgui/glad/CMakeLists.txt
vendored
Normal 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)
|
2
dependencies/imgui/glad/include/GL/gl.h
vendored
Normal file
2
dependencies/imgui/glad/include/GL/gl.h
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
#pragma once
|
||||
#include <glad/glad.h>
|
1
dependencies/imgui/glad/src
vendored
Submodule
1
dependencies/imgui/glad/src
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit e86f90457371c6233053bacf0d6f486a51ddcd67
|
1
dependencies/imgui/src
vendored
Submodule
1
dependencies/imgui/src
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 6982ce43f5b143c5dce5fab0ce07dd4867b705ae
|
5
dependencies/minecraft-pi/CMakeLists.txt
vendored
5
dependencies/minecraft-pi/CMakeLists.txt
vendored
@ -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(
|
||||
|
14
dependencies/runtime/CMakeLists.txt
vendored
14
dependencies/runtime/CMakeLists.txt
vendored
@ -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()
|
2
dependencies/runtime/src
vendored
2
dependencies/runtime/src
vendored
@ -1 +1 @@
|
||||
Subproject commit 377f9ddbc4747ca3a640231d259c0e6fcc71b4b0
|
||||
Subproject commit 84e37b572b55afb1eaa2ada1e37bc36de1584cfd
|
16
dependencies/stb_image/CMakeLists.txt
vendored
16
dependencies/stb_image/CMakeLists.txt
vendored
@ -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")
|
||||
|
2
dependencies/symbol-processor/src
vendored
2
dependencies/symbol-processor/src
vendored
@ -1 +1 @@
|
||||
Subproject commit f72c4f0567c62897d74c734819c11705df0bf4ee
|
||||
Subproject commit c803572e248998cc9d197f84661fea56bebf7346
|
2
dependencies/utf8cpp/CMakeLists.txt
vendored
2
dependencies/utf8cpp/CMakeLists.txt
vendored
@ -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")
|
||||
|
20
dependencies/zenity/CMakeLists.txt
vendored
20
dependencies/zenity/CMakeLists.txt
vendored
@ -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
dependencies/zenity/src
vendored
1
dependencies/zenity/src
vendored
@ -1 +0,0 @@
|
||||
Subproject commit a7496461161c917878d58131711425e7c8e59436
|
@ -51,6 +51,16 @@
|
||||
* `Render Vignette` (Enabled By Default)
|
||||
* `Increase Render Chunk Size` (Enabled By Default)
|
||||
* `Proper Entity Shading` (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`
|
||||
@ -58,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`
|
||||
@ -69,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
|
||||
@ -88,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)
|
||||
|
@ -8,3 +8,4 @@
|
||||
| [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 |
|
||||
| [ReMinecraftPE](https://github.com/ReMinecraftPE/mcpe) | A Lot Of Decompiled Code |
|
@ -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>
|
||||
|
||||
|
@ -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
|
||||
```
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
)
|
||||
|
||||
|
BIN
images/mojang/grasscolor.png
Normal file
BIN
images/mojang/grasscolor.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
BIN
images/start.png
BIN
images/start.png
Binary file not shown.
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 133 KiB |
@ -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 & Mojang AB</developer_name>\n"
|
||||
" <developer_name>${MCPI_AUTHOR} & 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()
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
15
launcher/src/bootstrap/assets.cpp
Normal file
15
launcher/src/bootstrap/assets.cpp
Normal 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);
|
||||
}
|
78
launcher/src/bootstrap/bootstrap.cpp
Normal file
78
launcher/src/bootstrap/bootstrap.cpp
Normal 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);
|
||||
}
|
20
launcher/src/bootstrap/bootstrap.h
Normal file
20
launcher/src/bootstrap/bootstrap.h
Normal 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);
|
38
launcher/src/bootstrap/debug.cpp
Normal file
38
launcher/src/bootstrap/debug.cpp
Normal 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());
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
@ -1,114 +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 Increase Render Chunk Size
|
||||
TRUE Proper Entity Shading
|
@ -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");
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
302
launcher/src/client/ui.cpp
Normal 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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void setup_crash_report();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
92
launcher/src/logger/crash-report.cpp
Normal file
92
launcher/src/logger/crash-report.cpp
Normal 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);
|
||||
}
|
||||
}
|
143
launcher/src/logger/logger.cpp
Normal file
143
launcher/src/logger/logger.cpp
Normal 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);
|
||||
}
|
||||
}
|
7
launcher/src/logger/logger.h
Normal file
7
launcher/src/logger/logger.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
std::string get_logs_folder();
|
||||
void setup_logger();
|
||||
void show_report(const char *log_filename);
|
@ -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
|
||||
|
@ -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")
|
||||
@ -11,3 +11,4 @@ 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(run_update, "update", -13, "Run Updater (If Available)")
|
@ -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;
|
||||
|
@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <libreborn/libreborn.h>
|
||||
|
||||
#define OPTION(name, ...) bool name;
|
||||
struct options_t {
|
||||
#include "option-list.h"
|
||||
|
@ -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);
|
@ -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
83
launcher/src/ui/color.cpp
Normal 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
135
launcher/src/ui/frame.cpp
Normal 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
32
launcher/src/ui/frame.h
Normal 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);
|
||||
};
|
122
launcher/src/updater/appimage.cpp
Normal file
122
launcher/src/updater/appimage.cpp
Normal 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);
|
||||
}
|
48
launcher/src/updater/updater.cpp
Normal file
48
launcher/src/updater/updater.cpp
Normal 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();
|
||||
}
|
||||
}
|
31
launcher/src/updater/updater.h
Normal file
31
launcher/src/updater/updater.h
Normal 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;
|
||||
};
|
@ -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;
|
||||
}
|
@ -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
52
launcher/src/util/env.cpp
Normal 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
167
launcher/src/util/sdk.cpp
Normal 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);
|
||||
}
|
60
launcher/src/util/util.cpp
Normal file
60
launcher/src/util/util.cpp
Normal 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
15
launcher/src/util/util.h
Normal 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);
|
@ -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()
|
||||
|
@ -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();
|
@ -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
26
libreborn/include/libreborn/env/env.h
vendored
Normal 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
49
libreborn/include/libreborn/env/flags.h
vendored
Normal 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;
|
||||
};
|
@ -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, "")
|
19
libreborn/include/libreborn/env/servers.h
vendored
Normal file
19
libreborn/include/libreborn/env/servers.h
vendored
Normal 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;
|
||||
};
|
@ -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
|
@ -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"
|
@ -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
|
||||
})
|
||||
|
@ -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);
|
||||
patch_address((void *) target->get_vtable_addr(), (void *) replacement);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -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
|
@ -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
|
31
libreborn/include/libreborn/util/exec.h
Normal file
31
libreborn/include/libreborn/util/exec.h
Normal 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);
|
11
libreborn/include/libreborn/util/glfw.h
Normal file
11
libreborn/include/libreborn/util/glfw.h
Normal 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);
|
17
libreborn/include/libreborn/util/io.h
Normal file
17
libreborn/include/libreborn/util/io.h
Normal 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);
|
14
libreborn/include/libreborn/util/string.h
Normal file
14
libreborn/include/libreborn/util/string.h
Normal 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);
|
49
libreborn/include/libreborn/util/util.h
Normal file
49
libreborn/include/libreborn/util/util.h
Normal 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
|
@ -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
Loading…
x
Reference in New Issue
Block a user