Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
e812406a15 |
201
.github/build.sh
vendored
201
.github/build.sh
vendored
@ -40,45 +40,11 @@ x86-android-bionic-static) ;;
|
|||||||
x86_64-android-bionic-static) ;;
|
x86_64-android-bionic-static) ;;
|
||||||
arm-android-bionic-static) ;;
|
arm-android-bionic-static) ;;
|
||||||
aarch64-android-bionic-static) ;;
|
aarch64-android-bionic-static) ;;
|
||||||
wasm32-emscripten-emscripten-static) ;;
|
|
||||||
*) >&2 echo "configuration $BSH_HOST_ARCH-$BSH_HOST_PLATFORM-$BSH_HOST_LIBC-$BSH_STATIC_DYNAMIC is not supported" && exit 1;;
|
*) >&2 echo "configuration $BSH_HOST_ARCH-$BSH_HOST_PLATFORM-$BSH_HOST_LIBC-$BSH_STATIC_DYNAMIC is not supported" && exit 1;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
if [[ $BSH_HOST_PLATFORM == android ]]; then
|
|
||||||
android_platform=android-31
|
|
||||||
if [[ -z "${JAVA_HOME_8_X64-}" ]]; then
|
|
||||||
>&2 echo "JAVA_HOME_8_X64 not set"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [[ -z "${ANDROID_SDK_ROOT-}" ]]; then
|
|
||||||
>&2 echo "ANDROID_SDK_ROOT not set"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [[ -z "${ANDROID_NDK_LATEST_HOME-}" ]]; then
|
|
||||||
>&2 echo "ANDROID_NDK_LATEST_HOME not set"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -z ${BSH_NO_PACKAGES-} ]]; then
|
if [[ -z ${BSH_NO_PACKAGES-} ]]; then
|
||||||
case $BSH_HOST_PLATFORM in
|
case $BSH_BUILD_PLATFORM in
|
||||||
android)
|
|
||||||
(
|
|
||||||
export PATH=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$ANDROID_SDK_ROOT/tools/bin:$PATH
|
|
||||||
sdkmanager "platforms;$android_platform"
|
|
||||||
)
|
|
||||||
;;
|
|
||||||
windows)
|
|
||||||
if [[ $BSH_BUILD_PLATFORM-$BSH_HOST_LIBC == windows-mingw ]]; then
|
|
||||||
pacman -S --noconfirm --needed mingw-w64-ucrt-x86_64-gcc
|
|
||||||
if [[ $BSH_STATIC_DYNAMIC == static ]]; then
|
|
||||||
pacman -S --noconfirm --needed mingw-w64-ucrt-x86_64-{cmake,7zip,jq} patch
|
|
||||||
else
|
|
||||||
pacman -S --noconfirm --needed mingw-w64-ucrt-x86_64-{pkgconf,bzip2,luajit,jsoncpp,curl,SDL2,libpng,meson,fftw,jq}
|
|
||||||
fi
|
|
||||||
export PKG_CONFIG=$(which pkg-config.exe)
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
linux)
|
linux)
|
||||||
sudo apt update
|
sudo apt update
|
||||||
if [[ $BSH_STATIC_DYNAMIC == static ]]; then
|
if [[ $BSH_STATIC_DYNAMIC == static ]]; then
|
||||||
@ -86,6 +52,9 @@ if [[ -z ${BSH_NO_PACKAGES-} ]]; then
|
|||||||
else
|
else
|
||||||
sudo apt install libluajit-5.1-dev libcurl4-openssl-dev libfftw3-dev zlib1g-dev libsdl2-dev libbz2-dev libjsoncpp-dev
|
sudo apt install libluajit-5.1-dev libcurl4-openssl-dev libfftw3-dev zlib1g-dev libsdl2-dev libbz2-dev libjsoncpp-dev
|
||||||
fi
|
fi
|
||||||
|
if [[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC == windows-mingw ]]; then
|
||||||
|
sudo apt install g++-mingw-w64-x86-64
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
darwin)
|
darwin)
|
||||||
brew install pkg-config binutils
|
brew install pkg-config binutils
|
||||||
@ -93,14 +62,6 @@ if [[ -z ${BSH_NO_PACKAGES-} ]]; then
|
|||||||
brew install luajit curl fftw zlib sdl2 bzip2 jsoncpp
|
brew install luajit curl fftw zlib sdl2 bzip2 jsoncpp
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
emscripten)
|
|
||||||
git clone https://github.com/emscripten-core/emsdk.git --branch 3.1.30
|
|
||||||
cd emsdk
|
|
||||||
./emsdk install latest
|
|
||||||
./emsdk activate latest
|
|
||||||
. ./emsdk_env.sh
|
|
||||||
cd ..
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -114,6 +75,34 @@ function inplace_sed() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function subst_version() {
|
||||||
|
local path=$1
|
||||||
|
if [[ $BSH_HOST_PLATFORM == darwin ]]; then
|
||||||
|
inplace_sed "s|SUBST_MACOS_MIN_VER|$macos_min_ver|g" $path
|
||||||
|
else
|
||||||
|
inplace_sed "s|SUBST_DATE|$(date --iso-8601)|g" $path
|
||||||
|
fi
|
||||||
|
inplace_sed "s|SUBST_SAVE_VERSION|$save_version|g" $path
|
||||||
|
inplace_sed "s|SUBST_MINOR_VERSION|$minor_version|g" $path
|
||||||
|
inplace_sed "s|SUBST_BUILD_NUM|$build_num|g" $path
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ $BSH_HOST_PLATFORM == android ]]; then
|
||||||
|
android_platform=android-30
|
||||||
|
if [[ -z "${JAVA_HOME_8_X64-}" ]]; then
|
||||||
|
>&2 echo "JAVA_HOME_8_X64 not set"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ -z "${ANDROID_SDK_ROOT-}" ]]; then
|
||||||
|
>&2 echo "ANDROID_SDK_ROOT not set"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ -z "${ANDROID_NDK_LATEST_HOME-}" ]]; then
|
||||||
|
>&2 echo "ANDROID_NDK_LATEST_HOME not set"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC == windows-msvc ]]; then
|
if [[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC == windows-msvc ]]; then
|
||||||
case $BSH_HOST_ARCH in
|
case $BSH_HOST_ARCH in
|
||||||
x86_64) vs_env_arch=x64;;
|
x86_64) vs_env_arch=x64;;
|
||||||
@ -142,9 +131,9 @@ elif [[ $BSH_HOST_PLATFORM == darwin ]]; then
|
|||||||
elif [[ $BSH_HOST_PLATFORM == android ]]; then
|
elif [[ $BSH_HOST_PLATFORM == android ]]; then
|
||||||
case $BSH_HOST_ARCH in
|
case $BSH_HOST_ARCH in
|
||||||
x86_64) android_toolchain_prefix=x86_64-linux-android ; android_system_version=21; android_arch_abi=x86_64 ;;
|
x86_64) android_toolchain_prefix=x86_64-linux-android ; android_system_version=21; android_arch_abi=x86_64 ;;
|
||||||
x86) android_toolchain_prefix=i686-linux-android ; android_system_version=21; android_arch_abi=x86 ;;
|
x86) android_toolchain_prefix=i686-linux-android ; android_system_version=19; android_arch_abi=x86 ;;
|
||||||
aarch64) android_toolchain_prefix=aarch64-linux-android ; android_system_version=21; android_arch_abi=arm64-v8a ;;
|
aarch64) android_toolchain_prefix=aarch64-linux-android ; android_system_version=21; android_arch_abi=arm64-v8a ;;
|
||||||
arm) android_toolchain_prefix=armv7a-linux-androideabi; android_system_version=21; android_arch_abi=armeabi-v7a;;
|
arm) android_toolchain_prefix=armv7a-linux-androideabi; android_system_version=19; android_arch_abi=armeabi-v7a;;
|
||||||
esac
|
esac
|
||||||
android_toolchain_dir=$ANDROID_NDK_LATEST_HOME/toolchains/llvm/prebuilt/linux-x86_64
|
android_toolchain_dir=$ANDROID_NDK_LATEST_HOME/toolchains/llvm/prebuilt/linux-x86_64
|
||||||
CC=$android_toolchain_dir/bin/$android_toolchain_prefix$android_system_version-clang
|
CC=$android_toolchain_dir/bin/$android_toolchain_prefix$android_system_version-clang
|
||||||
@ -188,18 +177,13 @@ meson_configure=meson$'\t'setup
|
|||||||
if [[ $BSH_DEBUG_RELEASE == release ]]; then
|
if [[ $BSH_DEBUG_RELEASE == release ]]; then
|
||||||
meson_configure+=$'\t'-Dbuildtype=debugoptimized
|
meson_configure+=$'\t'-Dbuildtype=debugoptimized
|
||||||
fi
|
fi
|
||||||
if [[ $BSH_HOST_PLATFORM == darwin ]]; then
|
|
||||||
meson_configure+=$'\t'-Dmanifest_macos_min_ver=$macos_min_ver
|
|
||||||
else
|
|
||||||
meson_configure+=$'\t'-Dmanifest_date=$(date --iso-8601)
|
|
||||||
fi
|
|
||||||
meson_configure+=$'\t'-Dapp_name=$APP_NAME
|
meson_configure+=$'\t'-Dapp_name=$APP_NAME
|
||||||
meson_configure+=$'\t'-Dapp_comment=$APP_COMMENT
|
meson_configure+=$'\t'-Dapp_comment=$APP_COMMENT
|
||||||
meson_configure+=$'\t'-Dapp_exe=$APP_EXE
|
meson_configure+=$'\t'-Dapp_exe=$APP_EXE
|
||||||
meson_configure+=$'\t'-Dapp_id=$APP_ID
|
meson_configure+=$'\t'-Dapp_id=$APP_ID
|
||||||
meson_configure+=$'\t'-Dapp_data=$APP_DATA
|
meson_configure+=$'\t'-Dapp_data=$APP_DATA
|
||||||
meson_configure+=$'\t'-Dapp_vendor=$APP_VENDOR
|
meson_configure+=$'\t'-Dapp_vendor=$APP_VENDOR
|
||||||
meson_configure+=$'\t'-Dstrip=false
|
meson_configure+=$'\t'-Db_strip=false
|
||||||
meson_configure+=$'\t'-Db_staticpic=false
|
meson_configure+=$'\t'-Db_staticpic=false
|
||||||
meson_configure+=$'\t'-Dmod_id=$MOD_ID
|
meson_configure+=$'\t'-Dmod_id=$MOD_ID
|
||||||
case $BSH_HOST_ARCH-$BSH_HOST_PLATFORM-$BSH_HOST_LIBC-$BSH_DEBUG_RELEASE in
|
case $BSH_HOST_ARCH-$BSH_HOST_PLATFORM-$BSH_HOST_LIBC-$BSH_DEBUG_RELEASE in
|
||||||
@ -217,12 +201,6 @@ fi
|
|||||||
if [[ $PACKAGE_MODE == nolua ]]; then
|
if [[ $PACKAGE_MODE == nolua ]]; then
|
||||||
meson_configure+=$'\t'-Dlua=none
|
meson_configure+=$'\t'-Dlua=none
|
||||||
fi
|
fi
|
||||||
if [[ $PACKAGE_MODE == backendvs ]]; then
|
|
||||||
meson_configure+=$'\t'-Dbackend=vs
|
|
||||||
# meson 1.2.3 configures vs projects that bring their own manifest, which conflicts with ours
|
|
||||||
# TODO: remove this patch once https://github.com/mesonbuild/meson/pull/12472 makes it into a release that we can use
|
|
||||||
meson_configure+=$'\t'-Dwindows_utf8cp=false
|
|
||||||
fi
|
|
||||||
if [[ $BSH_STATIC_DYNAMIC == static ]]; then
|
if [[ $BSH_STATIC_DYNAMIC == static ]]; then
|
||||||
meson_configure+=$'\t'-Dstatic=prebuilt
|
meson_configure+=$'\t'-Dstatic=prebuilt
|
||||||
if [[ $BSH_HOST_PLATFORM == windows ]]; then
|
if [[ $BSH_HOST_PLATFORM == windows ]]; then
|
||||||
@ -238,11 +216,6 @@ if [[ $BSH_STATIC_DYNAMIC == static ]]; then
|
|||||||
c_link_args+=\'-static-libstdc++\',
|
c_link_args+=\'-static-libstdc++\',
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
if [[ "$BSH_HOST_PLATFORM-$BSH_HOST_LIBC $BSH_BUILD_PLATFORM" == "windows-mingw windows" ]]; then
|
|
||||||
meson_configure+=$'\t'-Dworkaround_elusive_bzip2=true
|
|
||||||
meson_configure+=$'\t'-Dworkaround_elusive_bzip2_include_dir=/ucrt64/include
|
|
||||||
meson_configure+=$'\t'-Dworkaround_elusive_bzip2_lib_dir=/ucrt64/lib
|
|
||||||
fi
|
|
||||||
if [[ $BSH_BUILD_PLATFORM == linux ]]; then
|
if [[ $BSH_BUILD_PLATFORM == linux ]]; then
|
||||||
meson_configure+=$'\t'-Dworkaround_elusive_bzip2=true
|
meson_configure+=$'\t'-Dworkaround_elusive_bzip2=true
|
||||||
fi
|
fi
|
||||||
@ -266,54 +239,52 @@ fi
|
|||||||
if [[ $RELEASE_TYPE == stable ]]; then
|
if [[ $RELEASE_TYPE == stable ]]; then
|
||||||
stable_or_beta=yes
|
stable_or_beta=yes
|
||||||
fi
|
fi
|
||||||
if [[ $stable_or_beta == yes ]]; then
|
set +e
|
||||||
xyz=$(echo $RELEASE_NAME | cut -d 'v' -f 2 | cut -d 'b' -f 1) # $RELEASE_NAME is vX.Y.Z or vX.Y.Zb
|
save_version=$(cat src/Config.template.h | sed -n 's/constexpr int SAVE_VERSION * = \([^;]*\);/\1/p')
|
||||||
display_version_major=$(echo $xyz | cut -d '.' -f 1)
|
minor_version=$(cat src/Config.template.h | sed -n 's/constexpr int MINOR_VERSION * = \([^;]*\);/\1/p')
|
||||||
display_version_minor=$(echo $xyz | cut -d '.' -f 2)
|
build_num=$(cat src/Config.template.h | sed -n 's/constexpr int BUILD_NUM * = \([^;]*\);/\1/p')
|
||||||
build_num=$(echo $xyz | cut -d '.' -f 3)
|
if [[ -z ${save_version-} ]] || [[ -z ${minor_version-} ]] || [[ -z ${build_num-} ]]; then
|
||||||
if [[ $MOD_ID != 0 ]]; then
|
>&2 echo "failed to extract version from Config.template.h"
|
||||||
meson_configure+=$'\t'-Ddisplay_version_major=$display_version_major
|
exit 1
|
||||||
meson_configure+=$'\t'-Ddisplay_version_minor=$display_version_minor
|
fi
|
||||||
meson_configure+=$'\t'-Dbuild_num=$build_num
|
set -e
|
||||||
fi
|
if [[ $stable_or_beta == yes ]] && [[ $MOD_ID != 0 ]]; then
|
||||||
|
save_version=$(echo $RELEASE_NAME | cut -d '.' -f 1)
|
||||||
|
minor_version=$(echo $RELEASE_NAME | cut -d '.' -f 2)
|
||||||
|
build_num=$(echo $RELEASE_NAME | cut -d '.' -f 3)
|
||||||
fi
|
fi
|
||||||
if [[ $RELEASE_TYPE == snapshot ]]; then
|
if [[ $RELEASE_TYPE == snapshot ]]; then
|
||||||
build_num=$(echo $RELEASE_NAME | cut -d '-' -f 2) # $RELEASE_NAME is snapshot-X
|
|
||||||
meson_configure+=$'\t'-Dsnapshot=true
|
meson_configure+=$'\t'-Dsnapshot=true
|
||||||
if [[ $MOD_ID != 0 ]]; then
|
meson_configure+=$'\t'-Dsnapshot_id=$(echo $RELEASE_NAME | cut -d '-' -f 2) # $RELEASE_NAME is snapshot-X
|
||||||
meson_configure+=$'\t'-Dbuild_num=$build_num
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
if [[ $RELEASE_TYPE == snapshot ]] && [[ $MOD_ID != 0 ]]; then
|
if [[ $RELEASE_TYPE == snapshot ]] && [[ $MOD_ID != 0 ]]; then
|
||||||
>&2 echo "mods and snapshots do not mix"
|
>&2 echo "mods and snapshots do not mix"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
if [[ $stable_or_beta == yes ]] && [[ $MOD_ID != 0 ]]; then
|
||||||
|
# mods and snapshots both check their snapshot_id against whatever version starcatcher.us/TPT has
|
||||||
|
meson_configure+=$'\t'-Dsnapshot_id=$(echo $RELEASE_NAME | cut -d '.' -f 3) # $RELEASE_NAME is vX.Y.Z
|
||||||
|
fi
|
||||||
if [[ $RELEASE_TYPE == snapshot ]] || [[ $MOD_ID != 0 ]]; then
|
if [[ $RELEASE_TYPE == snapshot ]] || [[ $MOD_ID != 0 ]]; then
|
||||||
meson_configure+=$'\t'-Dupdate_server=starcatcher.us/TPT
|
meson_configure+=$'\t'-Dupdate_server=starcatcher.us/TPT
|
||||||
if [[ $BSH_HOST_PLATFORM == emscripten ]]; then
|
|
||||||
meson_configure+=$'\t'-Dserver=tptserv.starcatcher.us
|
|
||||||
meson_configure+=$'\t'-Dstatic_server=tptserv.starcatcher.us/Static
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
if [[ $RELEASE_TYPE != dev ]]; then
|
if [[ $RELEASE_TYPE != dev ]]; then
|
||||||
meson_configure+=$'\t'-Dignore_updates=false
|
meson_configure+=$'\t'-Dignore_updates=false
|
||||||
fi
|
fi
|
||||||
if [[ "$BSH_HOST_PLATFORM-$BSH_HOST_LIBC" == "windows-mingw" ]]; then
|
if [[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC == windows-mingw ]]; then
|
||||||
meson_configure+=$'\t'--cross-file=.github/mingw-ghactions.ini
|
if [[ $BSH_BUILD_PLATFORM == linux ]]; then
|
||||||
# there is some mingw bug that only ever manifests on ghactions which makes MakeIco.exe use tons of memory and fail
|
meson_configure+=$'\t'--cross-file=.github/mingw-ghactions.ini
|
||||||
# TODO: remove this hack once we figure out how to fix that
|
fi
|
||||||
meson_configure+=$'\t'-Dwindows_icons=false
|
|
||||||
fi
|
fi
|
||||||
if [[ $BSH_DEBUG_RELEASE-$BSH_STATIC_DYNAMIC == release-static ]]; then
|
if [[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC != windows-mingw ]] && [[ $BSH_STATIC_DYNAMIC == static ]]; then
|
||||||
|
# LTO simply doesn't work with MinGW. I have no idea why and I also don't care.
|
||||||
|
# It also has a tendency to not play well with dynamic libraries.
|
||||||
meson_configure+=$'\t'-Db_lto=true
|
meson_configure+=$'\t'-Db_lto=true
|
||||||
fi
|
fi
|
||||||
if [[ $BSH_HOST_PLATFORM-$BSH_HOST_ARCH == darwin-aarch64 ]]; then
|
if [[ $BSH_HOST_PLATFORM-$BSH_HOST_ARCH == darwin-aarch64 ]]; then
|
||||||
meson_configure+=$'\t'--cross-file=.github/macaa64-ghactions.ini
|
meson_configure+=$'\t'--cross-file=.github/macaa64-ghactions.ini
|
||||||
fi
|
fi
|
||||||
if [[ $BSH_HOST_PLATFORM == emscripten ]]; then
|
if [[ $RELEASE_TYPE == tptlibsdev ]] && ([[ $BSH_HOST_PLATFORM == windows ]] || [[ $BSH_STATIC_DYNAMIC == static ]]); then
|
||||||
meson_configure+=$'\t'--cross-file=.github/emscripten-ghactions.ini
|
|
||||||
fi
|
|
||||||
if [[ $RELEASE_TYPE == tptlibsdev ]] && ([[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC == windows-msvc ]] || [[ $BSH_STATIC_DYNAMIC == static ]]); then
|
|
||||||
if [[ -z ${TPTLIBSREMOTE-} ]]; then
|
if [[ -z ${TPTLIBSREMOTE-} ]]; then
|
||||||
if [[ -z "${GITHUB_REPOSITORY_OWNER-}" ]]; then
|
if [[ -z "${GITHUB_REPOSITORY_OWNER-}" ]]; then
|
||||||
>&2 echo "GITHUB_REPOSITORY_OWNER not set"
|
>&2 echo "GITHUB_REPOSITORY_OWNER not set"
|
||||||
@ -323,6 +294,11 @@ if [[ $RELEASE_TYPE == tptlibsdev ]] && ([[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC ==
|
|||||||
else
|
else
|
||||||
tptlibsremote=$TPTLIBSREMOTE
|
tptlibsremote=$TPTLIBSREMOTE
|
||||||
fi
|
fi
|
||||||
|
if [[ "$BSH_HOST_ARCH-$BSH_HOST_PLATFORM-$BSH_HOST_LIBC-$BSH_STATIC_DYNAMIC $BSH_BUILD_PLATFORM" == "x86_64-windows-mingw-dynamic linux" ]]; then
|
||||||
|
>&2 echo "this configuration is not supported in tptlibsdev mode"
|
||||||
|
touch $ASSET_PATH
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
tptlibsbranch=$(echo $RELEASE_NAME | cut -d '-' -f 2-) # $RELEASE_NAME is tptlibsdev-BRANCH
|
tptlibsbranch=$(echo $RELEASE_NAME | cut -d '-' -f 2-) # $RELEASE_NAME is tptlibsdev-BRANCH
|
||||||
if [[ -d build-tpt-libs ]] && [[ ${TPTLIBSRESET-} == yes ]]; then
|
if [[ -d build-tpt-libs ]] && [[ ${TPTLIBSRESET-} == yes ]]; then
|
||||||
rm -rf build-tpt-libs
|
rm -rf build-tpt-libs
|
||||||
@ -350,11 +326,7 @@ if [[ $RELEASE_TYPE == tptlibsdev ]] && ([[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC ==
|
|||||||
meson_configure+=$'\t'-Dtpt_libs_vtag=$tpt_libs_vtag
|
meson_configure+=$'\t'-Dtpt_libs_vtag=$tpt_libs_vtag
|
||||||
fi
|
fi
|
||||||
if [[ $BSH_HOST_PLATFORM == android ]]; then
|
if [[ $BSH_HOST_PLATFORM == android ]]; then
|
||||||
android_platform_jar=$ANDROID_SDK_ROOT/platforms/$android_platform/android.jar
|
android_platform=android-30
|
||||||
if ! [[ -f $android_platform_jar ]]; then
|
|
||||||
>&2 echo "$android_platform_jar not found"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
meson_configure+=$'\t'--cross-file=android/cross/$BSH_HOST_ARCH.ini
|
meson_configure+=$'\t'--cross-file=android/cross/$BSH_HOST_ARCH.ini
|
||||||
cat << ANDROID_INI > .github/android-ghactions.ini
|
cat << ANDROID_INI > .github/android-ghactions.ini
|
||||||
[constants]
|
[constants]
|
||||||
@ -365,7 +337,7 @@ andriod_sdk_build_tools = '$ANDROID_SDK_ROOT/build-tools/32.0.0'
|
|||||||
# android_ndk_toolchain_prefix comes from the correct cross-file in ./android/cross
|
# android_ndk_toolchain_prefix comes from the correct cross-file in ./android/cross
|
||||||
android_ndk_toolchain_prefix = android_ndk_toolchain_prefix
|
android_ndk_toolchain_prefix = android_ndk_toolchain_prefix
|
||||||
android_platform = '$android_platform'
|
android_platform = '$android_platform'
|
||||||
android_platform_jar = '$android_platform_jar'
|
android_platform_jar = '$ANDROID_SDK_ROOT/platforms/' + android_platform + '/android.jar'
|
||||||
java_runtime_jar = '$JAVA_HOME_8_X64/jre/lib/rt.jar'
|
java_runtime_jar = '$JAVA_HOME_8_X64/jre/lib/rt.jar'
|
||||||
|
|
||||||
[binaries]
|
[binaries]
|
||||||
@ -382,6 +354,7 @@ zipalign = andriod_sdk_build_tools / 'zipalign'
|
|||||||
apksigner = andriod_sdk_build_tools / 'apksigner'
|
apksigner = andriod_sdk_build_tools / 'apksigner'
|
||||||
ANDROID_INI
|
ANDROID_INI
|
||||||
meson_configure+=$'\t'--cross-file=.github/android-ghactions.ini
|
meson_configure+=$'\t'--cross-file=.github/android-ghactions.ini
|
||||||
|
meson_configure+=$'\t'-Dhttp=false
|
||||||
fi
|
fi
|
||||||
meson_configure+=$'\t'-Dc_args=[$c_args]
|
meson_configure+=$'\t'-Dc_args=[$c_args]
|
||||||
meson_configure+=$'\t'-Dcpp_args=[$c_args]
|
meson_configure+=$'\t'-Dcpp_args=[$c_args]
|
||||||
@ -389,29 +362,6 @@ meson_configure+=$'\t'-Dc_link_args=[$c_link_args]
|
|||||||
meson_configure+=$'\t'-Dcpp_link_args=[$c_link_args]
|
meson_configure+=$'\t'-Dcpp_link_args=[$c_link_args]
|
||||||
$meson_configure build
|
$meson_configure build
|
||||||
cd build
|
cd build
|
||||||
|
|
||||||
function verify_version_component() {
|
|
||||||
local key=$1
|
|
||||||
local expected=$2
|
|
||||||
local actual=$(jq -r '.[] | select(.name == "'$key'") | .value' < meson-info/intro-buildoptions.json)
|
|
||||||
if [[ $actual != $expected ]]; then
|
|
||||||
>&2 echo "meson option $key expected to be $expected, is instead $actual"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
if [[ $stable_or_beta == yes ]] && [[ $MOD_ID == 0 ]]; then
|
|
||||||
verify_version_component display_version_major $display_version_major
|
|
||||||
verify_version_component display_version_minor $display_version_minor
|
|
||||||
verify_version_component build_num $build_num
|
|
||||||
verify_version_component upstream_version_major $display_version_major
|
|
||||||
verify_version_component upstream_version_minor $display_version_minor
|
|
||||||
verify_version_component upstream_build_num $build_num
|
|
||||||
fi
|
|
||||||
if [[ $RELEASE_TYPE == snapshot ]] && [[ $MOD_ID == 0 ]]; then
|
|
||||||
verify_version_component build_num $build_num
|
|
||||||
verify_version_component upstream_build_num $build_num
|
|
||||||
fi
|
|
||||||
|
|
||||||
strip=strip
|
strip=strip
|
||||||
objcopy=objcopy
|
objcopy=objcopy
|
||||||
strip_target=$ASSET_PATH
|
strip_target=$ASSET_PATH
|
||||||
@ -425,12 +375,9 @@ if [[ $PACKAGE_MODE == appimage ]]; then
|
|||||||
meson configure -Dcan_install=no -Dignore_updates=true -Dbuild_render=false -Dbuild_font=false
|
meson configure -Dcan_install=no -Dignore_updates=true -Dbuild_render=false -Dbuild_font=false
|
||||||
strip_target=$APP_EXE
|
strip_target=$APP_EXE
|
||||||
fi
|
fi
|
||||||
meson_compile=meson$'\t'compile
|
if [[ $BSH_BUILD_PLATFORM == windows ]]; then
|
||||||
meson_compile+=$'\t'-v
|
|
||||||
if [[ $BSH_BUILD_PLATFORM == windows ]] && [[ $PACKAGE_MODE != backendvs ]]; then
|
|
||||||
set +e
|
set +e
|
||||||
meson_compile+=$'\t'--ninja-args='["-d","keeprsp"]'
|
ninja -v -d keeprsp
|
||||||
$meson_compile
|
|
||||||
ninja_code=$?
|
ninja_code=$?
|
||||||
set -e
|
set -e
|
||||||
cat $APP_EXE.exe.rsp
|
cat $APP_EXE.exe.rsp
|
||||||
@ -444,7 +391,7 @@ if [[ $BSH_BUILD_PLATFORM == windows ]] && [[ $PACKAGE_MODE != backendvs ]]; the
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
$meson_compile
|
ninja -v
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $SEPARATE_DEBUG == yes ]] && [[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC != windows-msvc ]]; then
|
if [[ $SEPARATE_DEBUG == yes ]] && [[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC != windows-msvc ]]; then
|
||||||
@ -465,6 +412,7 @@ if [[ $PACKAGE_MODE == dmg ]]; then
|
|||||||
mkdir $appdir
|
mkdir $appdir
|
||||||
mkdir $appdir/Contents
|
mkdir $appdir/Contents
|
||||||
cp resources/Info.plist $appdir/Contents/Info.plist
|
cp resources/Info.plist $appdir/Contents/Info.plist
|
||||||
|
subst_version $appdir/Contents/Info.plist
|
||||||
mkdir $appdir/Contents/MacOS
|
mkdir $appdir/Contents/MacOS
|
||||||
cp $APP_EXE $appdir/Contents/MacOS/$APP_EXE
|
cp $APP_EXE $appdir/Contents/MacOS/$APP_EXE
|
||||||
mkdir $appdir/Contents/Resources
|
mkdir $appdir/Contents/Resources
|
||||||
@ -485,8 +433,6 @@ if [[ $PACKAGE_MODE == dmg ]]; then
|
|||||||
cp ../LICENSE dmgroot/LICENSE
|
cp ../LICENSE dmgroot/LICENSE
|
||||||
cp ../README.md dmgroot/README.md
|
cp ../README.md dmgroot/README.md
|
||||||
hdiutil create -format UDZO -volname $APP_NAME -fs HFS+ -srcfolder dmgroot -o $ASSET_PATH
|
hdiutil create -format UDZO -volname $APP_NAME -fs HFS+ -srcfolder dmgroot -o $ASSET_PATH
|
||||||
elif [[ $PACKAGE_MODE == emscripten ]]; then
|
|
||||||
tar cvf $ASSET_PATH $APP_EXE.js $APP_EXE.worker.js $APP_EXE.wasm
|
|
||||||
elif [[ $PACKAGE_MODE == appimage ]]; then
|
elif [[ $PACKAGE_MODE == appimage ]]; then
|
||||||
# so far this can only happen with $BSH_HOST_PLATFORM-$BSH_HOST_LIBC == linux-gnu, but this may change later
|
# so far this can only happen with $BSH_HOST_PLATFORM-$BSH_HOST_LIBC == linux-gnu, but this may change later
|
||||||
case $BSH_HOST_ARCH in
|
case $BSH_HOST_ARCH in
|
||||||
@ -509,6 +455,7 @@ elif [[ $PACKAGE_MODE == appimage ]]; then
|
|||||||
cp ../resources/icon_exe.svg $appdir/$APP_VENDOR-$APP_EXE.svg
|
cp ../resources/icon_exe.svg $appdir/$APP_VENDOR-$APP_EXE.svg
|
||||||
cp resources/powder.desktop $appdir/$APP_ID.desktop
|
cp resources/powder.desktop $appdir/$APP_ID.desktop
|
||||||
cp resources/appdata.xml $appdir/usr/share/metainfo/$APP_ID.appdata.xml
|
cp resources/appdata.xml $appdir/usr/share/metainfo/$APP_ID.appdata.xml
|
||||||
|
subst_version $appdir/usr/share/metainfo/$APP_ID.appdata.xml
|
||||||
cp $appdir/$APP_VENDOR-$APP_EXE.svg $appdir/usr/share/icons/$APP_VENDOR-$APP_EXE.svg
|
cp $appdir/$APP_VENDOR-$APP_EXE.svg $appdir/usr/share/icons/$APP_VENDOR-$APP_EXE.svg
|
||||||
cp $appdir/$APP_ID.desktop $appdir/usr/share/applications/$APP_ID.desktop
|
cp $appdir/$APP_ID.desktop $appdir/usr/share/applications/$APP_ID.desktop
|
||||||
./appimagetool $appdir $ASSET_PATH
|
./appimagetool $appdir $ASSET_PATH
|
||||||
|
4
.github/create-release.sh
vendored
4
.github/create-release.sh
vendored
@ -1,4 +0,0 @@
|
|||||||
set -euo pipefail
|
|
||||||
IFS=$'\t\n'
|
|
||||||
|
|
||||||
gh release create --draft --verify-tag --title $RELEASE_NAME $GITHUB_REF_NAME
|
|
11
.github/emscripten-ghactions.ini
vendored
11
.github/emscripten-ghactions.ini
vendored
@ -1,11 +0,0 @@
|
|||||||
[binaries]
|
|
||||||
c = 'emcc'
|
|
||||||
cpp = 'em++'
|
|
||||||
strip = 'emstrip'
|
|
||||||
ar = 'emar'
|
|
||||||
|
|
||||||
[host_machine]
|
|
||||||
system = 'emscripten'
|
|
||||||
cpu_family = 'wasm32'
|
|
||||||
cpu = 'wasm32'
|
|
||||||
endian = 'little'
|
|
1
.github/macaa64-ghactions.ini
vendored
1
.github/macaa64-ghactions.ini
vendored
@ -1,7 +1,6 @@
|
|||||||
[binaries]
|
[binaries]
|
||||||
c = [ 'clang', '-arch', 'arm64' ]
|
c = [ 'clang', '-arch', 'arm64' ]
|
||||||
cpp = [ 'clang++', '-arch', 'arm64' ]
|
cpp = [ 'clang++', '-arch', 'arm64' ]
|
||||||
objcpp = [ 'clang++', '-arch', 'arm64' ]
|
|
||||||
strip = 'strip'
|
strip = 'strip'
|
||||||
|
|
||||||
[host_machine]
|
[host_machine]
|
||||||
|
4
.github/mingw-ghactions.ini
vendored
4
.github/mingw-ghactions.ini
vendored
@ -2,8 +2,8 @@
|
|||||||
prefix = 'x86_64-w64-mingw32'
|
prefix = 'x86_64-w64-mingw32'
|
||||||
|
|
||||||
[binaries]
|
[binaries]
|
||||||
c = prefix + '-gcc'
|
c = prefix + '-gcc-posix'
|
||||||
cpp = prefix + '-g++'
|
cpp = prefix + '-g++-posix'
|
||||||
strip = prefix + '-strip'
|
strip = prefix + '-strip'
|
||||||
windres = prefix + '-windres'
|
windres = prefix + '-windres'
|
||||||
|
|
||||||
|
123
.github/prepare.py
vendored
123
.github/prepare.py
vendored
@ -6,7 +6,6 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
ref = os.getenv('GITHUB_REF')
|
ref = os.getenv('GITHUB_REF')
|
||||||
event_name = os.getenv('GITHUB_EVENT_NAME')
|
|
||||||
publish_hostport = os.getenv('PUBLISH_HOSTPORT')
|
publish_hostport = os.getenv('PUBLISH_HOSTPORT')
|
||||||
|
|
||||||
def set_output(key, value):
|
def set_output(key, value):
|
||||||
@ -17,35 +16,25 @@ match_stable = re.fullmatch(r'refs/tags/v([0-9]+)\.([0-9]+)\.([0-9]+)', ref)
|
|||||||
match_beta = re.fullmatch(r'refs/tags/v([0-9]+)\.([0-9]+)\.([0-9]+)b', ref)
|
match_beta = re.fullmatch(r'refs/tags/v([0-9]+)\.([0-9]+)\.([0-9]+)b', ref)
|
||||||
match_snapshot = re.fullmatch(r'refs/tags/snapshot-([0-9]+)', ref)
|
match_snapshot = re.fullmatch(r'refs/tags/snapshot-([0-9]+)', ref)
|
||||||
match_tptlibsdev = re.fullmatch(r'refs/heads/tptlibsdev-(.*)', ref)
|
match_tptlibsdev = re.fullmatch(r'refs/heads/tptlibsdev-(.*)', ref)
|
||||||
match_alljobs = re.fullmatch(r'refs/heads/(.*)-alljobs', ref)
|
|
||||||
do_release = False
|
do_release = False
|
||||||
do_priority = 10
|
|
||||||
if event_name == 'pull_request':
|
|
||||||
do_priority = 0
|
|
||||||
if match_stable:
|
if match_stable:
|
||||||
release_type = 'stable'
|
release_type = 'stable'
|
||||||
release_name = 'v%s.%s.%s' % (match_stable.group(1), match_stable.group(2), match_stable.group(3))
|
release_name = 'v%s.%s.%s' % (match_stable.group(1), match_stable.group(2), match_stable.group(3))
|
||||||
do_release = True
|
do_release = True
|
||||||
do_priority = 0
|
|
||||||
elif match_beta:
|
elif match_beta:
|
||||||
release_type = 'beta'
|
release_type = 'beta'
|
||||||
release_name = 'v%s.%s.%sb' % (match_beta.group(1), match_beta.group(2), match_beta.group(3))
|
release_name = 'v%s.%s.%sb' % (match_beta.group(1), match_beta.group(2), match_beta.group(3))
|
||||||
do_release = True
|
do_release = True
|
||||||
do_priority = 0
|
|
||||||
elif match_snapshot:
|
elif match_snapshot:
|
||||||
release_type = 'snapshot'
|
release_type = 'snapshot'
|
||||||
release_name = 'snapshot-%s' % match_snapshot.group(1)
|
release_name = 'snapshot-%s' % match_snapshot.group(1)
|
||||||
do_release = True
|
do_release = True
|
||||||
do_priority = 0
|
|
||||||
elif match_tptlibsdev:
|
elif match_tptlibsdev:
|
||||||
release_type = 'tptlibsdev'
|
release_type = 'tptlibsdev'
|
||||||
release_name = 'tptlibsdev-%s' % match_tptlibsdev.group(1)
|
release_name = 'tptlibsdev-%s' % match_tptlibsdev.group(1)
|
||||||
do_priority = 0
|
|
||||||
else:
|
else:
|
||||||
release_type = 'dev'
|
release_type = 'dev'
|
||||||
release_name = 'dev'
|
release_name = 'dev'
|
||||||
if match_alljobs:
|
|
||||||
do_priority = 0
|
|
||||||
do_publish = publish_hostport and do_release
|
do_publish = publish_hostport and do_release
|
||||||
|
|
||||||
set_output('release_type', release_type)
|
set_output('release_type', release_type)
|
||||||
@ -62,23 +51,16 @@ if int(build_options['mod_id']) == 0 and os.path.exists('.github/mod_id.txt'):
|
|||||||
build_options['mod_id'] = f.read()
|
build_options['mod_id'] = f.read()
|
||||||
|
|
||||||
if int(build_options['mod_id']) == 0:
|
if int(build_options['mod_id']) == 0:
|
||||||
if release_type == 'stable':
|
if release_type == 'beta':
|
||||||
pass
|
|
||||||
elif release_type == 'beta':
|
|
||||||
build_options['app_name' ] += ' Beta'
|
build_options['app_name' ] += ' Beta'
|
||||||
build_options['app_comment'] += ' - Beta'
|
build_options['app_comment'] += ' - Beta'
|
||||||
build_options['app_exe' ] += 'beta'
|
build_options['app_exe' ] += 'beta'
|
||||||
build_options['app_id' ] += 'beta'
|
build_options['app_id' ] += 'beta'
|
||||||
elif release_type == 'snapshot':
|
if release_type == 'snapshot':
|
||||||
build_options['app_name' ] += ' Snapshot'
|
build_options['app_name' ] += ' Snapshot'
|
||||||
build_options['app_comment'] += ' - Snapshot'
|
build_options['app_comment'] += ' - Snapshot'
|
||||||
build_options['app_exe' ] += 'snapshot'
|
build_options['app_exe' ] += 'snapshot'
|
||||||
build_options['app_id' ] += 'snapshot'
|
build_options['app_id' ] += 'snapshot'
|
||||||
else:
|
|
||||||
build_options['app_name' ] += ' Dev'
|
|
||||||
build_options['app_comment'] += ' - Dev'
|
|
||||||
build_options['app_exe' ] += 'dev'
|
|
||||||
build_options['app_id' ] += 'dev'
|
|
||||||
|
|
||||||
set_output('mod_id' , build_options['mod_id' ])
|
set_output('mod_id' , build_options['mod_id' ])
|
||||||
set_output('app_name' , build_options['app_name' ])
|
set_output('app_name' , build_options['app_name' ])
|
||||||
@ -95,61 +77,47 @@ app_name_slug = re.sub('[^A-Za-z0-9]', '_', app_name)
|
|||||||
build_matrix = []
|
build_matrix = []
|
||||||
publish_matrix = []
|
publish_matrix = []
|
||||||
# consider disabling line wrapping to edit this monstrosity
|
# consider disabling line wrapping to edit this monstrosity
|
||||||
for arch, platform, libc, statdyn, bplatform, runson, suffix, publish, artifact, dbgsuffix, mode, starcatcher, dbgrel, priority in [
|
for arch, platform, libc, statdyn, bplatform, runson, suffix, publish, artifact, dbgsuffix, mode, starcatcher, dbgrel in [
|
||||||
( 'x86_64', 'linux', 'gnu', 'static', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug', 0 ), # priority = 0: static debug build
|
( 'x86_64', 'linux', 'gnu', 'static', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug' ),
|
||||||
( 'x86_64', 'linux', 'gnu', 'static', 'linux', 'ubuntu-20.04', '', True, True, '.dbg', None, 'x86_64-lin-gcc-static', 'release', 10 ),
|
( 'x86_64', 'linux', 'gnu', 'static', 'linux', 'ubuntu-20.04', '', True, True, '.dbg', None, 'x86_64-lin-gcc-static', 'release' ),
|
||||||
( 'x86_64', 'linux', 'gnu', 'static', 'linux', 'ubuntu-20.04', '', False, True, '.dbg', 'appimage', None, 'release', 0 ), # priority = 0: appimage release
|
( 'x86_64', 'linux', 'gnu', 'static', 'linux', 'ubuntu-20.04', '', False, True, '.dbg', 'appimage', None, 'release' ),
|
||||||
( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug', 10 ),
|
( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug' ),
|
||||||
( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, 'nohttp', None, 'debug', 10 ),
|
( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, 'nohttp', None, 'debug' ),
|
||||||
( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, 'nolua', None, 'debug', 10 ),
|
( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, 'nolua', None, 'debug' ),
|
||||||
( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'release', 10 ),
|
( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'release' ),
|
||||||
# ( 'x86_64', 'windows', 'mingw', 'static', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug', 10 ), # ubuntu-20.04 doesn't have windows TLS headers somehow and I haven't yet figured out how to get them; worse, it's a different toolchain
|
# ( 'x86_64', 'windows', 'mingw', 'static', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug' ), # ubuntu-20.04 doesn't have windows TLS headers somehow and I haven't yet figured out how to get them
|
||||||
# ( 'x86_64', 'windows', 'mingw', 'static', 'linux', 'ubuntu-20.04', '', False, True, '.dbg', None, None, 'release', 10 ), # ubuntu-20.04 doesn't have windows TLS headers somehow and I haven't yet figured out how to get them; worse, it's a different toolchain
|
# ( 'x86_64', 'windows', 'mingw', 'static', 'linux', 'ubuntu-20.04', '', False, True, '.dbg', None, None, 'release' ), # ubuntu-20.04 doesn't have windows TLS headers somehow and I haven't yet figured out how to get them
|
||||||
# ( 'x86_64', 'windows', 'mingw', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug', 10 ), # ubuntu-20.04 doesn't have ucrt64-capable mingw >_>
|
( 'x86_64', 'windows', 'mingw', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug' ),
|
||||||
# ( 'x86_64', 'windows', 'mingw', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'release', 10 ), # ubuntu-20.04 doesn't have ucrt64-capable mingw >_>
|
( 'x86_64', 'windows', 'mingw', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'release' ),
|
||||||
( 'x86_64', 'windows', 'mingw', 'static', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug', 0 ), # priority = 0: static debug build
|
( 'x86_64', 'windows', 'mingw', 'static', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug' ),
|
||||||
( 'x86_64', 'windows', 'mingw', 'static', 'windows', 'windows-2019', '.exe', False, True, '.dbg', None, None, 'release', 10 ),
|
( 'x86_64', 'windows', 'mingw', 'static', 'windows', 'windows-2019', '.exe', False, True, '.dbg', None, None, 'release' ),
|
||||||
( 'x86_64', 'windows', 'mingw', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug', 10 ),
|
( 'x86_64', 'windows', 'mingw', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug' ),
|
||||||
( 'x86_64', 'windows', 'mingw', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'release', 10 ),
|
( 'x86_64', 'windows', 'mingw', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'release' ),
|
||||||
( 'x86_64', 'windows', 'msvc', 'static', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug', 0 ), # priority = 0: static debug build
|
( 'x86_64', 'windows', 'msvc', 'static', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug' ),
|
||||||
( 'x86_64', 'windows', 'msvc', 'static', 'windows', 'windows-2019', '.exe', True, True, '.pdb', None,'x86_64-win-msvc-static', 'release', 10 ),
|
( 'x86_64', 'windows', 'msvc', 'static', 'windows', 'windows-2019', '.exe', True, True, '.pdb', None,'x86_64-win-msvc-static', 'release' ),
|
||||||
( 'x86_64', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug', 10 ),
|
( 'x86_64', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug' ),
|
||||||
# ( 'x86_64', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, 'backendvs', None, 'debug', 0 ), # priority = 0: backend=vs build
|
( 'x86_64', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'release' ),
|
||||||
( 'x86_64', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'release', 10 ),
|
( 'x86', 'windows', 'msvc', 'static', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug' ),
|
||||||
( 'x86', 'windows', 'msvc', 'static', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug', 0 ), # priority = 0: static debug build
|
( 'x86', 'windows', 'msvc', 'static', 'windows', 'windows-2019', '.exe', True, True, '.pdb', None, 'i686-win-msvc-static', 'release' ),
|
||||||
( 'x86', 'windows', 'msvc', 'static', 'windows', 'windows-2019', '.exe', True, True, '.pdb', None, 'i686-win-msvc-static', 'release', 10 ),
|
( 'x86', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug' ),
|
||||||
( 'x86', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug', 10 ),
|
( 'x86', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'release' ),
|
||||||
( 'x86', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'release', 10 ),
|
( 'x86_64', 'darwin', 'macos', 'static', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'debug' ),
|
||||||
( 'x86_64', 'darwin', 'macos', 'static', 'darwin', 'macos-12', '.dmg', False, False, None, 'dmg', None, 'debug', 0 ), # priority = 0: static debug build
|
( 'x86_64', 'darwin', 'macos', 'static', 'darwin', 'macos-11.0', '.dmg', True, True, None, 'dmg', 'x86_64-mac-gcc-static', 'release' ), # I have no idea how to separate debug info on macos
|
||||||
( 'x86_64', 'darwin', 'macos', 'static', 'darwin', 'macos-12', '.dmg', True, True, None, 'dmg', 'x86_64-mac-gcc-static', 'release', 10 ), # I have no idea how to separate debug info on macos
|
( 'x86_64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'debug' ),
|
||||||
( 'x86_64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-12', '.dmg', False, False, None, 'dmg', None, 'debug', 10 ),
|
( 'x86_64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'release' ),
|
||||||
( 'x86_64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-12', '.dmg', False, False, None, 'dmg', None, 'release', 10 ),
|
( 'aarch64', 'darwin', 'macos', 'static', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'debug' ),
|
||||||
( 'aarch64', 'darwin', 'macos', 'static', 'darwin', 'macos-12', '.dmg', False, False, None, 'dmg', None, 'debug', 0 ), # priority = 0: static debug build
|
( 'aarch64', 'darwin', 'macos', 'static', 'darwin', 'macos-11.0', '.dmg', True, True, None, 'dmg', 'arm64-mac-gcc-static', 'release' ),
|
||||||
( 'aarch64', 'darwin', 'macos', 'static', 'darwin', 'macos-12', '.dmg', True, True, None, 'dmg', 'arm64-mac-gcc-static', 'release', 10 ),
|
# ( 'aarch64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'debug' ), # macos-11.0 is x86_64 and I haven't yet figured out how to get homebrew to install aarch64 libs on x86_64
|
||||||
# ( 'aarch64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-12', '.dmg', False, False, None, 'dmg', None, 'debug', 10 ), # macos-11.0 is x86_64 and I haven't yet figured out how to get homebrew to install aarch64 libs on x86_64
|
# ( 'aarch64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'release' ), # macos-11.0 is x86_64 and I haven't yet figured out how to get homebrew to install aarch64 libs on x86_64
|
||||||
# ( 'aarch64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-12', '.dmg', False, False, None, 'dmg', None, 'release', 10 ), # macos-11.0 is x86_64 and I haven't yet figured out how to get homebrew to install aarch64 libs on x86_64
|
( 'x86', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', False, False, None, None, None, 'debug' ),
|
||||||
( 'x86', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', False, False, None, None, None, 'debug', 0 ), # priority = 0: rarely used debug build
|
( 'x86', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', True, True, '.dbg', None, 'i686-and-gcc-static', 'release' ),
|
||||||
( 'x86', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', True, True, '.dbg', None, 'i686-and-gcc-static', 'release', 10 ),
|
( 'x86_64', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', False, False, None, None, None, 'debug' ),
|
||||||
( 'x86_64', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', False, False, None, None, None, 'debug', 0 ), # priority = 0: rarely used debug build
|
( 'x86_64', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', True, True, '.dbg', None, 'x86_64-and-gcc-static', 'release' ),
|
||||||
( 'x86_64', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', True, True, '.dbg', None, 'x86_64-and-gcc-static', 'release', 10 ),
|
( 'arm', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', False, False, None, None, None, 'debug' ),
|
||||||
( 'arm', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', False, False, None, None, None, 'debug', 0 ), # priority = 0: rarely used debug build
|
( 'arm', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', True, True, '.dbg', None, 'arm-and-gcc-static', 'release' ),
|
||||||
( 'arm', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', True, True, '.dbg', None, 'arm-and-gcc-static', 'release', 10 ),
|
( 'aarch64', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', False, False, None, None, None, 'debug' ),
|
||||||
( 'aarch64', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', False, False, None, None, None, 'debug', 0 ), # priority = 0: rarely used debug build
|
( 'aarch64', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', True, True, '.dbg', None, 'arm64-and-gcc-static', 'release' ),
|
||||||
( 'aarch64', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', True, True, '.dbg', None, 'arm64-and-gcc-static', 'release', 10 ),
|
|
||||||
( 'wasm32', 'emscripten', 'emscripten', 'static', 'linux', 'ubuntu-20.04', '.tar', False, False, None, None, None, 'debug', 0 ), # priority = 0: rarely used debug build
|
|
||||||
( 'wasm32', 'emscripten', 'emscripten', 'static', 'linux', 'ubuntu-20.04', '.tar', True, True, None, 'emscripten', 'wasm32-ems-static', 'release', 10 ), # I have no idea how to separate debug info on emscripten
|
|
||||||
]:
|
]:
|
||||||
if priority < do_priority:
|
|
||||||
continue
|
|
||||||
job_name = f'build'
|
|
||||||
if starcatcher:
|
|
||||||
job_name += f'+target=starcatcher-{starcatcher}'
|
|
||||||
else:
|
|
||||||
job_name += f'+target={arch}-{platform}-{libc}-{statdyn}-{dbgrel}'
|
|
||||||
if mode:
|
|
||||||
job_name += f'+mode={mode}'
|
|
||||||
if bplatform != platform:
|
|
||||||
job_name += f'+bplatform={bplatform}'
|
|
||||||
if not mode:
|
if not mode:
|
||||||
mode = 'default'
|
mode = 'default'
|
||||||
separate_debug = True
|
separate_debug = True
|
||||||
@ -173,10 +141,6 @@ for arch, platform, libc, statdyn, bplatform, runso
|
|||||||
debug_asset_path = f'{app_name_slug}-{arch}.AppImage.dbg'
|
debug_asset_path = f'{app_name_slug}-{arch}.AppImage.dbg'
|
||||||
debug_asset_name = f'{app_name_slug}-{arch}.AppImage.dbg'
|
debug_asset_name = f'{app_name_slug}-{arch}.AppImage.dbg'
|
||||||
starcatcher_name = f'powder-{release_name}-{starcatcher}{suffix}'
|
starcatcher_name = f'powder-{release_name}-{starcatcher}{suffix}'
|
||||||
msys2_bash = (bplatform == 'windows' and libc == 'mingw')
|
|
||||||
shell = 'bash'
|
|
||||||
if msys2_bash:
|
|
||||||
shell = 'msys2 {0}'
|
|
||||||
build_matrix.append({
|
build_matrix.append({
|
||||||
'bsh_build_platform': bplatform, # part of the unique portion of the matrix
|
'bsh_build_platform': bplatform, # part of the unique portion of the matrix
|
||||||
'bsh_host_arch': arch, # part of the unique portion of the matrix
|
'bsh_host_arch': arch, # part of the unique portion of the matrix
|
||||||
@ -185,7 +149,6 @@ for arch, platform, libc, statdyn, bplatform, runso
|
|||||||
'bsh_static_dynamic': statdyn, # part of the unique portion of the matrix
|
'bsh_static_dynamic': statdyn, # part of the unique portion of the matrix
|
||||||
'bsh_debug_release': dbgrel, # part of the unique portion of the matrix
|
'bsh_debug_release': dbgrel, # part of the unique portion of the matrix
|
||||||
'runs_on': runson,
|
'runs_on': runson,
|
||||||
'force_msys2_bash': msys2_bash and 'yes' or 'no',
|
|
||||||
'package_suffix': suffix,
|
'package_suffix': suffix,
|
||||||
'package_mode': mode,
|
'package_mode': mode,
|
||||||
'publish': publish and 'yes' or 'no',
|
'publish': publish and 'yes' or 'no',
|
||||||
@ -195,8 +158,6 @@ for arch, platform, libc, statdyn, bplatform, runso
|
|||||||
'asset_name': asset_name,
|
'asset_name': asset_name,
|
||||||
'debug_asset_path': debug_asset_path,
|
'debug_asset_path': debug_asset_path,
|
||||||
'debug_asset_name': debug_asset_name,
|
'debug_asset_name': debug_asset_name,
|
||||||
'job_name': job_name,
|
|
||||||
'shell': shell,
|
|
||||||
})
|
})
|
||||||
if publish:
|
if publish:
|
||||||
publish_matrix.append({
|
publish_matrix.append({
|
||||||
|
16
.github/starcatcher-publish.sh
vendored
16
.github/starcatcher-publish.sh
vendored
@ -3,18 +3,4 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
IFS=$'\n\t'
|
IFS=$'\n\t'
|
||||||
|
|
||||||
cat << NETRC > ~/.netrc
|
LFTP_PASSWORD=$PUBLISH_PASSWORD lftp -c "open --user '$PUBLISH_USERNAME' --env-password -e 'set ftp:ssl-protect-data true; set ssl:verify-certificate false; cd "${PUBLISH_DIRECTORY:-.}"; put \"$PUBLISH_FILENAME\";' ftp://$PUBLISH_HOSTPORT"
|
||||||
machine $(echo $PUBLISH_HOSTPORT | cut -d ':' -f 1)
|
|
||||||
login $PUBLISH_USERNAME
|
|
||||||
password $PUBLISH_PASSWORD
|
|
||||||
NETRC
|
|
||||||
chmod 660 ~/.netrc
|
|
||||||
|
|
||||||
mountpoint=ftpmnt
|
|
||||||
mkdir $mountpoint
|
|
||||||
curlftpfs "$PUBLISH_HOSTPORT" $mountpoint -o ssl,ciphers='ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-GCM-SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256'
|
|
||||||
if [[ -z ${PUBLISH_ACCESSCHECK-} ]]; then
|
|
||||||
cp $PUBLISH_FILENAME $mountpoint/${PUBLISH_DIRECTORY:-.}/
|
|
||||||
fi
|
|
||||||
fusermount -u $mountpoint
|
|
||||||
rmdir $mountpoint
|
|
||||||
|
12
.github/upload-release-asset.sh
vendored
12
.github/upload-release-asset.sh
vendored
@ -1,12 +0,0 @@
|
|||||||
set -euo pipefail
|
|
||||||
IFS=$'\t\n'
|
|
||||||
|
|
||||||
temp=.temp
|
|
||||||
mkdir $temp
|
|
||||||
cp $ASSET_PATH $temp/$ASSET_NAME
|
|
||||||
(
|
|
||||||
cd $temp
|
|
||||||
gh release upload $GITHUB_REF_NAME $ASSET_NAME
|
|
||||||
)
|
|
||||||
rm -r $temp
|
|
||||||
echo browser_download_url=https://github.com/$GITHUB_REPOSITORY/releases/download/$GITHUB_REF_NAME/$ASSET_NAME >> $GITHUB_OUTPUT
|
|
3
.github/vs-env.sh
vendored
Executable file → Normal file
3
.github/vs-env.sh
vendored
Executable file → Normal file
@ -24,8 +24,5 @@ IFS=$'\t\n'
|
|||||||
for i in $(MSYS_NO_PATHCONV=1 cmd /c "$vs_install_dir\\VC\\Auxiliary\\Build\\vcvarsall.bat" $VS_ENV_PARAMS \& env \& exit /b); do
|
for i in $(MSYS_NO_PATHCONV=1 cmd /c "$vs_install_dir\\VC\\Auxiliary\\Build\\vcvarsall.bat" $VS_ENV_PARAMS \& env \& exit /b); do
|
||||||
set +e
|
set +e
|
||||||
export "$i" 2>/dev/null
|
export "$i" 2>/dev/null
|
||||||
echo $i | grep ERROR
|
|
||||||
set -e
|
set -e
|
||||||
done
|
done
|
||||||
|
|
||||||
cl
|
|
||||||
|
96
.github/workflows/build.yaml
vendored
96
.github/workflows/build.yaml
vendored
@ -16,6 +16,7 @@ jobs:
|
|||||||
prepare:
|
prepare:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
|
release_upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
do_release: ${{ steps.prepare.outputs.do_release }}
|
do_release: ${{ steps.prepare.outputs.do_release }}
|
||||||
build_matrix: ${{ steps.prepare.outputs.build_matrix }}
|
build_matrix: ${{ steps.prepare.outputs.build_matrix }}
|
||||||
publish_matrix: ${{ steps.prepare.outputs.publish_matrix }}
|
publish_matrix: ${{ steps.prepare.outputs.publish_matrix }}
|
||||||
@ -31,76 +32,39 @@ jobs:
|
|||||||
do_publish: ${{ steps.prepare.outputs.do_publish }}
|
do_publish: ${{ steps.prepare.outputs.do_publish }}
|
||||||
steps:
|
steps:
|
||||||
- run: git config --global core.autocrlf false
|
- run: git config --global core.autocrlf false
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.10'
|
python-version: '3.10'
|
||||||
- run: python -m pip install meson==1.2.3 ninja # TODO: go back to using latest meson once https://github.com/mesonbuild/meson/pull/12544 is live
|
- run: python -m pip install meson ninja
|
||||||
- id: prepare
|
- id: prepare
|
||||||
run: python ./.github/prepare.py
|
run: python ./.github/prepare.py
|
||||||
env:
|
env:
|
||||||
PUBLISH_HOSTPORT: ${{ secrets.STARCATCHER_PUBLISH_HOSTPORT }}
|
PUBLISH_HOSTPORT: ${{ secrets.STARCATCHER_PUBLISH_HOSTPORT }}
|
||||||
GITHUB_REF: ${{ github.ref }}
|
GITHUB_REF: ${{ github.ref }}
|
||||||
- if: steps.prepare.outputs.do_publish == 'yes'
|
|
||||||
run: sudo apt update && sudo apt install curlftpfs && bash -c './.github/starcatcher-publish.sh'
|
|
||||||
env:
|
|
||||||
PUBLISH_HOSTPORT: ${{ secrets.STARCATCHER_PUBLISH_HOSTPORT }}
|
|
||||||
PUBLISH_USERNAME: ${{ secrets.STARCATCHER_PUBLISH_USERNAME }}
|
|
||||||
PUBLISH_PASSWORD: ${{ secrets.STARCATCHER_PUBLISH_PASSWORD }}
|
|
||||||
PUBLISH_ACCESSCHECK: yes
|
|
||||||
- if: steps.prepare.outputs.do_release == 'yes'
|
- if: steps.prepare.outputs.do_release == 'yes'
|
||||||
id: create_release
|
id: create_release
|
||||||
|
uses: LBPHacker/create-release@v2
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
RELEASE_NAME: ${{ steps.prepare.outputs.release_name }}
|
with:
|
||||||
run: bash -c './.github/create-release.sh'
|
tag_name: ${{ github.ref }}
|
||||||
|
release_name: ${{ steps.prepare.outputs.release_name }}
|
||||||
|
draft: true
|
||||||
|
prerelease: false
|
||||||
build:
|
build:
|
||||||
runs-on: ${{ matrix.runs_on }}
|
runs-on: ${{ matrix.runs_on }}
|
||||||
name: ${{ matrix.job_name }}
|
|
||||||
needs: [prepare]
|
needs: [prepare]
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix: ${{ fromJSON(needs.prepare.outputs.build_matrix) }}
|
matrix: ${{ fromJSON(needs.prepare.outputs.build_matrix) }}
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
shell: ${{ matrix.shell }}
|
|
||||||
steps:
|
steps:
|
||||||
- if: matrix.force_msys2_bash == 'yes'
|
|
||||||
uses: msys2/setup-msys2@v2
|
|
||||||
with:
|
|
||||||
msystem: UCRT64
|
|
||||||
update: true
|
|
||||||
path-type: strict
|
|
||||||
cache: true
|
|
||||||
# this list doesn't have to mirror the one in build.sh perfectly
|
|
||||||
# but the packages listed here get cached properly and take less time to install
|
|
||||||
install: >-
|
|
||||||
git
|
|
||||||
curl
|
|
||||||
mingw-w64-ucrt-x86_64-gcc
|
|
||||||
mingw-w64-ucrt-x86_64-pkgconf
|
|
||||||
mingw-w64-ucrt-x86_64-bzip2
|
|
||||||
mingw-w64-ucrt-x86_64-luajit
|
|
||||||
mingw-w64-ucrt-x86_64-jsoncpp
|
|
||||||
mingw-w64-ucrt-x86_64-curl
|
|
||||||
mingw-w64-ucrt-x86_64-SDL2
|
|
||||||
mingw-w64-ucrt-x86_64-libpng
|
|
||||||
mingw-w64-ucrt-x86_64-meson
|
|
||||||
mingw-w64-ucrt-x86_64-python
|
|
||||||
mingw-w64-ucrt-x86_64-python-pip
|
|
||||||
mingw-w64-ucrt-x86_64-fftw
|
|
||||||
mingw-w64-ucrt-x86_64-cmake
|
|
||||||
mingw-w64-ucrt-x86_64-7zip
|
|
||||||
mingw-w64-ucrt-x86_64-jq
|
|
||||||
patch
|
|
||||||
- run: git config --global core.autocrlf false
|
- run: git config --global core.autocrlf false
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- if: matrix.force_msys2_bash != 'yes'
|
- uses: actions/setup-python@v4
|
||||||
uses: actions/setup-python@v5
|
|
||||||
with:
|
with:
|
||||||
python-version: '3.10'
|
python-version: '3.10'
|
||||||
- if: matrix.force_msys2_bash != 'yes'
|
- run: python -m pip install meson ninja
|
||||||
run: python -m pip install meson==1.2.3 ninja # TODO: go back to using latest meson once https://github.com/mesonbuild/meson/pull/12544 is live
|
|
||||||
- if: matrix.bsh_build_platform == 'darwin'
|
- if: matrix.bsh_build_platform == 'darwin'
|
||||||
run: brew install bash coreutils
|
run: brew install bash coreutils
|
||||||
- run: bash -c './.github/build.sh'
|
- run: bash -c './.github/build.sh'
|
||||||
@ -124,24 +88,30 @@ jobs:
|
|||||||
PACKAGE_MODE: ${{ matrix.package_mode }}
|
PACKAGE_MODE: ${{ matrix.package_mode }}
|
||||||
ASSET_PATH: ${{ matrix.asset_path }}
|
ASSET_PATH: ${{ matrix.asset_path }}
|
||||||
DEBUG_ASSET_PATH: ${{ matrix.debug_asset_path }}
|
DEBUG_ASSET_PATH: ${{ matrix.debug_asset_path }}
|
||||||
- if: needs.prepare.outputs.do_release == 'yes' && matrix.publish == 'yes' # TODO-NTL: ship licenses
|
- uses: LBPHacker/upload-release-asset@v2 # TODO-NTL: ship licenses
|
||||||
|
if: needs.prepare.outputs.do_release == 'yes' && matrix.publish == 'yes'
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
ASSET_PATH: build/${{ matrix.asset_path }}
|
with:
|
||||||
ASSET_NAME: ${{ matrix.asset_name }}
|
upload_url: ${{ needs.prepare.outputs.release_upload_url }}
|
||||||
run: bash -c './.github/upload-release-asset.sh'
|
asset_path: build/${{ matrix.asset_path }}
|
||||||
- if: needs.prepare.outputs.do_release == 'yes' && matrix.publish == 'yes' && matrix.separate_debug == 'yes'
|
asset_name: ${{ matrix.asset_name }}
|
||||||
|
asset_content_type: application/zip
|
||||||
|
- uses: LBPHacker/upload-release-asset@v2
|
||||||
|
if: needs.prepare.outputs.do_release == 'yes' && matrix.publish == 'yes' && matrix.separate_debug == 'yes'
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
ASSET_PATH: build/${{ matrix.debug_asset_path }}
|
with:
|
||||||
ASSET_NAME: ${{ matrix.debug_asset_name }}
|
upload_url: ${{ needs.prepare.outputs.release_upload_url }}
|
||||||
run: bash -c './.github/upload-release-asset.sh'
|
asset_path: build/${{ matrix.debug_asset_path }}
|
||||||
- uses: actions/upload-artifact@v4
|
asset_name: ${{ matrix.debug_asset_name }}
|
||||||
|
asset_content_type: application/zip
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
if: matrix.artifact == 'yes'
|
if: matrix.artifact == 'yes'
|
||||||
with:
|
with:
|
||||||
path: build/${{ matrix.asset_path }}
|
path: build/${{ matrix.asset_path }}
|
||||||
name: ${{ matrix.asset_name }}
|
name: ${{ matrix.asset_name }}
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v3
|
||||||
if: matrix.artifact == 'yes' && matrix.separate_debug == 'yes'
|
if: matrix.artifact == 'yes' && matrix.separate_debug == 'yes'
|
||||||
with:
|
with:
|
||||||
path: build/${{ matrix.debug_asset_path }}
|
path: build/${{ matrix.debug_asset_path }}
|
||||||
@ -155,12 +125,12 @@ jobs:
|
|||||||
if: needs.prepare.outputs.do_publish == 'yes'
|
if: needs.prepare.outputs.do_publish == 'yes'
|
||||||
steps:
|
steps:
|
||||||
- run: git config --global core.autocrlf false
|
- run: git config --global core.autocrlf false
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.asset_name }}
|
name: ${{ matrix.asset_name }}
|
||||||
- run: mv ${{ matrix.asset_path }} ${{ matrix.starcatcher_name }}
|
- run: mv ${{ matrix.asset_path }} ${{ matrix.starcatcher_name }}
|
||||||
- run: sudo apt update && sudo apt install curlftpfs && bash -c './.github/starcatcher-publish.sh'
|
- run: sudo apt update && sudo apt install lftp && bash -c './.github/starcatcher-publish.sh'
|
||||||
env:
|
env:
|
||||||
PUBLISH_HOSTPORT: ${{ secrets.STARCATCHER_PUBLISH_HOSTPORT }}
|
PUBLISH_HOSTPORT: ${{ secrets.STARCATCHER_PUBLISH_HOSTPORT }}
|
||||||
PUBLISH_USERNAME: ${{ secrets.STARCATCHER_PUBLISH_USERNAME }}
|
PUBLISH_USERNAME: ${{ secrets.STARCATCHER_PUBLISH_USERNAME }}
|
||||||
@ -173,7 +143,7 @@ jobs:
|
|||||||
if: needs.prepare.outputs.do_publish == 'yes'
|
if: needs.prepare.outputs.do_publish == 'yes'
|
||||||
steps:
|
steps:
|
||||||
- run: git config --global core.autocrlf false
|
- run: git config --global core.autocrlf false
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- run: ./.github/starcatcher-release.sh
|
- run: ./.github/starcatcher-release.sh
|
||||||
env:
|
env:
|
||||||
RELEASE_NAME: ${{ needs.prepare.outputs.release_name }}
|
RELEASE_NAME: ${{ needs.prepare.outputs.release_name }}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
The Powder Toy - April 2024
|
The Powder Toy - January 2023
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
Get the latest version [from the Powder Toy website](https://powdertoy.co.uk/Download.html).
|
Get the latest version [from the Powder Toy website](https://powdertoy.co.uk/Download.html).
|
||||||
@ -25,7 +25,7 @@ Thanks
|
|||||||
* Skresanov Savely
|
* Skresanov Savely
|
||||||
* cracker64
|
* cracker64
|
||||||
* Catelite
|
* Catelite
|
||||||
* Victoria Hoyle
|
* Bryan Hoyle
|
||||||
* Nathan Cousins
|
* Nathan Cousins
|
||||||
* jacksonmj
|
* jacksonmj
|
||||||
* Felix Wallin
|
* Felix Wallin
|
||||||
@ -82,7 +82,6 @@ Controls
|
|||||||
| I | Invert Pressure and Velocity map |
|
| I | Invert Pressure and Velocity map |
|
||||||
| W | Cycle gravity modes (use with Ctrl when STK2 is out) |
|
| W | Cycle gravity modes (use with Ctrl when STK2 is out) |
|
||||||
| Y | Cycle air modes |
|
| Y | Cycle air modes |
|
||||||
| Ctrl + E | Cycle edge modes |
|
|
||||||
| B | Enter decoration editor menu |
|
| B | Enter decoration editor menu |
|
||||||
| Ctrl + B | Toggle decorations on/off |
|
| Ctrl + B | Toggle decorations on/off |
|
||||||
| N | Toggle Newtonian Gravity on/off |
|
| N | Toggle Newtonian Gravity on/off |
|
||||||
@ -108,7 +107,6 @@ Controls
|
|||||||
| Shift + R | Horizontal mirror for selected area when pasting stamps |
|
| Shift + R | Horizontal mirror for selected area when pasting stamps |
|
||||||
| Ctrl + Shift + R | Vertical mirror for selected area when pasting stamps |
|
| Ctrl + Shift + R | Vertical mirror for selected area when pasting stamps |
|
||||||
| R | Rotate selected area counterclockwise when pasting stamps |
|
| R | Rotate selected area counterclockwise when pasting stamps |
|
||||||
| F11 | Toggle fullscreen |
|
|
||||||
|
|
||||||
Command Line
|
Command Line
|
||||||
---------------------------------------------------------------------------
|
---------------------------------------------------------------------------
|
||||||
|
@ -2,12 +2,12 @@
|
|||||||
<manifest
|
<manifest
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="@APPID@"
|
package="@APPID@"
|
||||||
android:versionCode="@BUILD_NUM@"
|
android:versionCode="1"
|
||||||
android:versionName="@DISPLAY_VERSION_MAJOR@.@DISPLAY_VERSION_MINOR@.@BUILD_NUM@"
|
android:versionName="1.0"
|
||||||
android:installLocation="auto"
|
android:installLocation="auto"
|
||||||
>
|
>
|
||||||
<uses-sdk
|
<uses-sdk
|
||||||
android:minSdkVersion="21"
|
android:minSdkVersion="19"
|
||||||
android:targetSdkVersion="30"
|
android:targetSdkVersion="30"
|
||||||
/>
|
/>
|
||||||
<uses-feature
|
<uses-feature
|
||||||
@ -33,18 +33,16 @@
|
|||||||
android:name="android.hardware.type.pc"
|
android:name="android.hardware.type.pc"
|
||||||
android:required="false"
|
android:required="false"
|
||||||
/>
|
/>
|
||||||
@ANDROID_PERMISSIONS@
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
<application
|
<application
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
|
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
|
||||||
android:hardwareAccelerated="true"
|
android:hardwareAccelerated="true"
|
||||||
@ANDROID_PROPERTIES@
|
|
||||||
>
|
>
|
||||||
<activity
|
<activity
|
||||||
android:name=".PowderActivity"
|
android:name=".PowderActivity"
|
||||||
android:screenOrientation="landscape"
|
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:alwaysRetainTaskState="true"
|
android:alwaysRetainTaskState="true"
|
||||||
android:launchMode="singleInstance"
|
android:launchMode="singleInstance"
|
||||||
|
@ -1,42 +1,7 @@
|
|||||||
package @APPID@;
|
package @APPID@;
|
||||||
|
|
||||||
import org.libsdl.app.SDLActivity;
|
import org.libsdl.app.SDLActivity;
|
||||||
import java.security.KeyStore;
|
|
||||||
import java.security.KeyStoreException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Base64;
|
|
||||||
|
|
||||||
public class PowderActivity extends SDLActivity
|
public class PowderActivity extends SDLActivity
|
||||||
{
|
{
|
||||||
public static String getCertificateBundle()
|
|
||||||
{
|
|
||||||
String allPems = "";
|
|
||||||
try {
|
|
||||||
KeyStore ks = KeyStore.getInstance("AndroidCAStore");
|
|
||||||
if (ks != null) {
|
|
||||||
ks.load(null, null);
|
|
||||||
Enumeration<String> aliases = ks.aliases();
|
|
||||||
while (aliases.hasMoreElements()) {
|
|
||||||
String alias = (String)aliases.nextElement();
|
|
||||||
java.security.cert.X509Certificate cert = (java.security.cert.X509Certificate)ks.getCertificate(alias);
|
|
||||||
allPems += "-----BEGIN CERTIFICATE-----\n" + Base64.getMimeEncoder().encodeToString(cert.getEncoded()) + "\n-----END CERTIFICATE-----\n";;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return "";
|
|
||||||
} catch (KeyStoreException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return "";
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return "";
|
|
||||||
} catch (java.security.cert.CertificateException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return allPems;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[constants]
|
[constants]
|
||||||
android_ndk_toolchain_prefix = 'armv7a-linux-androideabi21-'
|
android_ndk_toolchain_prefix = 'armv7a-linux-androideabi19-'
|
||||||
|
|
||||||
[host_machine]
|
[host_machine]
|
||||||
system = 'android'
|
system = 'android'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[constants]
|
[constants]
|
||||||
android_ndk_toolchain_prefix = 'i686-linux-android21-'
|
android_ndk_toolchain_prefix = 'i686-linux-android19-'
|
||||||
|
|
||||||
[host_machine]
|
[host_machine]
|
||||||
system = 'android'
|
system = 'android'
|
||||||
|
272
android/debug.sh
272
android/debug.sh
@ -1,272 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# mainly based on https://www.sh-zam.com/2019/05/debugging-krita-on-android.html
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
IFS=$'\n\t'
|
|
||||||
|
|
||||||
function get_buildoption() {
|
|
||||||
jq -r '.[] | select(.name == "'$1'") | .value' < meson-info/intro-buildoptions.json
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_cpp_compiler() {
|
|
||||||
jq -r '.[] | select(.name == "'$1'") | .target_sources.[] | select(.language == "cpp") | .compiler.[]' < meson-info/intro-targets.json
|
|
||||||
}
|
|
||||||
|
|
||||||
# customize
|
|
||||||
default_app_id=uk.co.powdertoy.tpt
|
|
||||||
default_app_exe=powder
|
|
||||||
default_lldb_server=/opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/17/lib/linux/aarch64/lldb-server
|
|
||||||
default_lldb_client=/opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/lldb.sh
|
|
||||||
in_build_site=no
|
|
||||||
if which jq >/dev/null && [[ -f meson-info/intro-buildoptions.json ]]; then
|
|
||||||
>&2 echo "[+] pwd is a build site, auto-detecting parameters"
|
|
||||||
in_build_site=yes
|
|
||||||
default_app_id=$(get_buildoption app_id)
|
|
||||||
default_app_exe=$(get_buildoption app_exe)
|
|
||||||
found_lldb=no
|
|
||||||
compiler_path=$(get_cpp_compiler $default_app_exe)
|
|
||||||
for line in $compiler_path; do
|
|
||||||
# iterate over the command array (might be for example [ "ccache", "aarch64-linux-android21-clang++" ])
|
|
||||||
if (echo $line | grep toolchains && basename $line | grep android | grep clang++) >/dev/null; then
|
|
||||||
arch=$(basename $line | cut -d '-' -f 1)
|
|
||||||
default_lldb_server=$(realpath $(dirname $line)/../lib/clang/*/lib/linux/$arch/lldb-server)
|
|
||||||
default_lldb_client=$(realpath $(dirname $line)/lldb.sh)
|
|
||||||
found_lldb=yes
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
if [[ $found_lldb != yes ]]; then
|
|
||||||
>&2 echo "[-] cannot determine LLDB paths from compiler command array:"
|
|
||||||
for line in $compiler_path; do
|
|
||||||
>&2 echo "[-] - $line"
|
|
||||||
done
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
>&2 echo "[+] APP_ID: $default_app_id"
|
|
||||||
>&2 echo "[+] APP_EXE: $default_app_exe"
|
|
||||||
>&2 echo "[+] LLDB_SERVER: $default_lldb_server"
|
|
||||||
>&2 echo "[+] LLDB_CLIENT: $default_lldb_client"
|
|
||||||
else
|
|
||||||
>&2 echo "[+] pwd is not a build site, not auto-detecting parameters"
|
|
||||||
fi
|
|
||||||
app_id=${APP_ID:-$default_app_id}
|
|
||||||
app_exe=${APP_EXE:-$default_app_exe}
|
|
||||||
lldb_server=${LLDB_SERVER:-$default_lldb_server}
|
|
||||||
lldb_server_port=${LLDB_SERVER_PORT:-9998}
|
|
||||||
jdb_port=${JDB_PORT:-13456}
|
|
||||||
lldb_client=${LLDB_CLIENT:-$default_lldb_client}
|
|
||||||
meson=${MESON:-meson}
|
|
||||||
adb=${ADB:-adb}
|
|
||||||
jdb=${JDB:-jdb}
|
|
||||||
|
|
||||||
# don't customize unless necessary
|
|
||||||
app_activity=${APP_ACTIVITY:-PowderActivity}
|
|
||||||
lldb_server_staging=${LLDB_SERVER_STAGING:-/data/local/tmp/lldb-server}
|
|
||||||
lldb_server_remote=${LLDB_SERVER_REMOTE:-lldb-server}
|
|
||||||
pidof_retry_count=${PIDOF_RETRY_COUNT:-20}
|
|
||||||
pidof_retry_delay=${PIDOF_RETRY_DELAY:-0.1}
|
|
||||||
|
|
||||||
function check_which() {
|
|
||||||
if ! which $1 >/dev/null; then
|
|
||||||
>&2 echo "[-] can't run $1"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function check_env() {
|
|
||||||
if ! [[ -f $lldb_server ]]; then
|
|
||||||
>&2 echo "[-] $lldb_server doesn't exist"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
check_which $lldb_client
|
|
||||||
check_which $adb
|
|
||||||
check_which $jdb
|
|
||||||
}
|
|
||||||
|
|
||||||
function check_adb() {
|
|
||||||
$adb shell whoami >/dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
function maybe_install_app() {
|
|
||||||
if [[ $in_build_site != yes ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
android_keystore=$(get_buildoption android_keystore)
|
|
||||||
android_keyalias=$(get_buildoption android_keyalias)
|
|
||||||
if [[ -z ${ANDROID_KEYSTORE_PASS-} ]]; then
|
|
||||||
>&2 echo "[-] ANDROID_KEYSTORE_PASS not set"
|
|
||||||
>&2 echo
|
|
||||||
>&2 cat << HELP
|
|
||||||
The current directory seems to be a build site, but ANDROID_KEYSTORE_PASS is not set, so android/$app_exe.apk cannot be invoked. If you don't have a keystore yet, create one with:
|
|
||||||
|
|
||||||
ANDROID_KEYSTORE_PASS=bagelsbagels keytool -genkey \\
|
|
||||||
-keystore $android_keystore \\
|
|
||||||
-alias $android_keyalias \\
|
|
||||||
-storepass:env ANDROID_KEYSTORE_PASS \\
|
|
||||||
-keypass:env ANDROID_KEYSTORE_PASS \\
|
|
||||||
-dname CN=bagels
|
|
||||||
|
|
||||||
Then try again with:
|
|
||||||
|
|
||||||
ANDROID_KEYSTORE_PASS=bagelsbagels $0
|
|
||||||
|
|
||||||
Naturally, replace bagelsbagels with an appropriate password.
|
|
||||||
HELP
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
>&2 echo "[+] meson compiling android/$app_exe.apk"
|
|
||||||
if ! $meson compile sign-apk; then
|
|
||||||
>&2 echo "[-] failed"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
>&2 echo "[+] adb installing android/$app_exe.apk"
|
|
||||||
if ! $adb install android/$app_exe.apk; then
|
|
||||||
>&2 echo "[-] failed"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function check_debuggable() {
|
|
||||||
$adb shell run-as $app_id whoami >/dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
function find_lldb_server() {
|
|
||||||
$adb shell run-as $app_id pgrep $lldb_server_remote >/dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
function kill_lldb_server() {
|
|
||||||
if ! $adb shell run-as $app_id pkill $lldb_server_remote; then
|
|
||||||
>&2 echo "[-] failed"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function adb_forward() {
|
|
||||||
>&2 echo "[+] adb forwarding tcp:$jdb_port jdwp:$pid"
|
|
||||||
if ! ($adb forward tcp:$jdb_port jdwp:$pid | grep $jdb_port >/dev/null); then
|
|
||||||
>&2 echo "[+] failed"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function adb_unforward() {
|
|
||||||
$adb forward --remove tcp:$jdb_port
|
|
||||||
}
|
|
||||||
|
|
||||||
function undo_current_adb_forward() {
|
|
||||||
>&2 echo "[+] adb un-forwarding orphaned tcp:$jdb_port"
|
|
||||||
adb_unforward
|
|
||||||
}
|
|
||||||
|
|
||||||
function maybe_undo_previous_adb_forward() {
|
|
||||||
if $adb forward --list | grep tcp:$jdb_port; then
|
|
||||||
>&2 echo "[+] adb un-forwarding orphaned tcp:$jdb_port"
|
|
||||||
adb_unforward
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function maybe_kill_previous_lldb_server() {
|
|
||||||
if find_lldb_server; then
|
|
||||||
>&2 echo "[+] killing orphaned $lldb_server_remote"
|
|
||||||
kill_lldb_server
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function kill_current_lldb_server() {
|
|
||||||
>&2 echo "[+] killing $lldb_server_remote"
|
|
||||||
kill_lldb_server
|
|
||||||
}
|
|
||||||
|
|
||||||
function start_app() {
|
|
||||||
>&2 echo "[+] starting $app_id/.$app_activity"
|
|
||||||
set +e
|
|
||||||
$adb shell am start -D -n "$app_id/.$app_activity" >/dev/null
|
|
||||||
set -e
|
|
||||||
local i
|
|
||||||
local maybe_pid
|
|
||||||
local pidof_result
|
|
||||||
for ((i = 0; i <= $pidof_retry_count; i++)); do
|
|
||||||
set +e
|
|
||||||
maybe_pid=$($adb shell pidof $app_id)
|
|
||||||
pidof_result=$?
|
|
||||||
set -e
|
|
||||||
if [[ $pidof_result == 0 ]]; then
|
|
||||||
pid=$maybe_pid
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
sleep $pidof_retry_delay
|
|
||||||
done
|
|
||||||
if [[ -z ${pid-} ]]; then
|
|
||||||
>&2 echo "[-] failed"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
echo $pid
|
|
||||||
}
|
|
||||||
|
|
||||||
function jdb_attach() {
|
|
||||||
>&2 echo "[+] attaching jdb in the background"
|
|
||||||
$jdb -attach localhost:$jdb_port >/dev/null 2>/dev/null &
|
|
||||||
disown $!
|
|
||||||
# at some point jdb exits because it doesn't have an stdin... fine by me
|
|
||||||
}
|
|
||||||
|
|
||||||
function maybe_deploy_lldb_server() {
|
|
||||||
if ! $adb shell [[ -f $lldb_server_staging ]]; then
|
|
||||||
>&2 echo "[+] $lldb_server_remote not present on host, deploying"
|
|
||||||
if ! ($adb push $lldb_server $lldb_server_staging && $adb shell chmod +x $lldb_server_staging); then
|
|
||||||
>&2 echo "[-] failed"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function start_lldb_server() {
|
|
||||||
if ! $adb shell run-as $app_id pgrep $lldb_server_remote >/dev/null; then
|
|
||||||
>&2 echo "[+] $lldb_server_remote not running on host, starting"
|
|
||||||
$adb shell run-as $app_id cp $lldb_server_staging /data/data/$app_id/$lldb_server_remote
|
|
||||||
$adb shell run-as $app_id ./$lldb_server_remote platform --server --listen "*:$lldb_server_port" >/dev/null 2>/dev/null &
|
|
||||||
disown $!
|
|
||||||
if ! $adb shell run-as $app_id pgrep $lldb_server_remote >/dev/null; then
|
|
||||||
>&2 echo "[-] failed"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function start_lldb() {
|
|
||||||
local pid=$1
|
|
||||||
>&2 echo "[+] starting $lldb_client"
|
|
||||||
local lldb_init=$(mktemp)
|
|
||||||
cat - << LLDB_INIT > $lldb_init
|
|
||||||
platform select remote-android
|
|
||||||
platform connect connect://localhost:$lldb_server_port
|
|
||||||
attach $pid
|
|
||||||
continue
|
|
||||||
LLDB_INIT
|
|
||||||
local lldb_status
|
|
||||||
set +e
|
|
||||||
$lldb_client --source $lldb_init
|
|
||||||
lldb_status=$?
|
|
||||||
set -e
|
|
||||||
>&2 echo "[+] $lldb_client exited with status $lldb_status"
|
|
||||||
rm $lldb_init
|
|
||||||
}
|
|
||||||
|
|
||||||
check_env
|
|
||||||
check_adb
|
|
||||||
maybe_install_app
|
|
||||||
check_debuggable
|
|
||||||
maybe_kill_previous_lldb_server
|
|
||||||
maybe_undo_previous_adb_forward
|
|
||||||
if [[ ${1-} == clean ]]; then
|
|
||||||
>&2 echo "[+] done"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
maybe_deploy_lldb_server
|
|
||||||
start_lldb_server
|
|
||||||
pid=$(start_app)
|
|
||||||
adb_forward
|
|
||||||
jdb_attach
|
|
||||||
start_lldb $pid
|
|
||||||
kill_current_lldb_server
|
|
||||||
undo_current_adb_forward
|
|
||||||
>&2 echo "[+] done"
|
|
@ -23,3 +23,6 @@ if subprocess.run([
|
|||||||
apk_path,
|
apk_path,
|
||||||
]).returncode:
|
]).returncode:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
with open(phony_path, 'w') as _:
|
||||||
|
pass
|
||||||
|
@ -1 +1,5 @@
|
|||||||
android_resources += fs.copyfile(rendered_icons['icon_exe'], 'ic_launcher.png')
|
android_resources += configure_file(
|
||||||
|
input: rendered_icons['icon_exe'],
|
||||||
|
output: 'ic_launcher.png',
|
||||||
|
copy: true,
|
||||||
|
)
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
[binaries]
|
[binaries]
|
||||||
c = [ 'clang', '-arch', 'arm64' ]
|
c = [ 'clang', '-arch', 'arm64' ]
|
||||||
cpp = [ 'clang++', '-arch', 'arm64' ]
|
cpp = [ 'clang++', '-arch', 'arm64' ]
|
||||||
objcpp = [ 'clang++', '-arch', 'arm64' ]
|
|
||||||
strip = 'strip'
|
strip = 'strip'
|
||||||
|
|
||||||
[host_machine]
|
[host_machine]
|
||||||
|
136
meson.build
136
meson.build
@ -10,7 +10,7 @@ project(
|
|||||||
'build.cpp_std=c++17', # used by to_array
|
'build.cpp_std=c++17', # used by to_array
|
||||||
'build.cpp_rtti=false', # used by to_array
|
'build.cpp_rtti=false', # used by to_array
|
||||||
],
|
],
|
||||||
meson_version: '>=0.64.0',
|
meson_version: '>=0.61.0',
|
||||||
)
|
)
|
||||||
|
|
||||||
if get_option('prepare')
|
if get_option('prepare')
|
||||||
@ -18,7 +18,6 @@ if get_option('prepare')
|
|||||||
subdir_done()
|
subdir_done()
|
||||||
endif
|
endif
|
||||||
|
|
||||||
fs = import('fs')
|
|
||||||
to_array = generator(
|
to_array = generator(
|
||||||
executable('toarray', sources: 'resources/ToArray.cpp', native: true),
|
executable('toarray', sources: 'resources/ToArray.cpp', native: true),
|
||||||
output: [ '@PLAINNAME@.cpp', '@PLAINNAME@.h' ],
|
output: [ '@PLAINNAME@.cpp', '@PLAINNAME@.h' ],
|
||||||
@ -44,11 +43,11 @@ if c_compiler.get_id() in [ 'msvc' ]
|
|||||||
host_libc = 'msvc'
|
host_libc = 'msvc'
|
||||||
elif c_compiler.get_id() in [ 'gcc' ] and host_platform == 'windows'
|
elif c_compiler.get_id() in [ 'gcc' ] and host_platform == 'windows'
|
||||||
host_libc = 'mingw'
|
host_libc = 'mingw'
|
||||||
|
if get_option('b_lto')
|
||||||
|
warning('mingw does not like static + lto')
|
||||||
|
endif
|
||||||
elif host_platform in [ 'darwin' ]
|
elif host_platform in [ 'darwin' ]
|
||||||
host_libc = 'macos'
|
host_libc = 'macos'
|
||||||
elif host_platform in [ 'emscripten' ]
|
|
||||||
host_platform = 'emscripten'
|
|
||||||
host_libc = 'emscripten'
|
|
||||||
elif host_platform in [ 'android' ]
|
elif host_platform in [ 'android' ]
|
||||||
host_platform = 'android'
|
host_platform = 'android'
|
||||||
host_libc = 'bionic'
|
host_libc = 'bionic'
|
||||||
@ -66,8 +65,8 @@ if static_variant != 'prebuilt' and host_platform == 'android'
|
|||||||
warning('only prebuilt libs are supported for android')
|
warning('only prebuilt libs are supported for android')
|
||||||
static_variant = 'prebuilt'
|
static_variant = 'prebuilt'
|
||||||
endif
|
endif
|
||||||
if static_variant == 'system' and host_platform == 'windows' and host_libc == 'msvc'
|
if static_variant == 'system' and host_platform == 'windows'
|
||||||
warning('no way to find system libs for msvc on windows')
|
warning('no way to find system libs on windows')
|
||||||
static_variant = 'prebuilt'
|
static_variant = 'prebuilt'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@ -79,19 +78,20 @@ tpt_libs_static = 'none'
|
|||||||
if static_variant == 'prebuilt'
|
if static_variant == 'prebuilt'
|
||||||
tpt_libs_static = 'static'
|
tpt_libs_static = 'static'
|
||||||
endif
|
endif
|
||||||
if static_variant == 'none' and host_platform == 'windows' and host_libc == 'msvc'
|
if static_variant == 'none' and host_platform == 'windows'
|
||||||
tpt_libs_static = 'dynamic'
|
tpt_libs_static = 'dynamic'
|
||||||
endif
|
endif
|
||||||
tpt_libs_debug = is_debug ? 'debug' : 'release'
|
tpt_libs_debug = is_debug ? 'debug' : 'release'
|
||||||
tpt_libs_variant = '@0@-@1@-@2@-@3@'.format(host_arch, host_platform, host_libc, tpt_libs_static)
|
tpt_libs_variant = '@0@-@1@-@2@-@3@'.format(host_arch, host_platform, host_libc, tpt_libs_static)
|
||||||
tpt_libs_vtag = get_option('tpt_libs_vtag')
|
tpt_libs_vtag = get_option('tpt_libs_vtag')
|
||||||
if tpt_libs_vtag == ''
|
if tpt_libs_vtag == ''
|
||||||
tpt_libs_vtag = 'v20240112165024'
|
tpt_libs_vtag = 'v20230205154205'
|
||||||
endif
|
endif
|
||||||
if tpt_libs_static != 'none'
|
if tpt_libs_static != 'none'
|
||||||
if tpt_libs_variant not in [
|
if tpt_libs_variant not in [
|
||||||
'x86_64-linux-gnu-static',
|
'x86_64-linux-gnu-static',
|
||||||
'x86_64-windows-mingw-static',
|
'x86_64-windows-mingw-static',
|
||||||
|
'x86_64-windows-mingw-dynamic',
|
||||||
'x86_64-windows-msvc-static',
|
'x86_64-windows-msvc-static',
|
||||||
'x86_64-windows-msvc-dynamic',
|
'x86_64-windows-msvc-dynamic',
|
||||||
'x86-windows-msvc-static',
|
'x86-windows-msvc-static',
|
||||||
@ -102,7 +102,6 @@ if tpt_libs_static != 'none'
|
|||||||
'x86_64-android-bionic-static',
|
'x86_64-android-bionic-static',
|
||||||
'arm-android-bionic-static',
|
'arm-android-bionic-static',
|
||||||
'aarch64-android-bionic-static',
|
'aarch64-android-bionic-static',
|
||||||
'wasm32-emscripten-emscripten-static',
|
|
||||||
]
|
]
|
||||||
error('no prebuilt @0@ libraries are currently provided'.format(tpt_libs_variant))
|
error('no prebuilt @0@ libraries are currently provided'.format(tpt_libs_variant))
|
||||||
endif
|
endif
|
||||||
@ -145,14 +144,7 @@ endif
|
|||||||
|
|
||||||
lua_variant = get_option('lua')
|
lua_variant = get_option('lua')
|
||||||
if lua_variant == 'auto'
|
if lua_variant == 'auto'
|
||||||
if host_platform == 'emscripten'
|
lua_variant = 'luajit'
|
||||||
lua_variant = 'lua5.2'
|
|
||||||
else
|
|
||||||
lua_variant = 'luajit'
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
if lua_variant == 'luajit' and host_platform == 'emscripten'
|
|
||||||
error('luajit does not work with emscripten')
|
|
||||||
endif
|
endif
|
||||||
if lua_variant == 'none'
|
if lua_variant == 'none'
|
||||||
lua_dep = []
|
lua_dep = []
|
||||||
@ -169,6 +161,9 @@ elif lua_variant == 'luajit'
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
enable_http = get_option('http')
|
enable_http = get_option('http')
|
||||||
|
if host_platform == 'android'
|
||||||
|
enable_http = false
|
||||||
|
endif
|
||||||
if host_platform == 'android'
|
if host_platform == 'android'
|
||||||
android_ndk_toolchain_prefix = meson.get_external_property('android_ndk_toolchain_prefix')
|
android_ndk_toolchain_prefix = meson.get_external_property('android_ndk_toolchain_prefix')
|
||||||
android_platform = meson.get_external_property('android_platform')
|
android_platform = meson.get_external_property('android_platform')
|
||||||
@ -182,57 +177,19 @@ if host_platform == 'android'
|
|||||||
error('tpt-libs android platform mismatch')
|
error('tpt-libs android platform mismatch')
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
curl_dep = []
|
curl_dep = enable_http ? dependency('libcurl', static: is_static) : []
|
||||||
if enable_http and host_platform != 'emscripten'
|
|
||||||
curl_dep = dependency('libcurl', static: is_static)
|
fftw_dep = dependency('fftw3f', static: is_static)
|
||||||
endif
|
threads_dep = dependency('threads')
|
||||||
|
zlib_dep = dependency('zlib', static: is_static)
|
||||||
|
png_dep = dependency('libpng16', static: is_static)
|
||||||
|
sdl2_dep = dependency('sdl2', static: is_static)
|
||||||
|
bzip2_dep = dependency('bzip2', static: is_static)
|
||||||
|
json_dep = dependency('jsoncpp', static: is_static)
|
||||||
|
|
||||||
project_link_args = []
|
project_link_args = []
|
||||||
project_c_args = []
|
project_c_args = []
|
||||||
project_cpp_args = []
|
project_cpp_args = []
|
||||||
|
|
||||||
fftw_dep = dependency('fftw3f', static: is_static)
|
|
||||||
threads_dep = dependency('threads')
|
|
||||||
if host_platform == 'emscripten'
|
|
||||||
zlib_dep = []
|
|
||||||
png_dep = []
|
|
||||||
sdl2_dep = []
|
|
||||||
bzip2_dep = []
|
|
||||||
project_link_args += [
|
|
||||||
'--no-heap-copy',
|
|
||||||
'-s', 'WASM=1',
|
|
||||||
'-s', 'ALLOW_MEMORY_GROWTH=1',
|
|
||||||
'-s', 'FORCE_FILESYSTEM=1',
|
|
||||||
'-s', 'EXIT_RUNTIME=0',
|
|
||||||
'-s', 'EXPORTED_RUNTIME_METHODS=ccall,cwrap',
|
|
||||||
'-s', 'FS_DEBUG',
|
|
||||||
'-s', 'MODULARIZE',
|
|
||||||
'-s', 'EXPORT_NAME=create_' + app_exe,
|
|
||||||
'-Wl,-u,_emscripten_run_callback_on_thread',
|
|
||||||
'-lidbfs.js',
|
|
||||||
]
|
|
||||||
emcc_args = [
|
|
||||||
'-s', 'USE_SDL=2',
|
|
||||||
'-s', 'USE_BZIP2=1',
|
|
||||||
'-s', 'USE_LIBPNG',
|
|
||||||
'-s', 'USE_ZLIB=1',
|
|
||||||
'-s', 'DISABLE_EXCEPTION_CATCHING=0',
|
|
||||||
]
|
|
||||||
if is_debug
|
|
||||||
project_link_args += [ '--source-map-base=./' ]
|
|
||||||
emcc_args += [ '-gsource-map' ]
|
|
||||||
endif
|
|
||||||
project_link_args += emcc_args
|
|
||||||
project_c_args += emcc_args
|
|
||||||
project_cpp_args += emcc_args
|
|
||||||
else
|
|
||||||
zlib_dep = dependency('zlib', static: is_static)
|
|
||||||
png_dep = dependency('libpng16', static: is_static)
|
|
||||||
sdl2_dep = dependency('sdl2', static: is_static)
|
|
||||||
bzip2_dep = dependency('bzip2', static: is_static)
|
|
||||||
endif
|
|
||||||
json_dep = dependency('jsoncpp', static: is_static)
|
|
||||||
|
|
||||||
if is_msvc
|
if is_msvc
|
||||||
if x86_sse_level >= 30
|
if x86_sse_level >= 30
|
||||||
warning('SSE3 configured to be enabled but unavailable in msvc')
|
warning('SSE3 configured to be enabled but unavailable in msvc')
|
||||||
@ -318,15 +275,14 @@ else
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
if host_platform == 'windows'
|
if host_platform == 'windows'
|
||||||
args_ccomp_win = [ '-D_WIN32_WINNT=0x0501', '-DNOMINMAX' ]
|
args_ccomp_win = [ '-D_WIN32_WINNT=0x0501' ]
|
||||||
windows_mod = import('windows')
|
windows_mod = import('windows')
|
||||||
if is_static
|
if is_static
|
||||||
args_ccomp_win += [ '-DCURL_STATICLIB' ]
|
args_ccomp_win += [ '-DCURL_STATICLIB' ]
|
||||||
if host_arch == 'x86_64'
|
if host_arch == 'x86_64'
|
||||||
args_ccomp_win += [ '-DZLIB_WINAPI' ]
|
args_ccomp_win += [ '-DZLIB_WINAPI' ]
|
||||||
endif
|
endif
|
||||||
endif
|
else
|
||||||
if tpt_libs_static == 'dynamic'
|
|
||||||
foreach input_output_condition : tpt_libs.get_variable('config_dlls')
|
foreach input_output_condition : tpt_libs.get_variable('config_dlls')
|
||||||
dll_input = input_output_condition[0]
|
dll_input = input_output_condition[0]
|
||||||
dll_output = input_output_condition[1]
|
dll_output = input_output_condition[1]
|
||||||
@ -338,7 +294,7 @@ if host_platform == 'windows'
|
|||||||
do_copy = true
|
do_copy = true
|
||||||
endif
|
endif
|
||||||
if do_copy
|
if do_copy
|
||||||
fs.copyfile(dll_input, dll_output)
|
configure_file(input: dll_input, output: dll_output, copy: true)
|
||||||
endif
|
endif
|
||||||
endforeach
|
endforeach
|
||||||
endif
|
endif
|
||||||
@ -358,11 +314,7 @@ else
|
|||||||
ident_platform = 'UNKNOWN'
|
ident_platform = 'UNKNOWN'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
project_deps = []
|
|
||||||
data_files = []
|
data_files = []
|
||||||
powder_deps = []
|
|
||||||
|
|
||||||
project_export_dynamic = false
|
|
||||||
|
|
||||||
subdir('src')
|
subdir('src')
|
||||||
subdir('resources')
|
subdir('resources')
|
||||||
@ -371,30 +323,8 @@ powder_files += data_files
|
|||||||
render_files += data_files
|
render_files += data_files
|
||||||
font_files += data_files
|
font_files += data_files
|
||||||
|
|
||||||
if host_platform == 'emscripten'
|
|
||||||
project_link_args += [
|
|
||||||
'-o', app_exe + '.js', # so we get a .wasm, and a .js
|
|
||||||
]
|
|
||||||
endif
|
|
||||||
if get_option('export_lua_symbols')
|
|
||||||
if is_static and lua_variant != 'none' and not project_export_dynamic
|
|
||||||
if host_platform == 'windows'
|
|
||||||
error('Lua symbols are currently impossible to export correctly on Windows')
|
|
||||||
elif c_compiler.has_link_argument('-Wl,--export-dynamic-symbol')
|
|
||||||
project_link_args += [
|
|
||||||
'-Wl,--export-dynamic-symbol=lua_*',
|
|
||||||
'-Wl,--export-dynamic-symbol=luaL_*',
|
|
||||||
'-Wl,--export-dynamic-symbol=luaopen_*',
|
|
||||||
]
|
|
||||||
else
|
|
||||||
warning('your linker does not support -Wl,--export-dynamic-symbol so Meson will be instructed to export all symbols in order to enable loading Lua shared modules, which may blow up the size of the resulting binary')
|
|
||||||
project_export_dynamic = true
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
if get_option('build_powder')
|
if get_option('build_powder')
|
||||||
powder_deps += project_deps + [
|
powder_deps = [
|
||||||
threads_dep,
|
threads_dep,
|
||||||
zlib_dep,
|
zlib_dep,
|
||||||
png_dep,
|
png_dep,
|
||||||
@ -426,17 +356,12 @@ if get_option('build_powder')
|
|||||||
win_subsystem: is_debug ? 'console' : 'windows',
|
win_subsystem: is_debug ? 'console' : 'windows',
|
||||||
link_args: project_link_args,
|
link_args: project_link_args,
|
||||||
dependencies: powder_deps,
|
dependencies: powder_deps,
|
||||||
export_dynamic: project_export_dynamic,
|
|
||||||
install: true,
|
|
||||||
)
|
)
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if get_option('build_render')
|
if get_option('build_render')
|
||||||
if host_platform == 'emscripten'
|
render_deps = [
|
||||||
error('render does not target emscripten')
|
|
||||||
endif
|
|
||||||
render_deps = project_deps + [
|
|
||||||
threads_dep,
|
threads_dep,
|
||||||
zlib_dep,
|
zlib_dep,
|
||||||
bzip2_dep,
|
bzip2_dep,
|
||||||
@ -455,15 +380,11 @@ if get_option('build_render')
|
|||||||
cpp_args: project_cpp_args,
|
cpp_args: project_cpp_args,
|
||||||
link_args: render_link_args,
|
link_args: render_link_args,
|
||||||
dependencies: render_deps,
|
dependencies: render_deps,
|
||||||
export_dynamic: project_export_dynamic,
|
|
||||||
)
|
)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if get_option('build_font')
|
if get_option('build_font')
|
||||||
if host_platform == 'emscripten'
|
font_deps = [
|
||||||
error('font does not target emscripten')
|
|
||||||
endif
|
|
||||||
font_deps = project_deps + [
|
|
||||||
threads_dep,
|
threads_dep,
|
||||||
zlib_dep,
|
zlib_dep,
|
||||||
png_dep,
|
png_dep,
|
||||||
@ -479,6 +400,5 @@ if get_option('build_font')
|
|||||||
cpp_args: project_cpp_args,
|
cpp_args: project_cpp_args,
|
||||||
link_args: project_link_args,
|
link_args: project_link_args,
|
||||||
dependencies: font_deps,
|
dependencies: font_deps,
|
||||||
export_dynamic: project_export_dynamic,
|
|
||||||
)
|
)
|
||||||
endif
|
endif
|
||||||
|
@ -37,46 +37,11 @@ option(
|
|||||||
description: 'Snapshot build'
|
description: 'Snapshot build'
|
||||||
)
|
)
|
||||||
option(
|
option(
|
||||||
'display_version_major',
|
'snapshot_id',
|
||||||
type: 'integer',
|
type: 'integer',
|
||||||
min: 0,
|
min: 0,
|
||||||
value: 98,
|
value: 0,
|
||||||
description: 'Major component of the display version, should more or less map to the MINOR version in semantic versioning'
|
description: 'Snapshot ID, only relevant if \'snapshot\' is true'
|
||||||
)
|
|
||||||
option(
|
|
||||||
'display_version_minor',
|
|
||||||
type: 'integer',
|
|
||||||
min: 0,
|
|
||||||
value: 2,
|
|
||||||
description: 'Minor component of the display version, should more or less map to the PATCH version in semantic versioning'
|
|
||||||
)
|
|
||||||
option(
|
|
||||||
'build_num',
|
|
||||||
type: 'integer',
|
|
||||||
min: 0,
|
|
||||||
value: 365,
|
|
||||||
description: 'Build number, should be strictly monotonously increasing across public releases'
|
|
||||||
)
|
|
||||||
option(
|
|
||||||
'upstream_version_major',
|
|
||||||
type: 'integer',
|
|
||||||
min: 0,
|
|
||||||
value: 98,
|
|
||||||
description: 'Major component of the upstream display version, mod owners should not change this but merge upstream changes to it'
|
|
||||||
)
|
|
||||||
option(
|
|
||||||
'upstream_version_minor',
|
|
||||||
type: 'integer',
|
|
||||||
min: 0,
|
|
||||||
value: 2,
|
|
||||||
description: 'Minor component of the upstream display version, mod owners should not change this but merge upstream changes to it'
|
|
||||||
)
|
|
||||||
option(
|
|
||||||
'upstream_build_num',
|
|
||||||
type: 'integer',
|
|
||||||
min: 0,
|
|
||||||
value: 365,
|
|
||||||
description: 'Upstream build number, mod owners should not change this but merge upstream changes to it'
|
|
||||||
)
|
)
|
||||||
option(
|
option(
|
||||||
'mod_id',
|
'mod_id',
|
||||||
@ -133,7 +98,7 @@ option(
|
|||||||
'update_server',
|
'update_server',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
value: '',
|
value: '',
|
||||||
description: 'Update server, only used by snapshots and mods, see \'snapshot\' and \'mod_id\''
|
description: 'Update server, only used by snapshots and mods, see \'snapshot_id\' and \'mod_id\''
|
||||||
)
|
)
|
||||||
option(
|
option(
|
||||||
'workaround_noncpp_lua',
|
'workaround_noncpp_lua',
|
||||||
@ -237,12 +202,6 @@ option(
|
|||||||
value: true,
|
value: true,
|
||||||
description: 'Enforce encrypted HTTP traffic, may be disabled for debugging'
|
description: 'Enforce encrypted HTTP traffic, may be disabled for debugging'
|
||||||
)
|
)
|
||||||
option(
|
|
||||||
'secure_ciphers_only',
|
|
||||||
type: 'boolean',
|
|
||||||
value: false,
|
|
||||||
description: 'Use only secure ciphers for encrypted HTTP traffic, please review cipher list before enabling'
|
|
||||||
)
|
|
||||||
option(
|
option(
|
||||||
'prepare',
|
'prepare',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
@ -255,59 +214,3 @@ option(
|
|||||||
value: 'disabled',
|
value: 'disabled',
|
||||||
description: 'Render icons with Inkscape (inkscape binary needs to be in PATH)'
|
description: 'Render icons with Inkscape (inkscape binary needs to be in PATH)'
|
||||||
)
|
)
|
||||||
option(
|
|
||||||
'resolve_vcs_tag',
|
|
||||||
type: 'combo',
|
|
||||||
choices: [ 'no', 'static_release_only', 'yes' ],
|
|
||||||
value: 'static_release_only',
|
|
||||||
description: 'Enable VCS tag resolution, introduces an always-stale custom target'
|
|
||||||
)
|
|
||||||
option(
|
|
||||||
'manifest_copyright',
|
|
||||||
type: 'string',
|
|
||||||
value: 'Copyright © 2008-2011 Stanislaw K Skowrenek, Copyright © 2011-2023 Simon Robertshaw, Copyright © 2016-2023 jacob1',
|
|
||||||
description: 'Copyright string, don\'t take too seriously, subject to change'
|
|
||||||
)
|
|
||||||
option(
|
|
||||||
'manifest_macos_min_ver',
|
|
||||||
type: 'string',
|
|
||||||
value: '',
|
|
||||||
description: 'MacOS minimum allowed platform version string, used by ghactions workflows, not useful otherwise'
|
|
||||||
)
|
|
||||||
option(
|
|
||||||
'manifest_date',
|
|
||||||
type: 'string',
|
|
||||||
value: '',
|
|
||||||
description: 'Build date string, used by ghactions workflows, not useful otherwise'
|
|
||||||
)
|
|
||||||
option(
|
|
||||||
'platform_clipboard',
|
|
||||||
type: 'boolean',
|
|
||||||
value: true,
|
|
||||||
description: 'Enable platform clipboard, allows copying simulation data between different windows'
|
|
||||||
)
|
|
||||||
option(
|
|
||||||
'use_bluescreen',
|
|
||||||
type: 'combo',
|
|
||||||
choices: [ 'no', 'yes', 'auto' ],
|
|
||||||
value: 'auto',
|
|
||||||
description: 'Show blue error screen upon unhandled signals and exceptions'
|
|
||||||
)
|
|
||||||
option(
|
|
||||||
'windows_icons',
|
|
||||||
type: 'boolean',
|
|
||||||
value: true,
|
|
||||||
description: 'Add icon resources to the executable on Windows'
|
|
||||||
)
|
|
||||||
option(
|
|
||||||
'windows_utf8cp',
|
|
||||||
type: 'boolean',
|
|
||||||
value: true,
|
|
||||||
description: 'Ask Windows nicely for UTF-8 as the codepage'
|
|
||||||
)
|
|
||||||
option(
|
|
||||||
'export_lua_symbols',
|
|
||||||
type: 'boolean',
|
|
||||||
value: false,
|
|
||||||
description: 'Export Lua symbols to enable loading of Lua shared modules'
|
|
||||||
)
|
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
<string>icon_exe.icns</string>
|
<string>icon_exe.icns</string>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>@MANIFEST_COPYRIGHT@</string>
|
<string>Copyright © 2008-2011 Stanislaw K Skowrenek, Copyright © 2011-2023 Simon Robertshaw, Copyright © 2016-2023 jacob1</string>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>English</string>
|
<string>English</string>
|
||||||
<key>CFBundleDocumentTypes</key>
|
<key>CFBundleDocumentTypes</key>
|
||||||
@ -57,13 +57,13 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>@DISPLAY_VERSION_MAJOR@.@DISPLAY_VERSION_MINOR@</string>
|
<string>SUBST_SAVE_VERSION.SUBST_MINOR_VERSION</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>@DISPLAY_VERSION_MAJOR@.@DISPLAY_VERSION_MINOR@ (build @BUILD_NUM@)</string>
|
<string>SUBST_SAVE_VERSION.SUBST_MINOR_VERSION (build SUBST_BUILD_NUM)</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>@MANIFEST_MACOS_MIN_VER@</string>
|
<string>SUBST_MACOS_MIN_VER</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
<string>NSApplication</string>
|
<string>NSApplication</string>
|
||||||
</dict>
|
</dict>
|
||||||
|
@ -1,137 +1,67 @@
|
|||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
static void writeU32LE(uint8_t *dest, uint32_t value)
|
|
||||||
{
|
|
||||||
dest[0] = uint8_t( value & 0xFF);
|
|
||||||
dest[1] = uint8_t((value >> 8) & 0xFF);
|
|
||||||
dest[2] = uint8_t((value >> 16) & 0xFF);
|
|
||||||
dest[3] = uint8_t((value >> 24) & 0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t readU32BE(const uint8_t *src)
|
|
||||||
{
|
|
||||||
return uint32_t(src[3]) |
|
|
||||||
(uint32_t(src[2]) << 8) |
|
|
||||||
(uint32_t(src[1]) << 16) |
|
|
||||||
(uint32_t(src[0]) << 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
if (argc < 3)
|
if (argc < 3)
|
||||||
{
|
{
|
||||||
std::cerr << "usage: " << argv[0] << " OUTPUT INPUT..." << std::endl;
|
return 1;
|
||||||
exit(1);
|
}
|
||||||
|
auto *outputIcoPath = argv[1];
|
||||||
|
std::ofstream outputIco(outputIcoPath, std::ios::binary);
|
||||||
|
if (!outputIco)
|
||||||
|
{
|
||||||
|
return 2;
|
||||||
}
|
}
|
||||||
auto images = argc - 2;
|
auto images = argc - 2;
|
||||||
if (images > 255)
|
std::vector<char> header(22 + images * 16);
|
||||||
|
auto *incondir = &header[0];
|
||||||
|
*reinterpret_cast<uint16_t *>(&incondir[0]) = 0; // reserved
|
||||||
|
*reinterpret_cast<uint16_t *>(&incondir[2]) = 1; // icon
|
||||||
|
*reinterpret_cast<uint16_t *>(&incondir[4]) = uint16_t(images);
|
||||||
|
std::vector<char> allData;
|
||||||
|
for (auto i = 0; i < images; ++i)
|
||||||
{
|
{
|
||||||
std::cerr << "too many images specified" << std::endl;
|
auto *inputAnyPath = argv[i + 2];
|
||||||
exit(1);
|
std::ifstream inputAny(inputAnyPath, std::ios::binary);
|
||||||
|
std::vector<char> data;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
char ch;
|
||||||
|
inputAny.read(&ch, 1);
|
||||||
|
if (inputAny.eof())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!inputAny)
|
||||||
|
{
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
data.push_back(ch);
|
||||||
|
}
|
||||||
|
if (*reinterpret_cast<uint64_t *>(&data[0]) != UINT64_C(0x0A1A0A0D474E5089)) // png magic
|
||||||
|
{
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
auto width = uint8_t(data[19]);
|
||||||
|
auto height = uint8_t(data[23]);
|
||||||
|
auto *incondirentry = &header[6 + i * 16];
|
||||||
|
*reinterpret_cast<uint8_t *>(&incondirentry[0]) = width;
|
||||||
|
*reinterpret_cast<uint8_t *>(&incondirentry[1]) = height;
|
||||||
|
*reinterpret_cast<uint8_t *>(&incondirentry[2]) = 0; // no color palette
|
||||||
|
*reinterpret_cast<uint8_t *>(&incondirentry[3]) = 0; // reserved
|
||||||
|
*reinterpret_cast<uint16_t *>(&incondirentry[4]) = 1; // 1 color plane
|
||||||
|
*reinterpret_cast<uint16_t *>(&incondirentry[6]) = 32; // 32 bits per pixel
|
||||||
|
*reinterpret_cast<uint32_t *>(&incondirentry[8]) = uint32_t(data.size()); // data size
|
||||||
|
*reinterpret_cast<uint32_t *>(&incondirentry[12]) = uint32_t(header.size() + allData.size()); // data offset
|
||||||
|
allData.insert(allData.end(), data.begin(), data.end());
|
||||||
}
|
}
|
||||||
std::string outputPath = argv[1];
|
outputIco.write(&header[0], header.size());
|
||||||
std::ofstream output(outputPath, std::ios::binary);
|
outputIco.write(&allData[0], allData.size());
|
||||||
auto outputFailure = [&outputPath](std::string action) {
|
if (!outputIco)
|
||||||
std::cerr << "failed to " << action << " " << outputPath << ": " << strerror(errno) << std::endl;
|
|
||||||
exit(1);
|
|
||||||
};
|
|
||||||
if (!output)
|
|
||||||
{
|
{
|
||||||
outputFailure("open");
|
return 4;
|
||||||
}
|
}
|
||||||
std::vector<char> header(6 + images * 16, 0);
|
|
||||||
auto writeHeader = [&header, &output, &outputFailure]() {
|
|
||||||
output.seekp(0, std::ios_base::beg);
|
|
||||||
output.write(&header[0], header.size());
|
|
||||||
if (!output)
|
|
||||||
{
|
|
||||||
outputFailure("write");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
writeHeader(); // make space for header
|
|
||||||
auto *headerU8 = reinterpret_cast<uint8_t *>(&header[0]);
|
|
||||||
headerU8[2] = 1;
|
|
||||||
headerU8[4] = images;
|
|
||||||
for (auto image = 0; image < images; ++image)
|
|
||||||
{
|
|
||||||
std::string inputPath = argv[2 + image];
|
|
||||||
std::ifstream input(inputPath, std::ios::binary);
|
|
||||||
auto inputFailure = [&inputPath](std::string action) {
|
|
||||||
std::cerr << "failed to " << action << " " << inputPath << ": " << strerror(errno) << std::endl;
|
|
||||||
exit(1);
|
|
||||||
};
|
|
||||||
auto imageFailure = [&inputPath](std::string failure) {
|
|
||||||
std::cerr << "failed to process " << inputPath << ": " << failure << std::endl;
|
|
||||||
exit(1);
|
|
||||||
};
|
|
||||||
if (!input)
|
|
||||||
{
|
|
||||||
inputFailure("open");
|
|
||||||
}
|
|
||||||
std::vector<char> buf;
|
|
||||||
input.seekg(0, std::ios_base::end);
|
|
||||||
buf.resize(input.tellg());
|
|
||||||
input.seekg(0, std::ios_base::beg);
|
|
||||||
input.read(&buf[0], buf.size());
|
|
||||||
if (!input)
|
|
||||||
{
|
|
||||||
inputFailure("read");
|
|
||||||
}
|
|
||||||
auto *bufU8 = reinterpret_cast<uint8_t *>(&buf[0]);
|
|
||||||
if (buf.size() < 0x21 ||
|
|
||||||
readU32BE(&bufU8[0]) != UINT32_C(0x89504E47) ||
|
|
||||||
readU32BE(&bufU8[4]) != UINT32_C(0x0D0A1A0A) ||
|
|
||||||
bufU8[0x18] != 8 ||
|
|
||||||
bufU8[0x19] != 6)
|
|
||||||
{
|
|
||||||
imageFailure("not a 32bpp RGBA PNG");
|
|
||||||
}
|
|
||||||
auto writeOffset = output.tellp();
|
|
||||||
output.write(&buf[0], buf.size());
|
|
||||||
if (!output)
|
|
||||||
{
|
|
||||||
outputFailure("write");
|
|
||||||
}
|
|
||||||
auto width = readU32BE(&bufU8[0x10]);
|
|
||||||
auto height = readU32BE(&bufU8[0x14]);
|
|
||||||
if (width == 256)
|
|
||||||
{
|
|
||||||
width = 0;
|
|
||||||
}
|
|
||||||
if (width > 255)
|
|
||||||
{
|
|
||||||
imageFailure("width exceeds U8 limit");
|
|
||||||
}
|
|
||||||
if (height == 256)
|
|
||||||
{
|
|
||||||
height = 0;
|
|
||||||
}
|
|
||||||
if (height > 255)
|
|
||||||
{
|
|
||||||
imageFailure("height exceeds U8 limit");
|
|
||||||
}
|
|
||||||
auto *entryU8 = headerU8 + 6 + image * 16;
|
|
||||||
entryU8[0] = width;
|
|
||||||
entryU8[1] = height;
|
|
||||||
entryU8[4] = 1;
|
|
||||||
entryU8[6] = 32;
|
|
||||||
if (buf.size() > UINT32_MAX)
|
|
||||||
{
|
|
||||||
imageFailure("data size exceeds U32 limit");
|
|
||||||
}
|
|
||||||
writeU32LE(&entryU8[8], uint32_t(buf.size()));
|
|
||||||
if (writeOffset > UINT32_MAX)
|
|
||||||
{
|
|
||||||
std::cerr << "output data size exceeds U32 limit" << std::endl;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
writeU32LE(&entryU8[12], uint32_t(writeOffset));
|
|
||||||
}
|
|
||||||
writeHeader(); // actually write it out
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,6 @@
|
|||||||
<url type="bugtracker">https://github.com/The-Powder-Toy/The-Powder-Toy/issues</url>
|
<url type="bugtracker">https://github.com/The-Powder-Toy/The-Powder-Toy/issues</url>
|
||||||
<url type="help">https://powdertoy.co.uk/Wiki/W/Main_Page.html</url>
|
<url type="help">https://powdertoy.co.uk/Wiki/W/Main_Page.html</url>
|
||||||
<releases>
|
<releases>
|
||||||
<release date="@MANIFEST_DATE@" version="@DISPLAY_VERSION_MAJOR@.@DISPLAY_VERSION_MINOR@.@BUILD_NUM@" />
|
<release date="SUBST_DATE" version="SUBST_SAVE_VERSION.SUBST_MINOR_VERSION.SUBST_BUILD_NUM" />
|
||||||
</releases>
|
</releases>
|
||||||
</component>
|
</component>
|
||||||
|
Binary file not shown.
@ -32,65 +32,35 @@ else
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
if host_platform == 'windows'
|
if host_platform == 'windows'
|
||||||
windows_icons = get_option('windows_icons')
|
make_ico = executable('makeico', sources: 'MakeIco.cpp', native: true)
|
||||||
windows_utf8cp = get_option('windows_utf8cp')
|
generated_win_icos = {}
|
||||||
rc_conf_depends = []
|
win_icos = {
|
||||||
rc_conf_depend_files = [
|
'icon_exe': [ 'icon_exe', 'icon_exe_48', 'icon_exe_32', 'icon_exe_16' ],
|
||||||
'resource.h',
|
'icon_cps': [ 'icon_cps', 'icon_cps_48', 'icon_cps_32', 'icon_cps_16' ],
|
||||||
]
|
}
|
||||||
icon_exe_ico_path = ''
|
foreach key, icons : win_icos
|
||||||
icon_cps_ico_path = ''
|
command = [
|
||||||
winutf8_xml_path = ''
|
make_ico,
|
||||||
if windows_icons
|
'@OUTPUT@',
|
||||||
make_ico = executable('makeico', sources: 'MakeIco.cpp', native: true)
|
]
|
||||||
generated_win_icos = {}
|
foreach ikey : icons
|
||||||
win_icos = {
|
command += [ rendered_icons[ikey] ]
|
||||||
'icon_exe': [ 'icon_exe', 'icon_exe_48', 'icon_exe_32', 'icon_exe_16' ],
|
|
||||||
'icon_cps': [ 'icon_cps', 'icon_cps_48', 'icon_cps_32', 'icon_cps_16' ],
|
|
||||||
}
|
|
||||||
foreach key, icons : win_icos
|
|
||||||
command = [
|
|
||||||
make_ico,
|
|
||||||
'@OUTPUT@',
|
|
||||||
]
|
|
||||||
foreach ikey : icons
|
|
||||||
command += [ rendered_icons[ikey] ]
|
|
||||||
endforeach
|
|
||||||
generated_win_icos += { key: custom_target(
|
|
||||||
key + '-ico',
|
|
||||||
output: key + '.ico',
|
|
||||||
command: command,
|
|
||||||
) }
|
|
||||||
endforeach
|
endforeach
|
||||||
rc_conf_depends += [
|
generated_win_icos += { key: custom_target(
|
||||||
|
key + '-ico',
|
||||||
|
output: key + '.ico',
|
||||||
|
command: command,
|
||||||
|
) }
|
||||||
|
endforeach
|
||||||
|
powder_files += windows_mod.compile_resources(
|
||||||
|
'powder-res.rc',
|
||||||
|
depends: [
|
||||||
generated_win_icos['icon_exe'],
|
generated_win_icos['icon_exe'],
|
||||||
generated_win_icos['icon_cps'],
|
generated_win_icos['icon_cps'],
|
||||||
]
|
],
|
||||||
icon_exe_ico_path = join_paths(meson.current_build_dir(), 'icon_exe.ico')
|
depend_files: [
|
||||||
icon_cps_ico_path = join_paths(meson.current_build_dir(), 'icon_cps.ico')
|
'resource.h',
|
||||||
endif
|
],
|
||||||
if windows_utf8cp
|
|
||||||
rc_conf_depend_files += [
|
|
||||||
'winutf8.xml',
|
|
||||||
]
|
|
||||||
winutf8_xml_path = join_paths(meson.current_source_dir(), 'winutf8.xml')
|
|
||||||
endif
|
|
||||||
rc_conf_data = configuration_data()
|
|
||||||
rc_conf_data.merge_from(conf_data)
|
|
||||||
rc_conf_data.set('HAVE_ICONS', windows_icons ? 1 : 0)
|
|
||||||
rc_conf_data.set('HAVE_UTF8CP', windows_utf8cp ? 1 : 0)
|
|
||||||
rc_conf_data.set('RESOUCE_H', join_paths(meson.current_source_dir(), 'resource.h'))
|
|
||||||
rc_conf_data.set('WINUTF8_XML', winutf8_xml_path)
|
|
||||||
rc_conf_data.set('ICON_EXE_ICO', icon_exe_ico_path)
|
|
||||||
rc_conf_data.set('ICON_CPS_ICO', icon_cps_ico_path)
|
|
||||||
powder_files += windows_mod.compile_resources(
|
|
||||||
configure_file(
|
|
||||||
input: 'powder-res.template.rc',
|
|
||||||
output: 'powder-res.rc',
|
|
||||||
configuration: rc_conf_data,
|
|
||||||
),
|
|
||||||
depends: rc_conf_depends,
|
|
||||||
depend_files: rc_conf_depend_files,
|
|
||||||
)
|
)
|
||||||
elif host_platform == 'darwin'
|
elif host_platform == 'darwin'
|
||||||
configure_file(
|
configure_file(
|
||||||
|
4
resources/powder-res.rc
Normal file
4
resources/powder-res.rc
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#include "resource.h"
|
||||||
|
|
||||||
|
IDI_ICON ICON DISCARDABLE "icon_exe.ico"
|
||||||
|
IDI_DOC_ICON ICON DISCARDABLE "icon_cps.ico"
|
@ -1,40 +0,0 @@
|
|||||||
#pragma code_page(65001) // UTF-8
|
|
||||||
|
|
||||||
#include "@RESOUCE_H@"
|
|
||||||
#include <winuser.h>
|
|
||||||
#include <winver.h>
|
|
||||||
#include <ntdef.h>
|
|
||||||
|
|
||||||
#define HAVE_ICONS @HAVE_ICONS@
|
|
||||||
#define HAVE_UTF8CP @HAVE_UTF8CP@
|
|
||||||
#if HAVE_ICONS
|
|
||||||
IDI_ICON ICON DISCARDABLE "@ICON_EXE_ICO@"
|
|
||||||
IDI_DOC_ICON ICON DISCARDABLE "@ICON_CPS_ICO@"
|
|
||||||
#endif
|
|
||||||
#if HAVE_UTF8CP
|
|
||||||
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "@WINUTF8_XML@"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
|
||||||
FILEVERSION @DISPLAY_VERSION_MAJOR@,@DISPLAY_VERSION_MINOR@,0,@BUILD_NUM@
|
|
||||||
PRODUCTVERSION @DISPLAY_VERSION_MAJOR@,@DISPLAY_VERSION_MINOR@,0,@BUILD_NUM@
|
|
||||||
{
|
|
||||||
BLOCK "StringFileInfo"
|
|
||||||
{
|
|
||||||
BLOCK "040904b0"
|
|
||||||
{
|
|
||||||
VALUE "CompanyName", "https://powdertoy.co.uk/\0"
|
|
||||||
VALUE "FileDescription", "@APPNAME@\0"
|
|
||||||
VALUE "FileVersion", "@DISPLAY_VERSION_MAJOR@.@DISPLAY_VERSION_MINOR@.0.@BUILD_NUM@\0"
|
|
||||||
VALUE "OriginalFilename", "@APPEXE@.exe\0"
|
|
||||||
VALUE "LegalCopyright", "@MANIFEST_COPYRIGHT@\0"
|
|
||||||
VALUE "ProductName", "@APPCOMMENT@\0"
|
|
||||||
VALUE "ProductVersion", "@DISPLAY_VERSION_MAJOR@.@DISPLAY_VERSION_MINOR@.0.@BUILD_NUM@\0"
|
|
||||||
VALUE "InternalName", "@APPID@\0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BLOCK "VarFileInfo"
|
|
||||||
{
|
|
||||||
VALUE "Translation", 0x409, 65001
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
|
||||||
<assemblyIdentity name="." version="6.0.0.0"/>
|
|
||||||
<application>
|
|
||||||
<windowsSettings>
|
|
||||||
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
|
|
||||||
</windowsSettings>
|
|
||||||
</application>
|
|
||||||
</assembly>
|
|
@ -1,6 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "VcsTag.h"
|
|
||||||
#include "common/Version.h"
|
|
||||||
|
|
||||||
constexpr bool SET_WINDOW_ICON = @SET_WINDOW_ICON@;
|
constexpr bool SET_WINDOW_ICON = @SET_WINDOW_ICON@;
|
||||||
constexpr bool DEBUG = @DEBUG@;
|
constexpr bool DEBUG = @DEBUG@;
|
||||||
@ -13,27 +11,11 @@ constexpr bool LUACONSOLE = @LUACONSOLE@;
|
|||||||
constexpr bool ALLOW_FAKE_NEWER_VERSION = @ALLOW_FAKE_NEWER_VERSION@;
|
constexpr bool ALLOW_FAKE_NEWER_VERSION = @ALLOW_FAKE_NEWER_VERSION@;
|
||||||
constexpr bool USE_UPDATESERVER = @USE_UPDATESERVER@;
|
constexpr bool USE_UPDATESERVER = @USE_UPDATESERVER@;
|
||||||
constexpr bool CAN_INSTALL = @CAN_INSTALL@;
|
constexpr bool CAN_INSTALL = @CAN_INSTALL@;
|
||||||
constexpr bool USE_BLUESCREEN = @USE_BLUESCREEN@;
|
|
||||||
constexpr bool INSTALL_CHECK = @INSTALL_CHECK@;
|
constexpr bool INSTALL_CHECK = @INSTALL_CHECK@;
|
||||||
constexpr bool IGNORE_UPDATES = @IGNORE_UPDATES@;
|
constexpr bool IGNORE_UPDATES = @IGNORE_UPDATES@;
|
||||||
constexpr bool ENFORCE_HTTPS = @ENFORCE_HTTPS@;
|
constexpr bool ENFORCE_HTTPS = @ENFORCE_HTTPS@;
|
||||||
constexpr bool SECURE_CIPHERS_ONLY = @SECURE_CIPHERS_ONLY@;
|
|
||||||
constexpr bool PLATFORM_CLIPBOARD = @PLATFORM_CLIPBOARD@;
|
|
||||||
constexpr bool USE_SYSTEM_CERT_PROVIDER = @USE_SYSTEM_CERT_PROVIDER@;
|
|
||||||
constexpr bool FFTW_PLAN_MEASURE = @FFTW_PLAN_MEASURE@;
|
|
||||||
constexpr bool ALLOW_QUIT = @ALLOW_QUIT@;
|
|
||||||
constexpr bool DEFAULT_TOUCH_UI = @DEFAULT_TOUCH_UI@;
|
|
||||||
constexpr bool ALLOW_DATA_FOLDER = @ALLOW_DATA_FOLDER@;
|
|
||||||
constexpr char PATH_SEP_CHAR = '@PATH_SEP_CHAR@';
|
constexpr char PATH_SEP_CHAR = '@PATH_SEP_CHAR@';
|
||||||
|
|
||||||
enum ForceWindowFrameOps
|
|
||||||
{
|
|
||||||
forceWindowFrameOpsNone, // usual behaviour
|
|
||||||
forceWindowFrameOpsEmbedded, // e.g. into a webpage; this sweeps a few emscripten limitations under the rug
|
|
||||||
forceWindowFrameOpsHandheld, // e.g. the system doesn't support windowed mode; includes odd setups like chromebooks
|
|
||||||
};
|
|
||||||
constexpr ForceWindowFrameOps FORCE_WINDOW_FRAME_OPS = @FORCE_WINDOW_FRAME_OPS@;
|
|
||||||
|
|
||||||
constexpr char SERVER[] = "@SERVER@";
|
constexpr char SERVER[] = "@SERVER@";
|
||||||
constexpr char STATICSERVER[] = "@STATICSERVER@";
|
constexpr char STATICSERVER[] = "@STATICSERVER@";
|
||||||
constexpr char UPDATESERVER[] = "@UPDATESERVER@";
|
constexpr char UPDATESERVER[] = "@UPDATESERVER@";
|
||||||
@ -46,17 +28,13 @@ constexpr char APPID[] = "@APPID@";
|
|||||||
constexpr char APPDATA[] = "@APPDATA@";
|
constexpr char APPDATA[] = "@APPDATA@";
|
||||||
constexpr char APPVENDOR[] = "@APPVENDOR@";
|
constexpr char APPVENDOR[] = "@APPVENDOR@";
|
||||||
|
|
||||||
|
constexpr int SAVE_VERSION = 97;
|
||||||
|
constexpr int MINOR_VERSION = 0;
|
||||||
|
constexpr int BUILD_NUM = 352;
|
||||||
|
constexpr int SNAPSHOT_ID = @SNAPSHOT_ID@;
|
||||||
constexpr int MOD_ID = @MOD_ID@;
|
constexpr int MOD_ID = @MOD_ID@;
|
||||||
|
constexpr int FUTURE_SAVE_VERSION = 97;
|
||||||
struct DisplayVersionWithBuild
|
constexpr int FUTURE_MINOR_VERSION = 0;
|
||||||
{
|
|
||||||
Version<2> displayVersion;
|
|
||||||
size_t build;
|
|
||||||
};
|
|
||||||
constexpr DisplayVersionWithBuild APP_VERSION = { { @DISPLAY_VERSION_MAJOR@, @DISPLAY_VERSION_MINOR@ }, @BUILD_NUM@ };
|
|
||||||
constexpr DisplayVersionWithBuild UPSTREAM_VERSION = { { @UPSTREAM_VERSION_MAJOR@, @UPSTREAM_VERSION_MINOR@ }, @UPSTREAM_BUILD_NUM@ };
|
|
||||||
|
|
||||||
constexpr auto DISPLAY_VERSION = APP_VERSION.displayVersion;
|
|
||||||
|
|
||||||
constexpr char IDENT_RELTYPE = SNAPSHOT ? 'S' : (BETA ? 'B' : 'R');
|
constexpr char IDENT_RELTYPE = SNAPSHOT ? 'S' : (BETA ? 'B' : 'R');
|
||||||
|
|
||||||
@ -65,6 +43,3 @@ constexpr char STATICSCHEME[] = "https://";
|
|||||||
constexpr char LOCAL_SAVE_DIR[] = "Saves";
|
constexpr char LOCAL_SAVE_DIR[] = "Saves";
|
||||||
constexpr char STAMPS_DIR[] = "stamps";
|
constexpr char STAMPS_DIR[] = "stamps";
|
||||||
constexpr char BRUSH_DIR[] = "Brushes";
|
constexpr char BRUSH_DIR[] = "Brushes";
|
||||||
|
|
||||||
constexpr int httpMaxConcurrentStreams = 50;
|
|
||||||
constexpr int httpConnectTimeoutS = 15;
|
|
||||||
|
@ -10,19 +10,12 @@
|
|||||||
#include "Format.h"
|
#include "Format.h"
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
|
|
||||||
ByteString format::UnixtimeToDate(time_t unixtime, ByteString dateFormat, bool local)
|
ByteString format::UnixtimeToDate(time_t unixtime, ByteString dateFormat)
|
||||||
{
|
{
|
||||||
struct tm * timeData;
|
struct tm * timeData;
|
||||||
char buffer[128];
|
char buffer[128];
|
||||||
|
|
||||||
if (local)
|
timeData = localtime(&unixtime);
|
||||||
{
|
|
||||||
timeData = localtime(&unixtime);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
timeData = gmtime(&unixtime);
|
|
||||||
}
|
|
||||||
|
|
||||||
strftime(buffer, 128, dateFormat.c_str(), timeData);
|
strftime(buffer, 128, dateFormat.c_str(), timeData);
|
||||||
return ByteString(buffer);
|
return ByteString(buffer);
|
||||||
|
@ -11,7 +11,7 @@ namespace format
|
|||||||
{
|
{
|
||||||
ByteString URLEncode(ByteString value);
|
ByteString URLEncode(ByteString value);
|
||||||
ByteString URLDecode(ByteString value);
|
ByteString URLDecode(ByteString value);
|
||||||
ByteString UnixtimeToDate(time_t unixtime, ByteString dateFomat = ByteString("%d %b %Y"), bool local = true);
|
ByteString UnixtimeToDate(time_t unixtime, ByteString dateFomat = ByteString("%d %b %Y"));
|
||||||
ByteString UnixtimeToDateMini(time_t unixtime);
|
ByteString UnixtimeToDateMini(time_t unixtime);
|
||||||
String CleanString(String dirtyString, bool ascii, bool color, bool newlines, bool numeric = false);
|
String CleanString(String dirtyString, bool ascii, bool color, bool newlines, bool numeric = false);
|
||||||
std::vector<char> PixelsToPPM(PlaneAdapter<std::vector<pixel>> const &);
|
std::vector<char> PixelsToPPM(PlaneAdapter<std::vector<pixel>> const &);
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <variant>
|
|
||||||
|
|
||||||
struct FpsLimitVsync
|
|
||||||
{
|
|
||||||
};
|
|
||||||
struct FpsLimitNone
|
|
||||||
{
|
|
||||||
};
|
|
||||||
struct FpsLimitExplicit
|
|
||||||
{
|
|
||||||
float value;
|
|
||||||
};
|
|
||||||
using FpsLimit = std::variant<FpsLimitVsync, FpsLimitNone, FpsLimitExplicit>;
|
|
71
src/Misc.cpp
71
src/Misc.cpp
@ -1,9 +1,75 @@
|
|||||||
#include "Misc.h"
|
#include "Misc.h"
|
||||||
|
#include "common/tpt-minmax.h"
|
||||||
#include "common/String.h"
|
#include "common/String.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <algorithm>
|
|
||||||
|
matrix2d m2d_multiply_m2d(matrix2d m1, matrix2d m2)
|
||||||
|
{
|
||||||
|
matrix2d result = {
|
||||||
|
m1.a*m2.a+m1.b*m2.c, m1.a*m2.b+m1.b*m2.d,
|
||||||
|
m1.c*m2.a+m1.d*m2.c, m1.c*m2.b+m1.d*m2.d
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector2d m2d_multiply_v2d(matrix2d m, vector2d v)
|
||||||
|
{
|
||||||
|
vector2d result = {
|
||||||
|
m.a*v.x+m.b*v.y,
|
||||||
|
m.c*v.x+m.d*v.y
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
matrix2d m2d_multiply_float(matrix2d m, float s)
|
||||||
|
{
|
||||||
|
matrix2d result = {
|
||||||
|
m.a*s, m.b*s,
|
||||||
|
m.c*s, m.d*s,
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector2d v2d_multiply_float(vector2d v, float s)
|
||||||
|
{
|
||||||
|
vector2d result = {
|
||||||
|
v.x*s,
|
||||||
|
v.y*s
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector2d v2d_add(vector2d v1, vector2d v2)
|
||||||
|
{
|
||||||
|
vector2d result = {
|
||||||
|
v1.x+v2.x,
|
||||||
|
v1.y+v2.y
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector2d v2d_sub(vector2d v1, vector2d v2)
|
||||||
|
{
|
||||||
|
vector2d result = {
|
||||||
|
v1.x-v2.x,
|
||||||
|
v1.y-v2.y
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
matrix2d m2d_new(float me0, float me1, float me2, float me3)
|
||||||
|
{
|
||||||
|
matrix2d result = {me0,me1,me2,me3};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector2d v2d_new(float x, float y)
|
||||||
|
{
|
||||||
|
vector2d result = {x, y};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void HSV_to_RGB(int h,int s,int v,int *r,int *g,int *b)//convert 0-255(0-360 for H) HSV values to 0-255 RGB
|
void HSV_to_RGB(int h,int s,int v,int *r,int *g,int *b)//convert 0-255(0-360 for H) HSV values to 0-255 RGB
|
||||||
{
|
{
|
||||||
@ -86,6 +152,9 @@ void membwand(void * destv, void * srcv, size_t destsize, size_t srcsize)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vector2d v2d_zero = {0,0};
|
||||||
|
matrix2d m2d_identity = {1,0,0,1};
|
||||||
|
|
||||||
bool byteStringEqualsString(const ByteString &str, const char *data, size_t size)
|
bool byteStringEqualsString(const ByteString &str, const char *data, size_t size)
|
||||||
{
|
{
|
||||||
return str.size() == size && !memcmp(str.data(), data, size);
|
return str.size() == size && !memcmp(str.data(), data, size);
|
||||||
|
63
src/Misc.h
63
src/Misc.h
@ -5,25 +5,6 @@
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
template<class Signed>
|
|
||||||
inline std::pair<Signed, Signed> floorDiv(Signed a, Signed b)
|
|
||||||
{
|
|
||||||
auto quo = a / b;
|
|
||||||
auto rem = a % b;
|
|
||||||
if (a < Signed(0) && rem)
|
|
||||||
{
|
|
||||||
quo -= Signed(1);
|
|
||||||
rem += b;
|
|
||||||
}
|
|
||||||
return { quo, rem };
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Signed>
|
|
||||||
inline std::pair<Signed, Signed> ceilDiv(Signed a, Signed b)
|
|
||||||
{
|
|
||||||
return floorDiv(a + b - Signed(1), b);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Linear interpolation
|
//Linear interpolation
|
||||||
template <typename T> inline T LinearInterpolate(T val1, T val2, T lowerCoord, T upperCoord, T coord)
|
template <typename T> inline T LinearInterpolate(T val1, T val2, T lowerCoord, T upperCoord, T coord)
|
||||||
{
|
{
|
||||||
@ -51,11 +32,6 @@ inline int isign(float i)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int iabs(int i)
|
|
||||||
{
|
|
||||||
return i * isign(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline unsigned clamp_flt(float f, float min, float max)
|
inline unsigned clamp_flt(float f, float min, float max)
|
||||||
{
|
{
|
||||||
if (f<min)
|
if (f<min)
|
||||||
@ -81,6 +57,45 @@ void HSV_to_RGB(int h,int s,int v,int *r,int *g,int *b);
|
|||||||
void RGB_to_HSV(int r,int g,int b,int *h,int *s,int *v);
|
void RGB_to_HSV(int r,int g,int b,int *h,int *s,int *v);
|
||||||
void membwand(void * dest, void * src, size_t destsize, size_t srcsize);
|
void membwand(void * dest, void * src, size_t destsize, size_t srcsize);
|
||||||
|
|
||||||
|
// a b
|
||||||
|
// c d
|
||||||
|
struct matrix2d {
|
||||||
|
[[deprecated("Use Mat2")]]
|
||||||
|
float a,b,c,d;
|
||||||
|
};
|
||||||
|
[[deprecated("Use Mat2")]]
|
||||||
|
typedef struct matrix2d matrix2d;
|
||||||
|
|
||||||
|
// column vector
|
||||||
|
struct vector2d {
|
||||||
|
[[deprecated("Use Vec2")]]
|
||||||
|
float x,y;
|
||||||
|
};
|
||||||
|
[[deprecated("Use Vec2")]]
|
||||||
|
typedef struct vector2d vector2d;
|
||||||
|
|
||||||
|
//matrix2d m2d_multiply_m2d(matrix2d m1, matrix2d m2);
|
||||||
|
[[deprecated("Use Mat2::operator*(Vec2)")]]
|
||||||
|
vector2d m2d_multiply_v2d(matrix2d m, vector2d v);
|
||||||
|
//matrix2d m2d_multiply_float(matrix2d m, float s);
|
||||||
|
[[deprecated("Use Vec2<float>::operator*(float)")]]
|
||||||
|
vector2d v2d_multiply_float(vector2d v, float s);
|
||||||
|
|
||||||
|
[[deprecated("Use Vec2::operator+")]]
|
||||||
|
vector2d v2d_add(vector2d v1, vector2d v2);
|
||||||
|
[[deprecated("Use Vec2::operator-")]]
|
||||||
|
vector2d v2d_sub(vector2d v1, vector2d v2);
|
||||||
|
|
||||||
|
[[deprecated("Use Mat2")]]
|
||||||
|
matrix2d m2d_new(float me0, float me1, float me2, float me3);
|
||||||
|
[[deprecated("Use Vec2")]]
|
||||||
|
vector2d v2d_new(float x, float y);
|
||||||
|
|
||||||
|
[[deprecated("Use Vec2::Zero")]]
|
||||||
|
extern vector2d v2d_zero;
|
||||||
|
[[deprecated("Use Mat2::Identity")]]
|
||||||
|
extern matrix2d m2d_identity;
|
||||||
|
|
||||||
class ByteString;
|
class ByteString;
|
||||||
|
|
||||||
bool byteStringEqualsString(const ByteString &str, const char *data, size_t size);
|
bool byteStringEqualsString(const ByteString &str, const char *data, size_t size);
|
||||||
|
@ -7,22 +7,17 @@
|
|||||||
#include "client/SaveFile.h"
|
#include "client/SaveFile.h"
|
||||||
#include "client/SaveInfo.h"
|
#include "client/SaveInfo.h"
|
||||||
#include "client/http/requestmanager/RequestManager.h"
|
#include "client/http/requestmanager/RequestManager.h"
|
||||||
#include "client/http/GetSaveRequest.h"
|
|
||||||
#include "client/http/GetSaveDataRequest.h"
|
|
||||||
#include "common/platform/Platform.h"
|
#include "common/platform/Platform.h"
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
#include "simulation/SaveRenderer.h"
|
#include "simulation/SaveRenderer.h"
|
||||||
#include "simulation/SimulationData.h"
|
|
||||||
#include "common/tpt-rand.h"
|
#include "common/tpt-rand.h"
|
||||||
#include "gui/game/Favorite.h"
|
#include "gui/game/Favorite.h"
|
||||||
#include "gui/Style.h"
|
#include "gui/Style.h"
|
||||||
#include "gui/game/GameController.h"
|
#include "gui/game/GameController.h"
|
||||||
#include "gui/game/GameView.h"
|
#include "gui/game/GameView.h"
|
||||||
#include "gui/game/IntroText.h"
|
|
||||||
#include "gui/dialogues/ConfirmPrompt.h"
|
#include "gui/dialogues/ConfirmPrompt.h"
|
||||||
#include "gui/dialogues/ErrorMessage.h"
|
#include "gui/dialogues/ErrorMessage.h"
|
||||||
#include "gui/interface/Engine.h"
|
#include "gui/interface/Engine.h"
|
||||||
#include "gui/interface/TextWrapper.h"
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "SimulationConfig.h"
|
#include "SimulationConfig.h"
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@ -30,8 +25,6 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
#include <exception>
|
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
void LoadWindowPosition()
|
void LoadWindowPosition()
|
||||||
{
|
{
|
||||||
@ -85,14 +78,14 @@ void SaveWindowPosition()
|
|||||||
void LargeScreenDialog()
|
void LargeScreenDialog()
|
||||||
{
|
{
|
||||||
StringBuilder message;
|
StringBuilder message;
|
||||||
auto scale = ui::Engine::Ref().windowFrameOps.scale;
|
|
||||||
message << "Switching to " << scale << "x size mode since your screen was determined to be large enough: ";
|
message << "Switching to " << scale << "x size mode since your screen was determined to be large enough: ";
|
||||||
message << desktopWidth << "x" << desktopHeight << " detected, " << WINDOWW * scale << "x" << WINDOWH * scale << " required";
|
message << desktopWidth << "x" << desktopHeight << " detected, " << WINDOWW*scale << "x" << WINDOWH*scale << " required";
|
||||||
message << "\nTo undo this, hit Cancel. You can change this in settings at any time.";
|
message << "\nTo undo this, hit Cancel. You can change this in settings at any time.";
|
||||||
new ConfirmPrompt("Large screen detected", message.Build(), { nullptr, []() {
|
if (!ConfirmPrompt::Blocking("Large screen detected", message.Build()))
|
||||||
|
{
|
||||||
GlobalPrefs::Ref().Set("Scale", 1);
|
GlobalPrefs::Ref().Set("Scale", 1);
|
||||||
ui::Engine::Ref().windowFrameOps.scale = 1;
|
ui::Engine::Ref().SetScale(1);
|
||||||
} });
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TickClient()
|
void TickClient()
|
||||||
@ -100,115 +93,50 @@ void TickClient()
|
|||||||
Client::Ref().Tick();
|
Client::Ref().Tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void BlueScreen(String detailMessage, std::optional<std::vector<String>> stackTrace)
|
void BlueScreen(String detailMessage)
|
||||||
{
|
{
|
||||||
auto &engine = ui::Engine::Ref();
|
auto &engine = ui::Engine::Ref();
|
||||||
engine.g->BlendFilledRect(engine.g->Size().OriginRect(), 0x1172A9_rgb .WithAlpha(0xD2));
|
engine.g->BlendFilledRect(engine.g->Size().OriginRect(), 0x1172A9_rgb .WithAlpha(0xD2));
|
||||||
|
|
||||||
auto crashPrevLogPath = ByteString("crash.prev.log");
|
String errorTitle = "ERROR";
|
||||||
auto crashLogPath = ByteString("crash.log");
|
String errorDetails = "Details: " + detailMessage;
|
||||||
Platform::RenameFile(crashLogPath, crashPrevLogPath, true);
|
String errorHelp = String("An unrecoverable fault has occurred, please report the error by visiting the website below\n") + SCHEME + SERVER;
|
||||||
|
|
||||||
StringBuilder crashInfo;
|
// We use the width of errorHelp to center, but heights of the individual texts for vertical spacing
|
||||||
crashInfo << "ERROR - Details: " << detailMessage << "\n";
|
auto pos = engine.g->Size() / 2 - Vec2(Graphics::TextSize(errorHelp).X / 2, 100);
|
||||||
crashInfo << "An unrecoverable fault has occurred, please report it by visiting the website below\n\n " << SCHEME << SERVER << "\n\n";
|
engine.g->BlendText(pos, errorTitle, 0xFFFFFF_rgb .WithAlpha(0xFF));
|
||||||
crashInfo << "An attempt will be made to save all of this information to " << crashLogPath.FromUtf8() << " in your data folder.\n";
|
pos.Y += 4 + Graphics::TextSize(errorTitle).Y;
|
||||||
crashInfo << "Please attach this file to your report.\n\n";
|
engine.g->BlendText(pos, errorDetails, 0xFFFFFF_rgb .WithAlpha(0xFF));
|
||||||
crashInfo << "Version: " << VersionInfo().FromUtf8() << "\n";
|
pos.Y += 4 + Graphics::TextSize(errorDetails).Y;
|
||||||
crashInfo << "Tag: " << VCS_TAG << "\n";
|
engine.g->BlendText(pos, errorHelp, 0xFFFFFF_rgb .WithAlpha(0xFF));
|
||||||
crashInfo << "Date: " << format::UnixtimeToDate(time(NULL), "%Y-%m-%dT%H:%M:%SZ", false).FromUtf8() << "\n";
|
|
||||||
if (stackTrace)
|
|
||||||
{
|
|
||||||
crashInfo << "Stack trace:\n";
|
|
||||||
for (auto &item : *stackTrace)
|
|
||||||
{
|
|
||||||
crashInfo << " - " << item << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
crashInfo << "Stack trace not available\n";
|
|
||||||
}
|
|
||||||
String errorText = crashInfo.Build();
|
|
||||||
constexpr auto width = 440;
|
|
||||||
ui::TextWrapper tw;
|
|
||||||
tw.Update(errorText, true, width);
|
|
||||||
engine.g->BlendText(ui::Point((engine.g->Size().X - width) / 2, 80), tw.WrappedText(), 0xFFFFFF_rgb .WithAlpha(0xFF));
|
|
||||||
|
|
||||||
auto crashLogData = errorText.ToUtf8();
|
|
||||||
std::cerr << crashLogData << std::endl;
|
|
||||||
Platform::WriteFile(std::vector<char>(crashLogData.begin(), crashLogData.end()), crashLogPath);
|
|
||||||
|
|
||||||
//Death loop
|
//Death loop
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
auto running = true;
|
while(true)
|
||||||
while (running)
|
|
||||||
{
|
{
|
||||||
while (SDL_PollEvent(&event))
|
while (SDL_PollEvent(&event))
|
||||||
{
|
if(event.type == SDL_QUIT)
|
||||||
if (event.type == SDL_QUIT)
|
exit(-1); // Don't use Platform::Exit, we're practically zombies at this point anyway.
|
||||||
{
|
|
||||||
running = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
blit(engine.g->Data());
|
blit(engine.g->Data());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't use Platform::Exit, we're practically zombies at this point anyway.
|
|
||||||
#if defined(__MINGW32__) || defined(__APPLE__) || defined(__EMSCRIPTEN__)
|
|
||||||
// Come on...
|
|
||||||
exit(-1);
|
|
||||||
#else
|
|
||||||
quick_exit(-1);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct
|
void SigHandler(int signal)
|
||||||
{
|
{
|
||||||
int sig;
|
switch(signal){
|
||||||
const char *message;
|
case SIGSEGV:
|
||||||
} signalMessages[] = {
|
BlueScreen("Memory read/write error");
|
||||||
{ SIGSEGV, "Memory read/write error" },
|
break;
|
||||||
{ SIGFPE, "Floating point exception" },
|
case SIGFPE:
|
||||||
{ SIGILL, "Program execution exception" },
|
BlueScreen("Floating point exception");
|
||||||
{ SIGABRT, "Unexpected program abort" },
|
break;
|
||||||
{ 0, nullptr },
|
case SIGILL:
|
||||||
};
|
BlueScreen("Program execution exception");
|
||||||
|
break;
|
||||||
static void SigHandler(int signal)
|
case SIGABRT:
|
||||||
{
|
BlueScreen("Unexpected program abort");
|
||||||
const char *message = "Unknown signal";
|
break;
|
||||||
for (auto *msg = signalMessages; msg->message; ++msg)
|
|
||||||
{
|
|
||||||
if (msg->sig == signal)
|
|
||||||
{
|
|
||||||
message = msg->message;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
BlueScreen(ByteString(message).FromUtf8(), Platform::StackTrace());
|
|
||||||
}
|
|
||||||
|
|
||||||
static void TerminateHandler()
|
|
||||||
{
|
|
||||||
ByteString err = "std::terminate called without a current exception";
|
|
||||||
auto eptr = std::current_exception();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (eptr)
|
|
||||||
{
|
|
||||||
std::rethrow_exception(eptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
err = "unhandled exception: " + ByteString(e.what());
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
err = "unhandled exception not derived from std::exception, cannot determine reason";
|
|
||||||
}
|
|
||||||
BlueScreen(err.FromUtf8(), Platform::StackTrace());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr int SCALE_MAXIMUM = 10;
|
constexpr int SCALE_MAXIMUM = 10;
|
||||||
@ -236,28 +164,17 @@ struct ExplicitSingletons
|
|||||||
http::RequestManagerPtr requestManager;
|
http::RequestManagerPtr requestManager;
|
||||||
std::unique_ptr<Client> client;
|
std::unique_ptr<Client> client;
|
||||||
std::unique_ptr<SaveRenderer> saveRenderer;
|
std::unique_ptr<SaveRenderer> saveRenderer;
|
||||||
|
std::unique_ptr<RNG> rng;
|
||||||
std::unique_ptr<Favorite> favorite;
|
std::unique_ptr<Favorite> favorite;
|
||||||
std::unique_ptr<ui::Engine> engine;
|
std::unique_ptr<ui::Engine> engine;
|
||||||
std::unique_ptr<SimulationData> simulationData;
|
|
||||||
std::unique_ptr<GameController> gameController;
|
std::unique_ptr<GameController> gameController;
|
||||||
};
|
};
|
||||||
static std::unique_ptr<ExplicitSingletons> explicitSingletons;
|
static std::unique_ptr<ExplicitSingletons> explicitSingletons;
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char * argv[])
|
||||||
{
|
{
|
||||||
Platform::SetupCrt();
|
Platform::SetupCrt();
|
||||||
return Platform::InvokeMain(argc, argv);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
Platform::Atexit([]() {
|
Platform::Atexit([]() {
|
||||||
SaveWindowPosition();
|
|
||||||
// Unregister dodgy error handlers so they don't try to show the blue screen when the window is closed
|
|
||||||
for (auto *msg = signalMessages; msg->message; ++msg)
|
|
||||||
{
|
|
||||||
signal(msg->sig, SIG_DFL);
|
|
||||||
}
|
|
||||||
SDLClose();
|
SDLClose();
|
||||||
explicitSingletons.reset();
|
explicitSingletons.reset();
|
||||||
});
|
});
|
||||||
@ -323,38 +240,35 @@ int Main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto ddir = Platform::DefaultDdir();
|
char *ddir = SDL_GetPrefPath(NULL, APPDATA);
|
||||||
if (!Platform::FileExists("powder.pref"))
|
if (!Platform::FileExists("powder.pref"))
|
||||||
{
|
{
|
||||||
if (ddir.size())
|
if (ddir)
|
||||||
{
|
{
|
||||||
if (!Platform::ChangeDir(ddir))
|
if (!Platform::ChangeDir(ddir))
|
||||||
{
|
{
|
||||||
perror("failed to chdir to default ddir");
|
perror("failed to chdir to default ddir");
|
||||||
ddir = {};
|
SDL_free(ddir);
|
||||||
|
ddir = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ddir.size())
|
if (ddir)
|
||||||
{
|
{
|
||||||
Platform::sharedCwd = ddir;
|
Platform::sharedCwd = ddir;
|
||||||
|
SDL_free(ddir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// We're now in the correct directory, time to get prefs.
|
// We're now in the correct directory, time to get prefs.
|
||||||
explicitSingletons->globalPrefs = std::make_unique<GlobalPrefs>();
|
explicitSingletons->globalPrefs = std::make_unique<GlobalPrefs>();
|
||||||
|
|
||||||
auto &prefs = GlobalPrefs::Ref();
|
auto &prefs = GlobalPrefs::Ref();
|
||||||
|
scale = prefs.Get("Scale", 1);
|
||||||
WindowFrameOps windowFrameOps{
|
resizable = prefs.Get("Resizable", false);
|
||||||
prefs.Get("Scale", 1),
|
fullscreen = prefs.Get("Fullscreen", false);
|
||||||
prefs.Get("Resizable", false),
|
altFullscreen = prefs.Get("AltFullscreen", false);
|
||||||
prefs.Get("Fullscreen", false),
|
forceIntegerScaling = prefs.Get("ForceIntegerScaling", true);
|
||||||
prefs.Get("AltFullscreen", false),
|
|
||||||
prefs.Get("ForceIntegerScaling", true),
|
|
||||||
prefs.Get("BlurryScaling", false),
|
|
||||||
};
|
|
||||||
auto graveExitsConsole = prefs.Get("GraveExitsConsole", true);
|
|
||||||
momentumScroll = prefs.Get("MomentumScroll", true);
|
momentumScroll = prefs.Get("MomentumScroll", true);
|
||||||
showAvatars = prefs.Get("ShowAvatars", true);
|
showAvatars = prefs.Get("ShowAvatars", true);
|
||||||
|
|
||||||
@ -375,8 +289,8 @@ int Main(int argc, char *argv[])
|
|||||||
auto kioskArg = arguments["kiosk"];
|
auto kioskArg = arguments["kiosk"];
|
||||||
if (kioskArg.has_value())
|
if (kioskArg.has_value())
|
||||||
{
|
{
|
||||||
windowFrameOps.fullscreen = true_string(kioskArg.value());
|
fullscreen = true_string(kioskArg.value());
|
||||||
prefs.Set("Fullscreen", windowFrameOps.fullscreen);
|
prefs.Set("Fullscreen", fullscreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (true_arg(arguments["redirect"]))
|
if (true_arg(arguments["redirect"]))
|
||||||
@ -394,8 +308,8 @@ int Main(int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
windowFrameOps.scale = scaleArg.value().ToNumber<int>();
|
scale = scaleArg.value().ToNumber<int>();
|
||||||
prefs.Set("Scale", windowFrameOps.scale);
|
prefs.Set("Scale", scale);
|
||||||
}
|
}
|
||||||
catch (const std::runtime_error &e)
|
catch (const std::runtime_error &e)
|
||||||
{
|
{
|
||||||
@ -430,45 +344,49 @@ int Main(int argc, char *argv[])
|
|||||||
Client::Ref().Initialize();
|
Client::Ref().Initialize();
|
||||||
|
|
||||||
explicitSingletons->saveRenderer = std::make_unique<SaveRenderer>();
|
explicitSingletons->saveRenderer = std::make_unique<SaveRenderer>();
|
||||||
|
explicitSingletons->rng = std::make_unique<RNG>();
|
||||||
explicitSingletons->favorite = std::make_unique<Favorite>();
|
explicitSingletons->favorite = std::make_unique<Favorite>();
|
||||||
explicitSingletons->engine = std::make_unique<ui::Engine>();
|
explicitSingletons->engine = std::make_unique<ui::Engine>();
|
||||||
|
|
||||||
// TODO: maybe bind the maximum allowed scale to screen size somehow
|
// TODO: maybe bind the maximum allowed scale to screen size somehow
|
||||||
if(windowFrameOps.scale < 1 || windowFrameOps.scale > SCALE_MAXIMUM)
|
if(scale < 1 || scale > SCALE_MAXIMUM)
|
||||||
windowFrameOps.scale = 1;
|
scale = 1;
|
||||||
|
|
||||||
auto &engine = ui::Engine::Ref();
|
|
||||||
engine.g = new Graphics();
|
|
||||||
engine.GraveExitsConsole = graveExitsConsole;
|
|
||||||
engine.MomentumScroll = momentumScroll;
|
|
||||||
engine.ShowAvatars = showAvatars;
|
|
||||||
engine.Begin();
|
|
||||||
engine.SetFastQuit(prefs.Get("FastQuit", true));
|
|
||||||
engine.TouchUI = prefs.Get("TouchUI", DEFAULT_TOUCH_UI);
|
|
||||||
engine.windowFrameOps = windowFrameOps;
|
|
||||||
|
|
||||||
SDLOpen();
|
SDLOpen();
|
||||||
|
|
||||||
if (Client::Ref().IsFirstRun() && FORCE_WINDOW_FRAME_OPS == forceWindowFrameOpsNone)
|
if (Client::Ref().IsFirstRun())
|
||||||
{
|
{
|
||||||
auto guessed = GuessBestScale();
|
scale = GuessBestScale();
|
||||||
if (engine.windowFrameOps.scale != guessed)
|
if (scale > 1)
|
||||||
{
|
{
|
||||||
engine.windowFrameOps.scale = guessed;
|
prefs.Set("Scale", scale);
|
||||||
prefs.Set("Scale", windowFrameOps.scale);
|
SDL_SetWindowSize(sdl_window, WINDOWW * scale, WINDOWH * scale);
|
||||||
showLargeScreenDialog = true;
|
showLargeScreenDialog = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool enableBluescreen = USE_BLUESCREEN && !true_arg(arguments["disable-bluescreen"]);
|
StopTextInput();
|
||||||
|
|
||||||
|
auto &engine = ui::Engine::Ref();
|
||||||
|
engine.g = new Graphics();
|
||||||
|
engine.Scale = scale;
|
||||||
|
engine.SetResizable(resizable);
|
||||||
|
engine.Fullscreen = fullscreen;
|
||||||
|
engine.SetAltFullscreen(altFullscreen);
|
||||||
|
engine.SetForceIntegerScaling(forceIntegerScaling);
|
||||||
|
engine.MomentumScroll = momentumScroll;
|
||||||
|
engine.ShowAvatars = showAvatars;
|
||||||
|
engine.Begin();
|
||||||
|
engine.SetFastQuit(prefs.Get("FastQuit", true));
|
||||||
|
|
||||||
|
bool enableBluescreen = !DEBUG && !true_arg(arguments["disable-bluescreen"]);
|
||||||
if (enableBluescreen)
|
if (enableBluescreen)
|
||||||
{
|
{
|
||||||
//Get ready to catch any dodgy errors
|
//Get ready to catch any dodgy errors
|
||||||
for (auto *msg = signalMessages; msg->message; ++msg)
|
signal(SIGSEGV, SigHandler);
|
||||||
{
|
signal(SIGFPE, SigHandler);
|
||||||
signal(msg->sig, SigHandler);
|
signal(SIGILL, SigHandler);
|
||||||
}
|
signal(SIGABRT, SigHandler);
|
||||||
std::set_terminate(TerminateHandler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if constexpr (X86)
|
if constexpr (X86)
|
||||||
@ -476,97 +394,114 @@ int Main(int argc, char *argv[])
|
|||||||
X86KillDenormals();
|
X86KillDenormals();
|
||||||
}
|
}
|
||||||
|
|
||||||
explicitSingletons->simulationData = std::make_unique<SimulationData>();
|
auto wrapWithBluescreen = [&]() {
|
||||||
explicitSingletons->gameController = std::make_unique<GameController>();
|
explicitSingletons->gameController = std::make_unique<GameController>();
|
||||||
auto *gameController = explicitSingletons->gameController.get();
|
auto *gameController = explicitSingletons->gameController.get();
|
||||||
engine.ShowWindow(gameController->GetView());
|
engine.ShowWindow(gameController->GetView());
|
||||||
|
|
||||||
auto openArg = arguments["open"];
|
auto openArg = arguments["open"];
|
||||||
if (openArg.has_value())
|
if (openArg.has_value())
|
||||||
{
|
|
||||||
if constexpr (DEBUG)
|
|
||||||
{
|
{
|
||||||
std::cout << "Loading " << openArg.value() << std::endl;
|
if constexpr (DEBUG)
|
||||||
|
{
|
||||||
|
std::cout << "Loading " << openArg.value() << std::endl;
|
||||||
|
}
|
||||||
|
if (Platform::FileExists(openArg.value()))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::vector<char> gameSaveData;
|
||||||
|
if (!Platform::ReadFile(gameSaveData, openArg.value()))
|
||||||
|
{
|
||||||
|
new ErrorMessage("Error", "Could not read file");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SaveFile * newFile = new SaveFile(openArg.value());
|
||||||
|
GameSave * newSave = new GameSave(std::move(gameSaveData));
|
||||||
|
newFile->SetGameSave(newSave);
|
||||||
|
gameController->LoadSaveFile(newFile);
|
||||||
|
delete newFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (std::exception & e)
|
||||||
|
{
|
||||||
|
new ErrorMessage("Error", "Could not open save file:\n" + ByteString(e.what()).FromUtf8()) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
new ErrorMessage("Error", "Could not open file");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (Platform::FileExists(openArg.value()))
|
|
||||||
|
auto ptsaveArg = arguments["ptsave"];
|
||||||
|
if (ptsaveArg.has_value())
|
||||||
{
|
{
|
||||||
|
engine.g->Clear();
|
||||||
|
engine.g->DrawRect(RectSized(engine.g->Size() / 2 - Vec2(100, 25), Vec2(200, 50)), 0xB4B4B4_rgb);
|
||||||
|
String loadingText = "Loading save...";
|
||||||
|
engine.g->BlendText(engine.g->Size() / 2 - Vec2(Graphics::textwidth(loadingText) / 2, 5), loadingText, style::Colour::InformationTitle);
|
||||||
|
|
||||||
|
blit(engine.g->Data());
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::vector<char> gameSaveData;
|
ByteString saveIdPart;
|
||||||
if (!Platform::ReadFile(gameSaveData, openArg.value()))
|
if (ByteString::Split split = ptsaveArg.value().SplitBy(':'))
|
||||||
{
|
{
|
||||||
new ErrorMessage("Error", "Could not read file");
|
if (split.Before() != "ptsave")
|
||||||
|
throw std::runtime_error("Not a ptsave link");
|
||||||
|
saveIdPart = split.After().SplitBy('#').Before();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
throw std::runtime_error("Invalid save link");
|
||||||
auto newFile = std::make_unique<SaveFile>(openArg.value());
|
|
||||||
auto newSave = std::make_unique<GameSave>(std::move(gameSaveData));
|
|
||||||
newFile->SetGameSave(std::move(newSave));
|
|
||||||
gameController->LoadSaveFile(std::move(newFile));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!saveIdPart.size())
|
||||||
|
throw std::runtime_error("No Save ID");
|
||||||
|
if constexpr (DEBUG)
|
||||||
|
{
|
||||||
|
std::cout << "Got Ptsave: id: " << saveIdPart << std::endl;
|
||||||
|
}
|
||||||
|
int saveId = saveIdPart.ToNumber<int>();
|
||||||
|
|
||||||
|
SaveInfo * newSave = Client::Ref().GetSave(saveId, 0);
|
||||||
|
if (!newSave)
|
||||||
|
throw std::runtime_error("Could not load save info");
|
||||||
|
auto saveData = Client::Ref().GetSaveData(saveId, 0);
|
||||||
|
if (!saveData.size())
|
||||||
|
throw std::runtime_error(("Could not load save\n" + Client::Ref().GetLastError()).ToUtf8());
|
||||||
|
GameSave * newGameSave = new GameSave(std::move(saveData));
|
||||||
|
newSave->SetGameSave(newGameSave);
|
||||||
|
|
||||||
|
gameController->LoadSave(newSave);
|
||||||
|
delete newSave;
|
||||||
}
|
}
|
||||||
catch (std::exception & e)
|
catch (std::exception & e)
|
||||||
{
|
{
|
||||||
new ErrorMessage("Error", "Could not open save file:\n" + ByteString(e.what()).FromUtf8()) ;
|
new ErrorMessage("Error", ByteString(e.what()).FromUtf8());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
new ErrorMessage("Error", "Could not open file");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ptsaveArg = arguments["ptsave"];
|
EngineProcess();
|
||||||
if (ptsaveArg.has_value())
|
SaveWindowPosition();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (enableBluescreen)
|
||||||
{
|
{
|
||||||
engine.g->Clear();
|
|
||||||
engine.g->DrawRect(RectSized(engine.g->Size() / 2 - Vec2(100, 25), Vec2(200, 50)), 0xB4B4B4_rgb);
|
|
||||||
String loadingText = "Loading save...";
|
|
||||||
engine.g->BlendText(engine.g->Size() / 2 - Vec2((Graphics::TextSize(loadingText).X - 1) / 2, 5), loadingText, style::Colour::InformationTitle);
|
|
||||||
|
|
||||||
blit(engine.g->Data());
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ByteString saveIdPart;
|
wrapWithBluescreen();
|
||||||
if (ByteString::Split split = ptsaveArg.value().SplitBy(':'))
|
|
||||||
{
|
|
||||||
if (split.Before() != "ptsave")
|
|
||||||
throw std::runtime_error("Not a ptsave link");
|
|
||||||
saveIdPart = split.After().SplitBy('#').Before();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw std::runtime_error("Invalid save link");
|
|
||||||
|
|
||||||
if (!saveIdPart.size())
|
|
||||||
throw std::runtime_error("No Save ID");
|
|
||||||
if constexpr (DEBUG)
|
|
||||||
{
|
|
||||||
std::cout << "Got Ptsave: id: " << saveIdPart << std::endl;
|
|
||||||
}
|
|
||||||
ByteString saveHistoryPart = "0";
|
|
||||||
if (auto split = saveIdPart.SplitBy('@'))
|
|
||||||
{
|
|
||||||
saveHistoryPart = split.After();
|
|
||||||
saveIdPart = split.Before();
|
|
||||||
}
|
|
||||||
int saveId = saveIdPart.ToNumber<int>();
|
|
||||||
int saveHistory = saveHistoryPart.ToNumber<int>();
|
|
||||||
gameController->OpenSavePreview(saveId, saveHistory, savePreviewUrl);
|
|
||||||
}
|
}
|
||||||
catch (std::exception & e)
|
catch (const std::exception &e)
|
||||||
{
|
{
|
||||||
new ErrorMessage("Error", ByteString(e.what()).FromUtf8());
|
BlueScreen(ByteString(e.what()).FromUtf8());
|
||||||
Platform::MarkPresentable();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Platform::MarkPresentable();
|
wrapWithBluescreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
MainLoop();
|
|
||||||
|
|
||||||
Platform::Exit(0);
|
Platform::Exit(0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ void TickClient()
|
|||||||
struct ExplicitSingletons
|
struct ExplicitSingletons
|
||||||
{
|
{
|
||||||
// These need to be listed in the order they are populated in main.
|
// These need to be listed in the order they are populated in main.
|
||||||
|
std::unique_ptr<RNG> rng;
|
||||||
std::unique_ptr<ui::Engine> engine;
|
std::unique_ptr<ui::Engine> engine;
|
||||||
};
|
};
|
||||||
static std::unique_ptr<ExplicitSingletons> explicitSingletons;
|
static std::unique_ptr<ExplicitSingletons> explicitSingletons;
|
||||||
@ -41,31 +42,40 @@ int main(int argc, char * argv[])
|
|||||||
});
|
});
|
||||||
explicitSingletons = std::make_unique<ExplicitSingletons>();
|
explicitSingletons = std::make_unique<ExplicitSingletons>();
|
||||||
|
|
||||||
WindowFrameOps windowFrameOps;
|
scale = 1;
|
||||||
if (argc >= 3)
|
if (argc >= 3)
|
||||||
{
|
{
|
||||||
std::istringstream ss(argv[2]);
|
std::istringstream ss(argv[2]);
|
||||||
int buf;
|
int buf;
|
||||||
if (ss >> buf)
|
if (ss >> buf)
|
||||||
{
|
{
|
||||||
windowFrameOps.scale = buf;
|
scale = buf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
resizable = false;
|
||||||
|
fullscreen = false;
|
||||||
|
altFullscreen = false;
|
||||||
|
forceIntegerScaling = true;
|
||||||
|
|
||||||
// TODO: maybe bind the maximum allowed scale to screen size somehow
|
// TODO: maybe bind the maximum allowed scale to screen size somehow
|
||||||
if (windowFrameOps.scale < 1 || windowFrameOps.scale > 10)
|
if(scale < 1 || scale > 10)
|
||||||
{
|
scale = 1;
|
||||||
windowFrameOps.scale = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
explicitSingletons->rng = std::make_unique<RNG>();
|
||||||
explicitSingletons->engine = std::make_unique<ui::Engine>();
|
explicitSingletons->engine = std::make_unique<ui::Engine>();
|
||||||
|
|
||||||
auto &engine = ui::Engine::Ref();
|
|
||||||
engine.g = new Graphics();
|
|
||||||
engine.windowFrameOps = windowFrameOps;
|
|
||||||
|
|
||||||
SDLOpen();
|
SDLOpen();
|
||||||
|
|
||||||
|
StopTextInput();
|
||||||
|
|
||||||
|
ui::Engine::Ref().g = new Graphics();
|
||||||
|
ui::Engine::Ref().Scale = scale;
|
||||||
|
ui::Engine::Ref().SetResizable(resizable);
|
||||||
|
ui::Engine::Ref().Fullscreen = fullscreen;
|
||||||
|
ui::Engine::Ref().SetAltFullscreen(altFullscreen);
|
||||||
|
ui::Engine::Ref().SetForceIntegerScaling(forceIntegerScaling);
|
||||||
|
|
||||||
|
auto &engine = ui::Engine::Ref();
|
||||||
engine.Begin();
|
engine.Begin();
|
||||||
engine.SetFastQuit(true);
|
engine.SetFastQuit(true);
|
||||||
|
|
||||||
@ -75,14 +85,11 @@ int main(int argc, char * argv[])
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::cerr << "path to font.bz2 not supplied" << std::endl;
|
std::cerr << "path to font.cpp not supplied" << std::endl;
|
||||||
Platform::Exit(1);
|
Platform::Exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (engine.Running())
|
EngineProcess();
|
||||||
{
|
|
||||||
EngineProcess();
|
|
||||||
}
|
|
||||||
Platform::Exit(0);
|
Platform::Exit(0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
#include "gui/interface/Engine.h"
|
#include "gui/interface/Engine.h"
|
||||||
#include "client/GameSave.h"
|
#include "client/GameSave.h"
|
||||||
#include "simulation/Simulation.h"
|
#include "simulation/Simulation.h"
|
||||||
#include "simulation/SimulationData.h"
|
|
||||||
#include "common/platform/Platform.h"
|
#include "common/platform/Platform.h"
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@ -22,18 +21,16 @@ int main(int argc, char *argv[])
|
|||||||
auto inputFilename = ByteString(argv[1]);
|
auto inputFilename = ByteString(argv[1]);
|
||||||
auto outputFilename = ByteString(argv[2]) + ".png";
|
auto outputFilename = ByteString(argv[2]) + ".png";
|
||||||
|
|
||||||
auto simulationData = std::make_unique<SimulationData>();
|
|
||||||
|
|
||||||
std::vector<char> fileData;
|
std::vector<char> fileData;
|
||||||
if (!Platform::ReadFile(fileData, inputFilename))
|
if (!Platform::ReadFile(fileData, inputFilename))
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<GameSave> gameSave;
|
GameSave * gameSave = NULL;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
gameSave = std::make_unique<GameSave>(fileData, false);
|
gameSave = new GameSave(fileData, false);
|
||||||
}
|
}
|
||||||
catch (ParseException &e)
|
catch (ParseException &e)
|
||||||
{
|
{
|
||||||
@ -42,12 +39,13 @@ int main(int argc, char *argv[])
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto rng = std::make_unique<RNG>();
|
||||||
Simulation * sim = new Simulation();
|
Simulation * sim = new Simulation();
|
||||||
Renderer * ren = new Renderer(sim);
|
Renderer * ren = new Renderer(sim);
|
||||||
|
|
||||||
if (gameSave)
|
if (gameSave)
|
||||||
{
|
{
|
||||||
sim->Load(gameSave.get(), true, { 0, 0 });
|
sim->Load(gameSave, true);
|
||||||
|
|
||||||
//Render save
|
//Render save
|
||||||
ren->decorations_enable = true;
|
ren->decorations_enable = true;
|
||||||
@ -64,10 +62,9 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ren->clearScreen();
|
int w = Graphics::textwidth("Save file invalid")+16, x = (XRES-w)/2, y = (YRES-24)/2;
|
||||||
int w = Graphics::TextSize("Save file invalid").X + 15, x = (XRES-w)/2, y = (YRES-24)/2;
|
ren->drawrect(x, y, w, 24, 192, 192, 192, 255);
|
||||||
ren->DrawRect(RectSized(Vec2{ x, y }, Vec2{ w, 24 }), 0xC0C0C0_rgb);
|
ren->drawtext(x+8, y+8, "Save file invalid", 192, 192, 240, 255);
|
||||||
ren->BlendText({ x+8, y+8 }, "Save file invalid", 0xC0C0F0_rgb .WithAlpha(255));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ren->RenderBegin();
|
ren->RenderBegin();
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
#include "gui/interface/Engine.h"
|
#include "gui/interface/Engine.h"
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
#include "common/platform/Platform.h"
|
#include "common/platform/Platform.h"
|
||||||
#include "common/clipboard/Clipboard.h"
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
int desktopWidth = 1280;
|
int desktopWidth = 1280;
|
||||||
@ -13,8 +12,11 @@ int desktopHeight = 1024;
|
|||||||
SDL_Window *sdl_window = NULL;
|
SDL_Window *sdl_window = NULL;
|
||||||
SDL_Renderer *sdl_renderer = NULL;
|
SDL_Renderer *sdl_renderer = NULL;
|
||||||
SDL_Texture *sdl_texture = NULL;
|
SDL_Texture *sdl_texture = NULL;
|
||||||
bool vsyncHint = false;
|
int scale = 1;
|
||||||
WindowFrameOps currentFrameOps;
|
bool fullscreen = false;
|
||||||
|
bool altFullscreen = false;
|
||||||
|
bool forceIntegerScaling = true;
|
||||||
|
bool resizable = false;
|
||||||
bool momentumScroll = true;
|
bool momentumScroll = true;
|
||||||
bool showAvatars = true;
|
bool showAvatars = true;
|
||||||
uint64_t lastTick = 0;
|
uint64_t lastTick = 0;
|
||||||
@ -26,10 +28,6 @@ int mouseButton = 0;
|
|||||||
bool mouseDown = false;
|
bool mouseDown = false;
|
||||||
bool calculatedInitialMouse = false;
|
bool calculatedInitialMouse = false;
|
||||||
bool hasMouseMoved = false;
|
bool hasMouseMoved = false;
|
||||||
double correctedFrameTimeAvg = 0;
|
|
||||||
uint64_t drawingTimer = 0;
|
|
||||||
uint64_t frameStart = 0;
|
|
||||||
uint64_t oldFrameStart = 0;
|
|
||||||
|
|
||||||
void StartTextInput()
|
void StartTextInput()
|
||||||
{
|
{
|
||||||
@ -43,24 +41,11 @@ void StopTextInput()
|
|||||||
|
|
||||||
void SetTextInputRect(int x, int y, int w, int h)
|
void SetTextInputRect(int x, int y, int w, int h)
|
||||||
{
|
{
|
||||||
// Why does SDL_SetTextInputRect not take logical coordinates???
|
|
||||||
SDL_Rect rect;
|
SDL_Rect rect;
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 18)
|
rect.x = x;
|
||||||
int wx, wy, wwx, why;
|
rect.y = y;
|
||||||
SDL_RenderLogicalToWindow(sdl_renderer, float(x), float(y), &wx, &wy);
|
rect.w = w;
|
||||||
SDL_RenderLogicalToWindow(sdl_renderer, float(x + w), float(y + h), &wwx, &why);
|
rect.h = h;
|
||||||
rect.x = wx;
|
|
||||||
rect.y = wy;
|
|
||||||
rect.w = wwx - wx;
|
|
||||||
rect.h = why - wy;
|
|
||||||
#else
|
|
||||||
// TODO: use SDL_RenderLogicalToWindow when ubuntu deigns to update to sdl 2.0.18
|
|
||||||
auto scale = ui::Engine::Ref().windowFrameOps.scale;
|
|
||||||
rect.x = x * scale;
|
|
||||||
rect.y = y * scale;
|
|
||||||
rect.w = w * scale;
|
|
||||||
rect.h = h * scale;
|
|
||||||
#endif
|
|
||||||
SDL_SetTextInputRect(&rect);
|
SDL_SetTextInputRect(&rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +69,7 @@ unsigned int GetTicks()
|
|||||||
return SDL_GetTicks();
|
return SDL_GetTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void CalculateMousePosition(int *x, int *y)
|
void CalculateMousePosition(int *x, int *y)
|
||||||
{
|
{
|
||||||
int globalMx, globalMy;
|
int globalMx, globalMy;
|
||||||
SDL_GetGlobalMouseState(&globalMx, &globalMy);
|
SDL_GetGlobalMouseState(&globalMx, &globalMy);
|
||||||
@ -92,16 +77,16 @@ static void CalculateMousePosition(int *x, int *y)
|
|||||||
SDL_GetWindowPosition(sdl_window, &windowX, &windowY);
|
SDL_GetWindowPosition(sdl_window, &windowX, &windowY);
|
||||||
|
|
||||||
if (x)
|
if (x)
|
||||||
*x = (globalMx - windowX) / currentFrameOps.scale;
|
*x = (globalMx - windowX) / scale;
|
||||||
if (y)
|
if (y)
|
||||||
*y = (globalMy - windowY) / currentFrameOps.scale;
|
*y = (globalMy - windowY) / scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
void blit(pixel *vid)
|
void blit(pixel *vid)
|
||||||
{
|
{
|
||||||
SDL_UpdateTexture(sdl_texture, NULL, vid, WINDOWW * sizeof (Uint32));
|
SDL_UpdateTexture(sdl_texture, NULL, vid, WINDOWW * sizeof (Uint32));
|
||||||
// need to clear the renderer if there are black edges (fullscreen, or resizable window)
|
// need to clear the renderer if there are black edges (fullscreen, or resizable window)
|
||||||
if (currentFrameOps.fullscreen || currentFrameOps.resizable)
|
if (fullscreen || resizable)
|
||||||
SDL_RenderClear(sdl_renderer);
|
SDL_RenderClear(sdl_renderer);
|
||||||
SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL);
|
SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL);
|
||||||
SDL_RenderPresent(sdl_renderer);
|
SDL_RenderPresent(sdl_renderer);
|
||||||
@ -114,9 +99,12 @@ void SDLOpen()
|
|||||||
fprintf(stderr, "Initializing SDL (video subsystem): %s\n", SDL_GetError());
|
fprintf(stderr, "Initializing SDL (video subsystem): %s\n", SDL_GetError());
|
||||||
Platform::Exit(-1);
|
Platform::Exit(-1);
|
||||||
}
|
}
|
||||||
Clipboard::Init();
|
|
||||||
|
|
||||||
SDLSetScreen();
|
if (!RecreateWindow())
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Creating SDL window: %s\n", SDL_GetError());
|
||||||
|
Platform::Exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
int displayIndex = SDL_GetWindowDisplayIndex(sdl_window);
|
int displayIndex = SDL_GetWindowDisplayIndex(sdl_window);
|
||||||
if (displayIndex >= 0)
|
if (displayIndex >= 0)
|
||||||
@ -129,7 +117,10 @@ void SDLOpen()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StopTextInput();
|
if constexpr (SET_WINDOW_ICON)
|
||||||
|
{
|
||||||
|
WindowIcon(sdl_window);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLClose()
|
void SDLClose()
|
||||||
@ -148,150 +139,105 @@ void SDLClose()
|
|||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLSetScreen()
|
void SDLSetScreen(int scale_, bool resizable_, bool fullscreen_, bool altFullscreen_, bool forceIntegerScaling_)
|
||||||
{
|
{
|
||||||
auto newFrameOps = ui::Engine::Ref().windowFrameOps;
|
// bool changingScale = scale != scale_;
|
||||||
auto newVsyncHint = std::holds_alternative<FpsLimitVsync>(ui::Engine::Ref().GetFpsLimit());
|
bool changingFullscreen = fullscreen_ != fullscreen || (altFullscreen_ != altFullscreen && fullscreen);
|
||||||
if (FORCE_WINDOW_FRAME_OPS == forceWindowFrameOpsEmbedded)
|
bool changingResizable = resizable != resizable_;
|
||||||
{
|
scale = scale_;
|
||||||
newFrameOps.resizable = false;
|
fullscreen = fullscreen_;
|
||||||
newFrameOps.fullscreen = false;
|
altFullscreen = altFullscreen_;
|
||||||
newFrameOps.changeResolution = false;
|
resizable = resizable_;
|
||||||
newFrameOps.forceIntegerScaling = false;
|
forceIntegerScaling = forceIntegerScaling_;
|
||||||
}
|
// Recreate the window when toggling fullscreen, due to occasional issues
|
||||||
if (FORCE_WINDOW_FRAME_OPS == forceWindowFrameOpsHandheld)
|
// Also recreate it when enabling resizable windows, to fix bugs on windows,
|
||||||
{
|
// see https://github.com/jacob1/The-Powder-Toy/issues/24
|
||||||
newFrameOps.resizable = false;
|
if (changingFullscreen || altFullscreen || (changingResizable && resizable && !fullscreen))
|
||||||
newFrameOps.fullscreen = true;
|
|
||||||
newFrameOps.changeResolution = false;
|
|
||||||
newFrameOps.forceIntegerScaling = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto currentFrameOpsNorm = currentFrameOps.Normalize();
|
|
||||||
auto newFrameOpsNorm = newFrameOps.Normalize();
|
|
||||||
auto recreate = !sdl_window ||
|
|
||||||
// Recreate the window when toggling fullscreen, due to occasional issues
|
|
||||||
newFrameOpsNorm.fullscreen != currentFrameOpsNorm.fullscreen ||
|
|
||||||
// Also recreate it when enabling resizable windows, to fix bugs on windows,
|
|
||||||
// see https://github.com/jacob1/The-Powder-Toy/issues/24
|
|
||||||
newFrameOpsNorm.resizable != currentFrameOpsNorm.resizable ||
|
|
||||||
newFrameOpsNorm.changeResolution != currentFrameOpsNorm.changeResolution ||
|
|
||||||
newFrameOpsNorm.blurryScaling != currentFrameOpsNorm.blurryScaling ||
|
|
||||||
newVsyncHint != vsyncHint;
|
|
||||||
|
|
||||||
if (!(recreate ||
|
|
||||||
newFrameOpsNorm.scale != currentFrameOpsNorm.scale ||
|
|
||||||
newFrameOpsNorm.forceIntegerScaling != currentFrameOpsNorm.forceIntegerScaling))
|
|
||||||
{
|
{
|
||||||
|
RecreateWindow();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (changingResizable)
|
||||||
|
SDL_RestoreWindow(sdl_window);
|
||||||
|
|
||||||
auto size = WINDOW * newFrameOpsNorm.scale;
|
SDL_SetWindowSize(sdl_window, WINDOWW * scale, WINDOWH * scale);
|
||||||
if (sdl_window && newFrameOpsNorm.resizable)
|
SDL_RenderSetIntegerScale(sdl_renderer, forceIntegerScaling && fullscreen ? SDL_TRUE : SDL_FALSE);
|
||||||
{
|
unsigned int flags = 0;
|
||||||
SDL_GetWindowSize(sdl_window, &size.X, &size.Y);
|
if (fullscreen)
|
||||||
}
|
flags = altFullscreen ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||||
|
SDL_SetWindowFullscreen(sdl_window, flags);
|
||||||
if (recreate)
|
if (fullscreen)
|
||||||
{
|
|
||||||
if (sdl_texture)
|
|
||||||
{
|
|
||||||
SDL_DestroyTexture(sdl_texture);
|
|
||||||
sdl_texture = NULL;
|
|
||||||
}
|
|
||||||
if (sdl_renderer)
|
|
||||||
{
|
|
||||||
SDL_DestroyRenderer(sdl_renderer);
|
|
||||||
sdl_renderer = NULL;
|
|
||||||
}
|
|
||||||
if (sdl_window)
|
|
||||||
{
|
|
||||||
SaveWindowPosition();
|
|
||||||
SDL_DestroyWindow(sdl_window);
|
|
||||||
sdl_window = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int flags = 0;
|
|
||||||
unsigned int rendererFlags = 0;
|
|
||||||
if (newFrameOpsNorm.fullscreen)
|
|
||||||
{
|
|
||||||
flags = newFrameOpsNorm.changeResolution ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP;
|
|
||||||
}
|
|
||||||
if (newFrameOpsNorm.resizable)
|
|
||||||
{
|
|
||||||
flags |= SDL_WINDOW_RESIZABLE;
|
|
||||||
}
|
|
||||||
if (vsyncHint)
|
|
||||||
{
|
|
||||||
rendererFlags |= SDL_RENDERER_PRESENTVSYNC;
|
|
||||||
}
|
|
||||||
sdl_window = SDL_CreateWindow(APPNAME, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, size.X, size.Y, flags);
|
|
||||||
if (!sdl_window)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "SDL_CreateWindow failed: %s\n", SDL_GetError());
|
|
||||||
Platform::Exit(-1);
|
|
||||||
}
|
|
||||||
if constexpr (SET_WINDOW_ICON)
|
|
||||||
{
|
|
||||||
WindowIcon(sdl_window);
|
|
||||||
}
|
|
||||||
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
|
|
||||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, newFrameOpsNorm.blurryScaling ? "linear" : "nearest");
|
|
||||||
sdl_renderer = SDL_CreateRenderer(sdl_window, -1, rendererFlags);
|
|
||||||
if (!sdl_renderer)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "SDL_CreateRenderer failed; available renderers:\n");
|
|
||||||
int num = SDL_GetNumRenderDrivers();
|
|
||||||
for (int i = 0; i < num; ++i)
|
|
||||||
{
|
|
||||||
SDL_RendererInfo info;
|
|
||||||
SDL_GetRenderDriverInfo(i, &info);
|
|
||||||
fprintf(stderr, " - %s\n", info.name);
|
|
||||||
}
|
|
||||||
Platform::Exit(-1);
|
|
||||||
}
|
|
||||||
SDL_RenderSetLogicalSize(sdl_renderer, WINDOWW, WINDOWH);
|
|
||||||
sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, WINDOWW, WINDOWH);
|
|
||||||
if (!sdl_texture)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "SDL_CreateTexture failed: %s\n", SDL_GetError());
|
|
||||||
Platform::Exit(-1);
|
|
||||||
}
|
|
||||||
SDL_RaiseWindow(sdl_window);
|
SDL_RaiseWindow(sdl_window);
|
||||||
Clipboard::RecreateWindow();
|
SDL_SetWindowResizable(sdl_window, resizable ? SDL_TRUE : SDL_FALSE);
|
||||||
}
|
|
||||||
SDL_RenderSetIntegerScale(sdl_renderer, newFrameOpsNorm.forceIntegerScaling ? SDL_TRUE : SDL_FALSE);
|
|
||||||
if (!(newFrameOpsNorm.resizable && SDL_GetWindowFlags(sdl_window) & SDL_WINDOW_MAXIMIZED))
|
|
||||||
{
|
|
||||||
SDL_SetWindowSize(sdl_window, size.X, size.Y);
|
|
||||||
LoadWindowPosition();
|
|
||||||
}
|
|
||||||
UpdateFpsLimit();
|
|
||||||
if (newFrameOpsNorm.fullscreen)
|
|
||||||
{
|
|
||||||
SDL_RaiseWindow(sdl_window);
|
|
||||||
}
|
|
||||||
currentFrameOps = newFrameOps;
|
|
||||||
vsyncHint = newVsyncHint;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void EventProcess(const SDL_Event &event)
|
bool RecreateWindow()
|
||||||
|
{
|
||||||
|
unsigned int flags = 0;
|
||||||
|
if (fullscreen)
|
||||||
|
flags = altFullscreen ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||||
|
if (resizable && !fullscreen)
|
||||||
|
flags |= SDL_WINDOW_RESIZABLE;
|
||||||
|
|
||||||
|
if (sdl_texture)
|
||||||
|
SDL_DestroyTexture(sdl_texture);
|
||||||
|
if (sdl_renderer)
|
||||||
|
SDL_DestroyRenderer(sdl_renderer);
|
||||||
|
if (sdl_window)
|
||||||
|
{
|
||||||
|
SaveWindowPosition();
|
||||||
|
SDL_DestroyWindow(sdl_window);
|
||||||
|
}
|
||||||
|
|
||||||
|
sdl_window = SDL_CreateWindow(APPNAME, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOWW * scale, WINDOWH * scale,
|
||||||
|
flags);
|
||||||
|
if (!sdl_window)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sdl_renderer = SDL_CreateRenderer(sdl_window, -1, 0);
|
||||||
|
if (!sdl_renderer)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "SDL_CreateRenderer failed; available renderers:\n");
|
||||||
|
int num = SDL_GetNumRenderDrivers();
|
||||||
|
for (int i = 0; i < num; ++i)
|
||||||
|
{
|
||||||
|
SDL_RendererInfo info;
|
||||||
|
SDL_GetRenderDriverInfo(i, &info);
|
||||||
|
fprintf(stderr, " - %s\n", info.name);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SDL_RenderSetLogicalSize(sdl_renderer, WINDOWW, WINDOWH);
|
||||||
|
if (forceIntegerScaling && fullscreen)
|
||||||
|
SDL_RenderSetIntegerScale(sdl_renderer, SDL_TRUE);
|
||||||
|
sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, WINDOWW, WINDOWH);
|
||||||
|
SDL_RaiseWindow(sdl_window);
|
||||||
|
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
|
||||||
|
//Uncomment this to enable resizing
|
||||||
|
//SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
|
||||||
|
//SDL_SetWindowResizable(sdl_window, SDL_TRUE);
|
||||||
|
|
||||||
|
LoadWindowPosition();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventProcess(const SDL_Event &event)
|
||||||
{
|
{
|
||||||
auto &engine = ui::Engine::Ref();
|
auto &engine = ui::Engine::Ref();
|
||||||
switch (event.type)
|
switch (event.type)
|
||||||
{
|
{
|
||||||
case SDL_QUIT:
|
case SDL_QUIT:
|
||||||
if (ALLOW_QUIT && (engine.GetFastQuit() || engine.CloseWindow()))
|
if (engine.GetFastQuit() || engine.CloseWindow())
|
||||||
{
|
|
||||||
engine.Exit();
|
engine.Exit();
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case SDL_KEYDOWN:
|
case SDL_KEYDOWN:
|
||||||
if (SDL_GetModState() & KMOD_GUI)
|
if (SDL_GetModState() & KMOD_GUI)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ALLOW_QUIT && !event.key.repeat && event.key.keysym.sym == 'q' && (event.key.keysym.mod&KMOD_CTRL))
|
if (!event.key.repeat && event.key.keysym.sym == 'q' && (event.key.keysym.mod&KMOD_CTRL))
|
||||||
engine.ConfirmExit();
|
engine.ConfirmExit();
|
||||||
else
|
else
|
||||||
engine.onKeyPress(event.key.keysym.sym, event.key.keysym.scancode, event.key.repeat, event.key.keysym.mod&KMOD_SHIFT, event.key.keysym.mod&KMOD_CTRL, event.key.keysym.mod&KMOD_ALT);
|
engine.onKeyPress(event.key.keysym.sym, event.key.keysym.scancode, event.key.repeat, event.key.keysym.mod&KMOD_SHIFT, event.key.keysym.mod&KMOD_CTRL, event.key.keysym.mod&KMOD_ALT);
|
||||||
@ -349,7 +295,7 @@ static void EventProcess(const SDL_Event &event)
|
|||||||
mousey = event.button.y;
|
mousey = event.button.y;
|
||||||
}
|
}
|
||||||
mouseButton = event.button.button;
|
mouseButton = event.button.button;
|
||||||
engine.onMouseDown(mousex, mousey, mouseButton);
|
engine.onMouseClick(mousex, mousey, mouseButton);
|
||||||
|
|
||||||
mouseDown = true;
|
mouseDown = true;
|
||||||
if constexpr (!DEBUG)
|
if constexpr (!DEBUG)
|
||||||
@ -365,7 +311,7 @@ static void EventProcess(const SDL_Event &event)
|
|||||||
mousey = event.button.y;
|
mousey = event.button.y;
|
||||||
}
|
}
|
||||||
mouseButton = event.button.button;
|
mouseButton = event.button.button;
|
||||||
engine.onMouseUp(mousex, mousey, mouseButton);
|
engine.onMouseUnclick(mousex, mousey, mouseButton);
|
||||||
|
|
||||||
mouseDown = false;
|
mouseDown = false;
|
||||||
if constexpr (!DEBUG)
|
if constexpr (!DEBUG)
|
||||||
@ -395,52 +341,75 @@ static void EventProcess(const SDL_Event &event)
|
|||||||
|
|
||||||
void EngineProcess()
|
void EngineProcess()
|
||||||
{
|
{
|
||||||
auto &engine = ui::Engine::Ref();
|
double correctedFrameTimeAvg = 0;
|
||||||
auto correctedFrameTime = frameStart - oldFrameStart;
|
|
||||||
drawingTimer += correctedFrameTime;
|
|
||||||
correctedFrameTimeAvg = correctedFrameTimeAvg + (correctedFrameTime - correctedFrameTimeAvg) * 0.05;
|
|
||||||
if (correctedFrameTime && frameStart - lastFpsUpdate > UINT64_C(200'000'000))
|
|
||||||
{
|
|
||||||
engine.SetFps(1e9f / correctedFrameTimeAvg);
|
|
||||||
lastFpsUpdate = frameStart;
|
|
||||||
}
|
|
||||||
if (frameStart - lastTick > UINT64_C(100'000'000))
|
|
||||||
{
|
|
||||||
lastTick = frameStart;
|
|
||||||
TickClient();
|
|
||||||
}
|
|
||||||
if (showLargeScreenDialog)
|
|
||||||
{
|
|
||||||
showLargeScreenDialog = false;
|
|
||||||
LargeScreenDialog();
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
while (SDL_PollEvent(&event))
|
|
||||||
{
|
|
||||||
EventProcess(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
engine.Tick();
|
uint64_t drawingTimer = 0;
|
||||||
|
auto frameStart = uint64_t(SDL_GetTicks()) * UINT64_C(1'000'000);
|
||||||
|
|
||||||
auto fpsLimit = ui::Engine::Ref().GetFpsLimit();
|
auto &engine = ui::Engine::Ref();
|
||||||
int drawcap = ui::Engine::Ref().GetDrawingFrequencyLimit();
|
while(engine.Running())
|
||||||
if (!drawcap || drawingTimer > 1e9f / drawcap)
|
|
||||||
{
|
{
|
||||||
engine.Draw();
|
if(engine.Broken()) { engine.UnBreak(); break; }
|
||||||
drawingTimer = 0;
|
event.type = 0;
|
||||||
SDLSetScreen();
|
while (SDL_PollEvent(&event))
|
||||||
blit(engine.g->Data());
|
{
|
||||||
|
EventProcess(event);
|
||||||
|
event.type = 0; //Clear last event
|
||||||
|
}
|
||||||
|
if(engine.Broken()) { engine.UnBreak(); break; }
|
||||||
|
|
||||||
|
engine.Tick();
|
||||||
|
|
||||||
|
int drawcap = ui::Engine::Ref().GetDrawingFrequencyLimit();
|
||||||
|
if (!drawcap || drawingTimer > 1e9f / drawcap)
|
||||||
|
{
|
||||||
|
engine.Draw();
|
||||||
|
drawingTimer = 0;
|
||||||
|
|
||||||
|
if (scale != engine.Scale || fullscreen != engine.Fullscreen ||
|
||||||
|
altFullscreen != engine.GetAltFullscreen() ||
|
||||||
|
forceIntegerScaling != engine.GetForceIntegerScaling() || resizable != engine.GetResizable())
|
||||||
|
{
|
||||||
|
SDLSetScreen(engine.Scale, engine.GetResizable(), engine.Fullscreen, engine.GetAltFullscreen(),
|
||||||
|
engine.GetForceIntegerScaling());
|
||||||
|
}
|
||||||
|
|
||||||
|
blit(engine.g->Data());
|
||||||
|
}
|
||||||
|
auto fpsLimit = ui::Engine::Ref().FpsLimit;
|
||||||
|
auto now = uint64_t(SDL_GetTicks()) * UINT64_C(1'000'000);
|
||||||
|
auto oldFrameStart = frameStart;
|
||||||
|
frameStart = now;
|
||||||
|
if (fpsLimit > 2)
|
||||||
|
{
|
||||||
|
auto timeBlockDuration = uint64_t(UINT64_C(1'000'000'000) / fpsLimit);
|
||||||
|
auto oldFrameStartTimeBlock = oldFrameStart / timeBlockDuration;
|
||||||
|
auto frameStartTimeBlock = oldFrameStartTimeBlock + 1U;
|
||||||
|
frameStart = std::max(frameStart, frameStartTimeBlock * timeBlockDuration);
|
||||||
|
SDL_Delay((frameStart - now) / UINT64_C(1'000'000));
|
||||||
|
}
|
||||||
|
auto correctedFrameTime = frameStart - oldFrameStart;
|
||||||
|
drawingTimer += correctedFrameTime;
|
||||||
|
correctedFrameTimeAvg = correctedFrameTimeAvg + (correctedFrameTime - correctedFrameTimeAvg) * 0.05;
|
||||||
|
if (frameStart - lastFpsUpdate > UINT64_C(200'000'000))
|
||||||
|
{
|
||||||
|
engine.SetFps(1e9f / correctedFrameTimeAvg);
|
||||||
|
lastFpsUpdate = frameStart;
|
||||||
|
}
|
||||||
|
if (frameStart - lastTick > UINT64_C(100'000'000))
|
||||||
|
{
|
||||||
|
lastTick = frameStart;
|
||||||
|
TickClient();
|
||||||
|
}
|
||||||
|
if (showLargeScreenDialog)
|
||||||
|
{
|
||||||
|
showLargeScreenDialog = false;
|
||||||
|
LargeScreenDialog();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
auto now = uint64_t(SDL_GetTicks()) * UINT64_C(1'000'000);
|
if constexpr (DEBUG)
|
||||||
oldFrameStart = frameStart;
|
|
||||||
frameStart = now;
|
|
||||||
if (auto *fpsLimitExplicit = std::get_if<FpsLimitExplicit>(&fpsLimit))
|
|
||||||
{
|
{
|
||||||
auto timeBlockDuration = uint64_t(UINT64_C(1'000'000'000) / fpsLimitExplicit->value);
|
std::cout << "Breaking out of EngineProcess" << std::endl;
|
||||||
auto oldFrameStartTimeBlock = oldFrameStart / timeBlockDuration;
|
|
||||||
auto frameStartTimeBlock = oldFrameStartTimeBlock + 1U;
|
|
||||||
frameStart = std::max(frameStart, frameStartTimeBlock * timeBlockDuration);
|
|
||||||
SDL_Delay((frameStart - now) / UINT64_C(1'000'000));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,19 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "common/String.h"
|
#include "common/String.h"
|
||||||
#include "graphics/Pixel.h"
|
#include "graphics/Pixel.h"
|
||||||
#include "gui/WindowFrameOps.h"
|
|
||||||
#include "FpsLimit.h"
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
#include <variant>
|
|
||||||
|
|
||||||
extern int desktopWidth;
|
extern int desktopWidth;
|
||||||
extern int desktopHeight;
|
extern int desktopHeight;
|
||||||
extern SDL_Window *sdl_window;
|
extern SDL_Window *sdl_window;
|
||||||
extern SDL_Renderer *sdl_renderer;
|
extern SDL_Renderer *sdl_renderer;
|
||||||
extern SDL_Texture *sdl_texture;
|
extern SDL_Texture *sdl_texture;
|
||||||
|
extern int scale;
|
||||||
|
extern bool fullscreen;
|
||||||
|
extern bool altFullscreen;
|
||||||
|
extern bool forceIntegerScaling;
|
||||||
|
extern bool resizable;
|
||||||
extern bool momentumScroll;
|
extern bool momentumScroll;
|
||||||
extern bool showAvatars;
|
extern bool showAvatars;
|
||||||
extern uint64_t lastTick;
|
extern uint64_t lastTick;
|
||||||
@ -24,7 +26,6 @@ extern bool mouseDown;
|
|||||||
extern bool calculatedInitialMouse;
|
extern bool calculatedInitialMouse;
|
||||||
extern bool hasMouseMoved;
|
extern bool hasMouseMoved;
|
||||||
|
|
||||||
void MainLoop();
|
|
||||||
void EngineProcess();
|
void EngineProcess();
|
||||||
void StartTextInput();
|
void StartTextInput();
|
||||||
void StopTextInput();
|
void StopTextInput();
|
||||||
@ -33,13 +34,14 @@ void ClipboardPush(ByteString text);
|
|||||||
ByteString ClipboardPull();
|
ByteString ClipboardPull();
|
||||||
int GetModifiers();
|
int GetModifiers();
|
||||||
unsigned int GetTicks();
|
unsigned int GetTicks();
|
||||||
|
void CalculateMousePosition(int *x, int *y);
|
||||||
void blit(pixel *vid);
|
void blit(pixel *vid);
|
||||||
void SDLOpen();
|
void SDLOpen();
|
||||||
void SDLClose();
|
void SDLClose();
|
||||||
void SDLSetScreen();
|
void SDLSetScreen(int scale_, bool resizable_, bool fullscreen_, bool altFullscreen_, bool forceIntegerScaling_);
|
||||||
void SetFpsLimit(FpsLimit newFpsLimit);
|
bool RecreateWindow();
|
||||||
void LoadWindowPosition();
|
void LoadWindowPosition();
|
||||||
void SaveWindowPosition();
|
void SaveWindowPosition();
|
||||||
void LargeScreenDialog();
|
void LargeScreenDialog();
|
||||||
void TickClient();
|
void TickClient();
|
||||||
void UpdateFpsLimit();
|
void EventProcess(const SDL_Event &event);
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
#include "PowderToySDL.h"
|
|
||||||
#include "gui/interface/Engine.h"
|
|
||||||
|
|
||||||
void MainLoop()
|
|
||||||
{
|
|
||||||
while (ui::Engine::Ref().Running())
|
|
||||||
{
|
|
||||||
EngineProcess();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetFpsLimit(FpsLimit newFpsLimit)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateFpsLimit()
|
|
||||||
{
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
#include "PowderToySDL.h"
|
|
||||||
#include "gui/interface/Engine.h"
|
|
||||||
#include <emscripten.h>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace Platform
|
|
||||||
{
|
|
||||||
void MaybeTriggerSyncFs();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void MainLoopBody()
|
|
||||||
{
|
|
||||||
EngineProcess();
|
|
||||||
Platform::MaybeTriggerSyncFs();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetFpsLimit(FpsLimit newFpsLimit)
|
|
||||||
{
|
|
||||||
static bool mainLoopSet = false;
|
|
||||||
if (!mainLoopSet)
|
|
||||||
{
|
|
||||||
emscripten_set_main_loop(MainLoopBody, 0, 0);
|
|
||||||
mainLoopSet = true;
|
|
||||||
}
|
|
||||||
if (auto *fpsLimitVsync = std::get_if<FpsLimitVsync>(&newFpsLimit))
|
|
||||||
{
|
|
||||||
emscripten_set_main_loop_timing(EM_TIMING_RAF, 1);
|
|
||||||
std::cerr << "implicit fps limit via vsync" << std::endl;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto delay = 0;
|
|
||||||
if (auto *fpsLimitExplicit = std::get_if<FpsLimitExplicit>(&newFpsLimit))
|
|
||||||
{
|
|
||||||
delay = int(1000.f / fpsLimitExplicit->value);
|
|
||||||
}
|
|
||||||
emscripten_set_main_loop_timing(EM_TIMING_SETTIMEOUT, delay);
|
|
||||||
std::cerr << "explicit fps limit: " << delay << "ms delays" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateFpsLimit()
|
|
||||||
{
|
|
||||||
SetFpsLimit(ui::Engine::Ref().GetFpsLimit());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is actually only called once at startup, the real main loop body is MainLoopBody.
|
|
||||||
void MainLoop()
|
|
||||||
{
|
|
||||||
UpdateFpsLimit();
|
|
||||||
MainLoopBody();
|
|
||||||
}
|
|
@ -31,7 +31,7 @@ constexpr int MAXSIGNS = 16;
|
|||||||
|
|
||||||
constexpr int ISTP = CELL / 2;
|
constexpr int ISTP = CELL / 2;
|
||||||
constexpr float CFDS = 4.0f / CELL;
|
constexpr float CFDS = 4.0f / CELL;
|
||||||
constexpr float MAX_VELOCITY = 1e4f;
|
constexpr float SIM_MAXVELOCITY = 1e4f;
|
||||||
|
|
||||||
//Air constants
|
//Air constants
|
||||||
constexpr float AIR_TSTEPP = 0.3f;
|
constexpr float AIR_TSTEPP = 0.3f;
|
||||||
@ -42,13 +42,10 @@ constexpr float AIR_PLOSS = 0.9999f;
|
|||||||
|
|
||||||
constexpr int NGOL = 24;
|
constexpr int NGOL = 24;
|
||||||
|
|
||||||
enum DefaultBrushes
|
constexpr int CIRCLE_BRUSH = 0;
|
||||||
{
|
constexpr int SQUARE_BRUSH = 1;
|
||||||
BRUSH_CIRCLE,
|
constexpr int TRI_BRUSH = 2;
|
||||||
BRUSH_SQUARE,
|
constexpr int BRUSH_NUM = 3;
|
||||||
BRUSH_TRIANGLE,
|
|
||||||
NUM_DEFAULTBRUSHES,
|
|
||||||
};
|
|
||||||
|
|
||||||
//Photon constants
|
//Photon constants
|
||||||
constexpr int SURF_RANGE = 10;
|
constexpr int SURF_RANGE = 10;
|
||||||
@ -64,5 +61,3 @@ constexpr float GLASS_IOR = 1.9f;
|
|||||||
constexpr float GLASS_DISP = 0.07f;
|
constexpr float GLASS_DISP = 0.07f;
|
||||||
|
|
||||||
constexpr float R_TEMP = 22;
|
constexpr float R_TEMP = 22;
|
||||||
|
|
||||||
constexpr bool LATENTHEAT = false;
|
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
constexpr char VCS_TAG[] = "@VCS_TAG@";
|
|
@ -19,6 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "common/tpt-inline.h"
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@ -1182,7 +1183,7 @@ bson_bool_t bson_check_string( bson *b, const char *string,
|
|||||||
#define bson_big_endian32(out, in) ( bson_swap_endian32(out, in) )
|
#define bson_big_endian32(out, in) ( bson_swap_endian32(out, in) )
|
||||||
//#endif
|
//#endif
|
||||||
|
|
||||||
static inline void bson_swap_endian64( void *outp, const void *inp ) {
|
static TPT_INLINE void bson_swap_endian64( void *outp, const void *inp ) {
|
||||||
const char *in = ( const char * )inp;
|
const char *in = ( const char * )inp;
|
||||||
char *out = ( char * )outp;
|
char *out = ( char * )outp;
|
||||||
|
|
||||||
@ -1196,7 +1197,7 @@ static inline void bson_swap_endian64( void *outp, const void *inp ) {
|
|||||||
out[7] = in[0];
|
out[7] = in[0];
|
||||||
|
|
||||||
}
|
}
|
||||||
static inline void bson_swap_endian32( void *outp, const void *inp ) {
|
static TPT_INLINE void bson_swap_endian32( void *outp, const void *inp ) {
|
||||||
const char *in = ( const char * )inp;
|
const char *in = ( const char * )inp;
|
||||||
char *out = ( char * )outp;
|
char *out = ( char * )outp;
|
||||||
|
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
#include "Client.h"
|
|
||||||
#include "prefs/GlobalPrefs.h"
|
|
||||||
|
|
||||||
void Client::LoadAuthUser()
|
|
||||||
{
|
|
||||||
auto &prefs = GlobalPrefs::Ref();
|
|
||||||
authUser.UserID = prefs.Get("User.ID", 0);
|
|
||||||
authUser.Username = prefs.Get("User.Username", ByteString(""));
|
|
||||||
authUser.SessionID = prefs.Get("User.SessionID", ByteString(""));
|
|
||||||
authUser.SessionKey = prefs.Get("User.SessionKey", ByteString(""));
|
|
||||||
authUser.UserElevation = prefs.Get("User.Elevation", User::ElevationNone);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Client::SaveAuthUser()
|
|
||||||
{
|
|
||||||
auto &prefs = GlobalPrefs::Ref();
|
|
||||||
Prefs::DeferWrite dw(prefs);
|
|
||||||
if (authUser.UserID)
|
|
||||||
{
|
|
||||||
prefs.Set("User.ID", authUser.UserID);
|
|
||||||
prefs.Set("User.SessionID", authUser.SessionID);
|
|
||||||
prefs.Set("User.SessionKey", authUser.SessionKey);
|
|
||||||
prefs.Set("User.Username", authUser.Username);
|
|
||||||
prefs.Set("User.Elevation", authUser.UserElevation);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
prefs.Clear("User");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
#include "Client.h"
|
|
||||||
#include "prefs/GlobalPrefs.h"
|
|
||||||
#include <emscripten.h>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
void Client::LoadAuthUser()
|
|
||||||
{
|
|
||||||
ByteString newUsername, newSessionKey;
|
|
||||||
if (EM_ASM_INT({
|
|
||||||
return (document.querySelector("#PowderSessionInfo [name='Username']") &&
|
|
||||||
document.querySelector("#PowderSessionInfo [name='SessionKey']")) ? 1 : 0;
|
|
||||||
}))
|
|
||||||
{
|
|
||||||
newUsername = ByteString(std::unique_ptr<char, decltype(&free)>((char *)EM_ASM_PTR({
|
|
||||||
return stringToNewUTF8(document.querySelector("#PowderSessionInfo [name='Username']").value);
|
|
||||||
}), free).get());
|
|
||||||
newSessionKey = ByteString(std::unique_ptr<char, decltype(&free)>((char *)EM_ASM_PTR({
|
|
||||||
return stringToNewUTF8(document.querySelector("#PowderSessionInfo [name='SessionKey']").value);
|
|
||||||
}), free).get());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::cerr << "required #PowderSessionInfo elements not found, can't authenticate" << std::endl;
|
|
||||||
}
|
|
||||||
if (newUsername.size() && newSessionKey.size())
|
|
||||||
{
|
|
||||||
authUser.UserID = -1; // Not quite valid but evaluates to true and that's all that matters for this codebase.
|
|
||||||
authUser.Username = newUsername;
|
|
||||||
authUser.SessionID = "(invalid)";
|
|
||||||
authUser.SessionKey = newSessionKey;
|
|
||||||
authUser.UserElevation = User::ElevationNone; // We don't deal with this in the browser.
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
authUser.UserID = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Client::SaveAuthUser()
|
|
||||||
{
|
|
||||||
// Nothing is; the cookie headers in the login and logout responses take care of state management.
|
|
||||||
}
|
|
@ -1,8 +1,9 @@
|
|||||||
#include "Client.h"
|
#include "Client.h"
|
||||||
#include "prefs/GlobalPrefs.h"
|
#include "prefs/GlobalPrefs.h"
|
||||||
#include "client/http/StartupRequest.h"
|
#include "client/http/Request.h"
|
||||||
#include "ClientListener.h"
|
#include "ClientListener.h"
|
||||||
#include "Format.h"
|
#include "Format.h"
|
||||||
|
#include "MD5.h"
|
||||||
#include "client/GameSave.h"
|
#include "client/GameSave.h"
|
||||||
#include "client/SaveFile.h"
|
#include "client/SaveFile.h"
|
||||||
#include "client/SaveInfo.h"
|
#include "client/SaveInfo.h"
|
||||||
@ -10,9 +11,9 @@
|
|||||||
#include "common/platform/Platform.h"
|
#include "common/platform/Platform.h"
|
||||||
#include "common/String.h"
|
#include "common/String.h"
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
#include "gui/dialogues/ErrorMessage.h"
|
|
||||||
#include "prefs/Prefs.h"
|
#include "prefs/Prefs.h"
|
||||||
#include "lua/CommandInterface.h"
|
#include "lua/CommandInterface.h"
|
||||||
|
#include "gui/preview/Comment.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
@ -28,12 +29,27 @@
|
|||||||
|
|
||||||
Client::Client():
|
Client::Client():
|
||||||
messageOfTheDay("Fetching the message of the day..."),
|
messageOfTheDay("Fetching the message of the day..."),
|
||||||
|
versionCheckRequest(nullptr),
|
||||||
|
alternateVersionCheckRequest(nullptr),
|
||||||
usingAltUpdateServer(false),
|
usingAltUpdateServer(false),
|
||||||
updateAvailable(false),
|
updateAvailable(false),
|
||||||
authUser(0, "")
|
authUser(0, "")
|
||||||
{
|
{
|
||||||
LoadAuthUser();
|
|
||||||
auto &prefs = GlobalPrefs::Ref();
|
auto &prefs = GlobalPrefs::Ref();
|
||||||
|
authUser.UserID = prefs.Get("User.ID", 0);
|
||||||
|
authUser.Username = prefs.Get("User.Username", ByteString(""));
|
||||||
|
authUser.SessionID = prefs.Get("User.SessionID", ByteString(""));
|
||||||
|
authUser.SessionKey = prefs.Get("User.SessionKey", ByteString(""));
|
||||||
|
auto elevation = prefs.Get("User.Elevation", ByteString(""));
|
||||||
|
authUser.UserElevation = User::ElevationNone;
|
||||||
|
if (elevation == "Admin")
|
||||||
|
{
|
||||||
|
authUser.UserElevation = User::ElevationAdmin;
|
||||||
|
}
|
||||||
|
if (elevation == "Mod")
|
||||||
|
{
|
||||||
|
authUser.UserElevation = User::ElevationModerator;
|
||||||
|
}
|
||||||
firstRun = !prefs.BackedByFile();
|
firstRun = !prefs.BackedByFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,14 +88,24 @@ void Client::Initialize()
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Begin version check
|
//Begin version check
|
||||||
versionCheckRequest = std::make_unique<http::StartupRequest>(false);
|
versionCheckRequest = std::make_unique<http::Request>(ByteString::Build(SCHEME, SERVER, "/Startup.json"));
|
||||||
|
|
||||||
|
if (authUser.UserID)
|
||||||
|
{
|
||||||
|
versionCheckRequest->AuthHeaders(ByteString::Build(authUser.UserID), authUser.SessionID);
|
||||||
|
}
|
||||||
versionCheckRequest->Start();
|
versionCheckRequest->Start();
|
||||||
|
|
||||||
if constexpr (USE_UPDATESERVER)
|
if constexpr (USE_UPDATESERVER)
|
||||||
{
|
{
|
||||||
// use an alternate update server
|
// use an alternate update server
|
||||||
alternateVersionCheckRequest = std::make_unique<http::StartupRequest>(true);
|
alternateVersionCheckRequest = std::make_unique<http::Request>(ByteString::Build(SCHEME, UPDATESERVER, "/Startup.json"));
|
||||||
alternateVersionCheckRequest->Start();
|
|
||||||
usingAltUpdateServer = true;
|
usingAltUpdateServer = true;
|
||||||
|
if (authUser.UserID)
|
||||||
|
{
|
||||||
|
alternateVersionCheckRequest->AuthHeaders(authUser.Username, "");
|
||||||
|
}
|
||||||
|
alternateVersionCheckRequest->Start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,82 +125,203 @@ String Client::GetMessageOfTheDay()
|
|||||||
return messageOfTheDay;
|
return messageOfTheDay;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::AddServerNotification(ServerNotification notification)
|
void Client::AddServerNotification(std::pair<String, ByteString> notification)
|
||||||
{
|
{
|
||||||
serverNotifications.push_back(notification);
|
serverNotifications.push_back(notification);
|
||||||
notifyNewNotification(notification);
|
notifyNewNotification(notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ServerNotification> Client::GetServerNotifications()
|
std::vector<std::pair<String, ByteString> > Client::GetServerNotifications()
|
||||||
{
|
{
|
||||||
return serverNotifications;
|
return serverNotifications;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RequestStatus Client::ParseServerReturn(ByteString &result, int status, bool json)
|
||||||
|
{
|
||||||
|
lastError = "";
|
||||||
|
// no server response, return "Malformed Response"
|
||||||
|
if (status == 200 && !result.size())
|
||||||
|
{
|
||||||
|
status = 603;
|
||||||
|
}
|
||||||
|
if (status == 302)
|
||||||
|
return RequestOkay;
|
||||||
|
if (status != 200)
|
||||||
|
{
|
||||||
|
lastError = String::Build("HTTP Error ", status, ": ", http::StatusText(status));
|
||||||
|
return RequestFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json)
|
||||||
|
{
|
||||||
|
std::istringstream datastream(result);
|
||||||
|
Json::Value root;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
datastream >> root;
|
||||||
|
// assume everything is fine if an empty [] is returned
|
||||||
|
if (root.size() == 0)
|
||||||
|
{
|
||||||
|
return RequestOkay;
|
||||||
|
}
|
||||||
|
int status = root.get("Status", 1).asInt();
|
||||||
|
if (status != 1)
|
||||||
|
{
|
||||||
|
lastError = ByteString(root.get("Error", "Unspecified Error").asString()).FromUtf8();
|
||||||
|
return RequestFailure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::exception &e)
|
||||||
|
{
|
||||||
|
// sometimes the server returns a 200 with the text "Error: 401"
|
||||||
|
if (!strncmp(result.c_str(), "Error: ", 7))
|
||||||
|
{
|
||||||
|
status = ByteString(result.begin() + 7, result.end()).ToNumber<int>();
|
||||||
|
lastError = String::Build("HTTP Error ", status, ": ", http::StatusText(status));
|
||||||
|
return RequestFailure;
|
||||||
|
}
|
||||||
|
lastError = "Could not read response: " + ByteString(e.what()).FromUtf8();
|
||||||
|
return RequestFailure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (strncmp(result.c_str(), "OK", 2))
|
||||||
|
{
|
||||||
|
lastError = result.FromUtf8();
|
||||||
|
return RequestFailure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return RequestOkay;
|
||||||
|
}
|
||||||
|
|
||||||
void Client::Tick()
|
void Client::Tick()
|
||||||
{
|
{
|
||||||
auto applyUpdateInfo = false;
|
CheckUpdate(versionCheckRequest, true);
|
||||||
if (versionCheckRequest && versionCheckRequest->CheckDone())
|
CheckUpdate(alternateVersionCheckRequest, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::CheckUpdate(std::unique_ptr<http::Request> &updateRequest, bool checkSession)
|
||||||
|
{
|
||||||
|
//Check status on version check request
|
||||||
|
if (updateRequest && updateRequest->CheckDone())
|
||||||
{
|
{
|
||||||
if (versionCheckRequest->StatusCode() == 618)
|
auto [ status, data ] = updateRequest->Finish();
|
||||||
|
|
||||||
|
if (checkSession && status == 618)
|
||||||
{
|
{
|
||||||
AddServerNotification({ "Failed to load SSL certificates", ByteString::Build(SCHEME, SERVER, "/FAQ.html") });
|
AddServerNotification({ "Failed to load SSL certificates", ByteString(SCHEME) + "powdertoy.co.uk/FAQ.html" });
|
||||||
}
|
}
|
||||||
try
|
|
||||||
|
if (status != 200)
|
||||||
{
|
{
|
||||||
auto info = versionCheckRequest->Finish();
|
//free(data);
|
||||||
if (!info.sessionGood)
|
if (usingAltUpdateServer && !checkSession)
|
||||||
|
this->messageOfTheDay = String::Build("HTTP Error ", status, " while checking for updates: ", http::StatusText(status));
|
||||||
|
else
|
||||||
|
this->messageOfTheDay = String::Build("HTTP Error ", status, " while fetching MotD");
|
||||||
|
}
|
||||||
|
else if(data.size())
|
||||||
|
{
|
||||||
|
std::istringstream dataStream(data);
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
SetAuthUser(User(0, ""));
|
Json::Value objDocument;
|
||||||
|
dataStream >> objDocument;
|
||||||
|
|
||||||
|
//Check session
|
||||||
|
if (checkSession)
|
||||||
|
{
|
||||||
|
if (!objDocument["Session"].asBool())
|
||||||
|
{
|
||||||
|
SetAuthUser(User(0, ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Notifications from server
|
||||||
|
Json::Value notificationsArray = objDocument["Notifications"];
|
||||||
|
for (Json::UInt j = 0; j < notificationsArray.size(); j++)
|
||||||
|
{
|
||||||
|
ByteString notificationLink = notificationsArray[j]["Link"].asString();
|
||||||
|
String notificationText = ByteString(notificationsArray[j]["Text"].asString()).FromUtf8();
|
||||||
|
|
||||||
|
std::pair<String, ByteString> item = std::pair<String, ByteString>(notificationText, notificationLink);
|
||||||
|
AddServerNotification(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//MOTD
|
||||||
|
if (!usingAltUpdateServer || !checkSession)
|
||||||
|
{
|
||||||
|
this->messageOfTheDay = ByteString(objDocument["MessageOfTheDay"].asString()).FromUtf8();
|
||||||
|
notifyMessageOfTheDay();
|
||||||
|
|
||||||
|
if constexpr (!IGNORE_UPDATES)
|
||||||
|
{
|
||||||
|
//Check for updates
|
||||||
|
Json::Value versions = objDocument["Updates"];
|
||||||
|
if constexpr (!SNAPSHOT)
|
||||||
|
{
|
||||||
|
Json::Value stableVersion = versions["Stable"];
|
||||||
|
int stableMajor = stableVersion["Major"].asInt();
|
||||||
|
int stableMinor = stableVersion["Minor"].asInt();
|
||||||
|
int stableBuild = stableVersion["Build"].asInt();
|
||||||
|
ByteString stableFile = stableVersion["File"].asString();
|
||||||
|
String stableChangelog = ByteString(stableVersion["Changelog"].asString()).FromUtf8();
|
||||||
|
if (stableBuild > BUILD_NUM)
|
||||||
|
{
|
||||||
|
updateAvailable = true;
|
||||||
|
updateInfo = UpdateInfo(stableMajor, stableMinor, stableBuild, stableFile, stableChangelog, UpdateInfo::Stable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!updateAvailable)
|
||||||
|
{
|
||||||
|
Json::Value betaVersion = versions["Beta"];
|
||||||
|
int betaMajor = betaVersion["Major"].asInt();
|
||||||
|
int betaMinor = betaVersion["Minor"].asInt();
|
||||||
|
int betaBuild = betaVersion["Build"].asInt();
|
||||||
|
ByteString betaFile = betaVersion["File"].asString();
|
||||||
|
String betaChangelog = ByteString(betaVersion["Changelog"].asString()).FromUtf8();
|
||||||
|
if (betaBuild > BUILD_NUM)
|
||||||
|
{
|
||||||
|
updateAvailable = true;
|
||||||
|
updateInfo = UpdateInfo(betaMajor, betaMinor, betaBuild, betaFile, betaChangelog, UpdateInfo::Beta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (SNAPSHOT || MOD)
|
||||||
|
{
|
||||||
|
Json::Value snapshotVersion = versions["Snapshot"];
|
||||||
|
int snapshotSnapshot = snapshotVersion["Snapshot"].asInt();
|
||||||
|
ByteString snapshotFile = snapshotVersion["File"].asString();
|
||||||
|
String snapshotChangelog = ByteString(snapshotVersion["Changelog"].asString()).FromUtf8();
|
||||||
|
if (snapshotSnapshot > SNAPSHOT_ID)
|
||||||
|
{
|
||||||
|
updateAvailable = true;
|
||||||
|
updateInfo = UpdateInfo(snapshotSnapshot, snapshotFile, snapshotChangelog, UpdateInfo::Snapshot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(updateAvailable)
|
||||||
|
{
|
||||||
|
notifyUpdateAvailable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!usingAltUpdateServer)
|
catch (std::exception & e)
|
||||||
{
|
{
|
||||||
updateInfo = info.updateInfo;
|
//Do nothing
|
||||||
applyUpdateInfo = true;
|
|
||||||
SetMessageOfTheDay(info.messageOfTheDay);
|
|
||||||
}
|
|
||||||
for (auto ¬ification : info.notifications)
|
|
||||||
{
|
|
||||||
AddServerNotification(notification);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const http::RequestError &ex)
|
updateRequest.reset();
|
||||||
{
|
|
||||||
if (!usingAltUpdateServer)
|
|
||||||
{
|
|
||||||
SetMessageOfTheDay(ByteString::Build("Error while fetching MotD: ", ex.what()).FromUtf8());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
versionCheckRequest.reset();
|
|
||||||
}
|
|
||||||
if (alternateVersionCheckRequest && alternateVersionCheckRequest->CheckDone())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
auto info = alternateVersionCheckRequest->Finish();
|
|
||||||
updateInfo = info.updateInfo;
|
|
||||||
applyUpdateInfo = true;
|
|
||||||
SetMessageOfTheDay(info.messageOfTheDay);
|
|
||||||
for (auto ¬ification : info.notifications)
|
|
||||||
{
|
|
||||||
AddServerNotification(notification);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const http::RequestError &ex)
|
|
||||||
{
|
|
||||||
SetMessageOfTheDay(ByteString::Build("Error while checking for updates: ", ex.what()).FromUtf8());
|
|
||||||
}
|
|
||||||
alternateVersionCheckRequest.reset();
|
|
||||||
}
|
|
||||||
if (applyUpdateInfo && !IGNORE_UPDATES)
|
|
||||||
{
|
|
||||||
if (updateInfo)
|
|
||||||
{
|
|
||||||
notifyUpdateAvailable();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<UpdateInfo> Client::GetUpdateInfo()
|
UpdateInfo Client::GetUpdateInfo()
|
||||||
{
|
{
|
||||||
return updateInfo;
|
return updateInfo;
|
||||||
}
|
}
|
||||||
@ -203,7 +350,7 @@ void Client::notifyAuthUserChanged()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::notifyNewNotification(ServerNotification notification)
|
void Client::notifyNewNotification(std::pair<String, ByteString> notification)
|
||||||
{
|
{
|
||||||
for (std::vector<ClientListener*>::iterator iterator = listeners.begin(), end = listeners.end(); iterator != end; ++iterator)
|
for (std::vector<ClientListener*>::iterator iterator = listeners.begin(), end = listeners.end(); iterator != end; ++iterator)
|
||||||
{
|
{
|
||||||
@ -235,7 +382,31 @@ Client::~Client()
|
|||||||
void Client::SetAuthUser(User user)
|
void Client::SetAuthUser(User user)
|
||||||
{
|
{
|
||||||
authUser = user;
|
authUser = user;
|
||||||
SaveAuthUser();
|
{
|
||||||
|
auto &prefs = GlobalPrefs::Ref();
|
||||||
|
Prefs::DeferWrite dw(prefs);
|
||||||
|
if (authUser.UserID)
|
||||||
|
{
|
||||||
|
prefs.Set("User.ID", authUser.UserID);
|
||||||
|
prefs.Set("User.SessionID", authUser.SessionID);
|
||||||
|
prefs.Set("User.SessionKey", authUser.SessionKey);
|
||||||
|
prefs.Set("User.Username", authUser.Username);
|
||||||
|
ByteString elevation = "None";
|
||||||
|
if (authUser.UserElevation == User::ElevationAdmin)
|
||||||
|
{
|
||||||
|
elevation = "Admin";
|
||||||
|
}
|
||||||
|
if (authUser.UserElevation == User::ElevationModerator)
|
||||||
|
{
|
||||||
|
elevation = "Mod";
|
||||||
|
}
|
||||||
|
prefs.Set("User.Elevation", elevation);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prefs.Clear("User");
|
||||||
|
}
|
||||||
|
}
|
||||||
notifyAuthUserChanged();
|
notifyAuthUserChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,6 +415,65 @@ User Client::GetAuthUser()
|
|||||||
return authUser;
|
return authUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RequestStatus Client::UploadSave(SaveInfo & save)
|
||||||
|
{
|
||||||
|
lastError = "";
|
||||||
|
int dataStatus;
|
||||||
|
ByteString data;
|
||||||
|
ByteString userID = ByteString::Build(authUser.UserID);
|
||||||
|
if (authUser.UserID)
|
||||||
|
{
|
||||||
|
if (!save.GetGameSave())
|
||||||
|
{
|
||||||
|
lastError = "Empty game save";
|
||||||
|
return RequestFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
save.SetID(0);
|
||||||
|
|
||||||
|
auto [ fromNewerVersion, gameData ] = save.GetGameSave()->Serialise();
|
||||||
|
(void)fromNewerVersion;
|
||||||
|
|
||||||
|
if (!gameData.size())
|
||||||
|
{
|
||||||
|
lastError = "Cannot serialize game save";
|
||||||
|
return RequestFailure;
|
||||||
|
}
|
||||||
|
else if (ALLOW_FAKE_NEWER_VERSION && fromNewerVersion && save.GetPublished())
|
||||||
|
{
|
||||||
|
lastError = "Cannot publish save, incompatible with latest release version.";
|
||||||
|
return RequestFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tie(dataStatus, data) = http::Request::SimpleAuth(ByteString::Build(SCHEME, SERVER, "/Save.api"), userID, authUser.SessionID, {
|
||||||
|
{ "Name", save.GetName().ToUtf8() },
|
||||||
|
{ "Description", save.GetDescription().ToUtf8() },
|
||||||
|
{ "Data:save.bin", ByteString(gameData.begin(), gameData.end()) },
|
||||||
|
{ "Publish", save.GetPublished() ? "Public" : "Private" },
|
||||||
|
{ "Key", authUser.SessionKey }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lastError = "Not authenticated";
|
||||||
|
return RequestFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestStatus ret = ParseServerReturn(data, dataStatus, false);
|
||||||
|
if (ret == RequestOkay)
|
||||||
|
{
|
||||||
|
int saveID = ByteString(data.begin() + 3, data.end()).ToNumber<int>();
|
||||||
|
if (!saveID)
|
||||||
|
{
|
||||||
|
lastError = "Server did not return Save ID";
|
||||||
|
ret = RequestFailure;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
save.SetID(saveID);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void Client::MoveStampToFront(ByteString stampID)
|
void Client::MoveStampToFront(ByteString stampID)
|
||||||
{
|
{
|
||||||
auto it = std::find(stampIDs.begin(), stampIDs.end(), stampID);
|
auto it = std::find(stampIDs.begin(), stampIDs.end(), stampID);
|
||||||
@ -265,10 +495,10 @@ void Client::MoveStampToFront(ByteString stampID)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<SaveFile> Client::GetStamp(ByteString stampID)
|
SaveFile * Client::GetStamp(ByteString stampID)
|
||||||
{
|
{
|
||||||
ByteString stampFile = ByteString(ByteString::Build(STAMPS_DIR, PATH_SEP_CHAR, stampID, ".stm"));
|
ByteString stampFile = ByteString(ByteString::Build(STAMPS_DIR, PATH_SEP_CHAR, stampID, ".stm"));
|
||||||
auto saveFile = LoadSaveFile(stampFile);
|
SaveFile *saveFile = LoadSaveFile(stampFile);
|
||||||
if (!saveFile)
|
if (!saveFile)
|
||||||
saveFile = LoadSaveFile(stampID);
|
saveFile = LoadSaveFile(stampID);
|
||||||
else
|
else
|
||||||
@ -287,28 +517,7 @@ void Client::DeleteStamp(ByteString stampID)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::RenameStamp(ByteString stampID, ByteString newName)
|
ByteString Client::AddStamp(GameSave * saveData)
|
||||||
{
|
|
||||||
auto oldPath = ByteString::Build(STAMPS_DIR, PATH_SEP_CHAR, stampID, ".stm");
|
|
||||||
auto newPath = ByteString::Build(STAMPS_DIR, PATH_SEP_CHAR, newName, ".stm");
|
|
||||||
|
|
||||||
if (Platform::FileExists(newPath))
|
|
||||||
{
|
|
||||||
new ErrorMessage("Error renaming stamp", "A stamp with this name already exists.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Platform::RenameFile(oldPath, newPath, false))
|
|
||||||
{
|
|
||||||
new ErrorMessage("Error renaming stamp", "Could not rename the stamp.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::replace(stampIDs.begin(), stampIDs.end(), stampID, newName);
|
|
||||||
WriteStamps();
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteString Client::AddStamp(std::unique_ptr<GameSave> saveData)
|
|
||||||
{
|
{
|
||||||
auto now = (uint64_t)time(NULL);
|
auto now = (uint64_t)time(NULL);
|
||||||
if (lastStampTime != now)
|
if (lastStampTime != now)
|
||||||
@ -346,8 +555,8 @@ ByteString Client::AddStamp(std::unique_ptr<GameSave> saveData)
|
|||||||
}
|
}
|
||||||
saveData->authors = stampInfo;
|
saveData->authors = stampInfo;
|
||||||
|
|
||||||
std::vector<char> gameData;
|
auto [ fromNewerVersion, gameData ] = saveData->Serialise();
|
||||||
std::tie(std::ignore, gameData) = saveData->Serialise();
|
(void)fromNewerVersion;
|
||||||
if (!gameData.size())
|
if (!gameData.size())
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
@ -377,6 +586,7 @@ void Client::RescanStamps()
|
|||||||
newStampIDs.push_back(stampID);
|
newStampIDs.push_back(stampID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
auto oldCount = newStampIDs.size();
|
||||||
auto stampIDsSet = std::set<ByteString>(stampIDs.begin(), stampIDs.end());
|
auto stampIDsSet = std::set<ByteString>(stampIDs.begin(), stampIDs.end());
|
||||||
for (auto &stampID : stampFilesSet)
|
for (auto &stampID : stampFilesSet)
|
||||||
{
|
{
|
||||||
@ -388,6 +598,8 @@ void Client::RescanStamps()
|
|||||||
}
|
}
|
||||||
if (changed)
|
if (changed)
|
||||||
{
|
{
|
||||||
|
// Move newly discovered stamps to front.
|
||||||
|
std::rotate(newStampIDs.begin(), newStampIDs.begin() + oldCount, newStampIDs.end());
|
||||||
stampIDs = newStampIDs;
|
stampIDs = newStampIDs;
|
||||||
WriteStamps();
|
WriteStamps();
|
||||||
}
|
}
|
||||||
@ -406,19 +618,325 @@ const std::vector<ByteString> &Client::GetStamps() const
|
|||||||
return stampIDs;
|
return stampIDs;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<SaveFile> Client::LoadSaveFile(ByteString filename)
|
RequestStatus Client::ExecVote(int saveID, int direction)
|
||||||
|
{
|
||||||
|
lastError = "";
|
||||||
|
int dataStatus;
|
||||||
|
ByteString data;
|
||||||
|
|
||||||
|
if (authUser.UserID)
|
||||||
|
{
|
||||||
|
ByteString saveIDText = ByteString::Build(saveID);
|
||||||
|
ByteString userIDText = ByteString::Build(authUser.UserID);
|
||||||
|
std::tie(dataStatus, data) = http::Request::SimpleAuth(ByteString::Build(SCHEME, SERVER, "/Vote.api"), userIDText, authUser.SessionID, {
|
||||||
|
{ "ID", saveIDText },
|
||||||
|
{ "Action", direction ? (direction == 1 ? "Up" : "Down") : "Reset" },
|
||||||
|
{ "Key", authUser.SessionKey }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lastError = "Not authenticated";
|
||||||
|
return RequestFailure;
|
||||||
|
}
|
||||||
|
RequestStatus ret = ParseServerReturn(data, dataStatus, false);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<char> Client::GetSaveData(int saveID, int saveDate)
|
||||||
|
{
|
||||||
|
lastError = "";
|
||||||
|
ByteString urlStr;
|
||||||
|
if (saveDate)
|
||||||
|
urlStr = ByteString::Build(STATICSCHEME, STATICSERVER, "/", saveID, "_", saveDate, ".cps");
|
||||||
|
else
|
||||||
|
urlStr = ByteString::Build(STATICSCHEME, STATICSERVER, "/", saveID, ".cps");
|
||||||
|
|
||||||
|
auto [ dataStatus, data ] = http::Request::Simple(urlStr);
|
||||||
|
|
||||||
|
// will always return failure
|
||||||
|
ParseServerReturn(data, dataStatus, false);
|
||||||
|
if (data.size() && dataStatus == 200)
|
||||||
|
{
|
||||||
|
return std::vector<char>(data.begin(), data.end());
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
LoginStatus Client::Login(ByteString username, ByteString password, User & user)
|
||||||
|
{
|
||||||
|
lastError = "";
|
||||||
|
|
||||||
|
user.UserID = 0;
|
||||||
|
user.Username = "";
|
||||||
|
user.SessionID = "";
|
||||||
|
user.SessionKey = "";
|
||||||
|
|
||||||
|
auto [ dataStatus, data ] = http::Request::Simple(ByteString::Build("https://", SERVER, "/Login.json"), {
|
||||||
|
{ "name", username },
|
||||||
|
{ "pass", password },
|
||||||
|
});
|
||||||
|
|
||||||
|
RequestStatus ret = ParseServerReturn(data, dataStatus, true);
|
||||||
|
if (ret == RequestOkay)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::istringstream dataStream(data);
|
||||||
|
Json::Value objDocument;
|
||||||
|
dataStream >> objDocument;
|
||||||
|
|
||||||
|
ByteString usernameTemp = objDocument["Username"].asString();
|
||||||
|
int userIDTemp = objDocument["UserID"].asInt();
|
||||||
|
ByteString sessionIDTemp = objDocument["SessionID"].asString();
|
||||||
|
ByteString sessionKeyTemp = objDocument["SessionKey"].asString();
|
||||||
|
ByteString userElevationTemp = objDocument["Elevation"].asString();
|
||||||
|
|
||||||
|
Json::Value notificationsArray = objDocument["Notifications"];
|
||||||
|
for (Json::UInt j = 0; j < notificationsArray.size(); j++)
|
||||||
|
{
|
||||||
|
ByteString notificationLink = notificationsArray[j]["Link"].asString();
|
||||||
|
String notificationText = ByteString(notificationsArray[j]["Text"].asString()).FromUtf8();
|
||||||
|
|
||||||
|
std::pair<String, ByteString> item = std::pair<String, ByteString>(notificationText, notificationLink);
|
||||||
|
AddServerNotification(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
user.Username = usernameTemp;
|
||||||
|
user.UserID = userIDTemp;
|
||||||
|
user.SessionID = sessionIDTemp;
|
||||||
|
user.SessionKey = sessionKeyTemp;
|
||||||
|
ByteString userElevation = userElevationTemp;
|
||||||
|
if(userElevation == "Admin")
|
||||||
|
user.UserElevation = User::ElevationAdmin;
|
||||||
|
else if(userElevation == "Mod")
|
||||||
|
user.UserElevation = User::ElevationModerator;
|
||||||
|
else
|
||||||
|
user.UserElevation= User::ElevationNone;
|
||||||
|
return LoginOkay;
|
||||||
|
}
|
||||||
|
catch (std::exception &e)
|
||||||
|
{
|
||||||
|
lastError = "Could not read response: " + ByteString(e.what()).FromUtf8();
|
||||||
|
return LoginError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return LoginError;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestStatus Client::DeleteSave(int saveID)
|
||||||
|
{
|
||||||
|
lastError = "";
|
||||||
|
ByteString data;
|
||||||
|
ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/Delete.json?ID=", saveID, "&Mode=Delete&Key=", authUser.SessionKey);
|
||||||
|
int dataStatus;
|
||||||
|
if(authUser.UserID)
|
||||||
|
{
|
||||||
|
ByteString userID = ByteString::Build(authUser.UserID);
|
||||||
|
std::tie(dataStatus, data) = http::Request::SimpleAuth(url, userID, authUser.SessionID);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lastError = "Not authenticated";
|
||||||
|
return RequestFailure;
|
||||||
|
}
|
||||||
|
RequestStatus ret = ParseServerReturn(data, dataStatus, true);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestStatus Client::AddComment(int saveID, String comment)
|
||||||
|
{
|
||||||
|
lastError = "";
|
||||||
|
ByteString data;
|
||||||
|
int dataStatus;
|
||||||
|
ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/Comments.json?ID=", saveID);
|
||||||
|
if(authUser.UserID)
|
||||||
|
{
|
||||||
|
ByteString userID = ByteString::Build(authUser.UserID);
|
||||||
|
std::tie(dataStatus, data) = http::Request::SimpleAuth(url, userID, authUser.SessionID, {
|
||||||
|
{ "Comment", comment.ToUtf8() },
|
||||||
|
{ "Key", authUser.SessionKey }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lastError = "Not authenticated";
|
||||||
|
return RequestFailure;
|
||||||
|
}
|
||||||
|
RequestStatus ret = ParseServerReturn(data, dataStatus, true);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestStatus Client::FavouriteSave(int saveID, bool favourite)
|
||||||
|
{
|
||||||
|
lastError = "";
|
||||||
|
ByteStringBuilder urlStream;
|
||||||
|
ByteString data;
|
||||||
|
int dataStatus;
|
||||||
|
urlStream << SCHEME << SERVER << "/Browse/Favourite.json?ID=" << saveID << "&Key=" << authUser.SessionKey;
|
||||||
|
if(!favourite)
|
||||||
|
urlStream << "&Mode=Remove";
|
||||||
|
if(authUser.UserID)
|
||||||
|
{
|
||||||
|
ByteString userID = ByteString::Build(authUser.UserID);
|
||||||
|
std::tie(dataStatus, data) = http::Request::SimpleAuth(urlStream.Build(), userID, authUser.SessionID);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lastError = "Not authenticated";
|
||||||
|
return RequestFailure;
|
||||||
|
}
|
||||||
|
RequestStatus ret = ParseServerReturn(data, dataStatus, true);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestStatus Client::ReportSave(int saveID, String message)
|
||||||
|
{
|
||||||
|
lastError = "";
|
||||||
|
ByteString data;
|
||||||
|
int dataStatus;
|
||||||
|
ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/Report.json?ID=", saveID, "&Key=", authUser.SessionKey);
|
||||||
|
if(authUser.UserID)
|
||||||
|
{
|
||||||
|
ByteString userID = ByteString::Build(authUser.UserID);
|
||||||
|
std::tie(dataStatus, data) = http::Request::SimpleAuth(url, userID, authUser.SessionID, {
|
||||||
|
{ "Reason", message.ToUtf8() },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lastError = "Not authenticated";
|
||||||
|
return RequestFailure;
|
||||||
|
}
|
||||||
|
RequestStatus ret = ParseServerReturn(data, dataStatus, true);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestStatus Client::UnpublishSave(int saveID)
|
||||||
|
{
|
||||||
|
lastError = "";
|
||||||
|
ByteString data;
|
||||||
|
int dataStatus;
|
||||||
|
ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/Delete.json?ID=", saveID, "&Mode=Unpublish&Key=", authUser.SessionKey);
|
||||||
|
if(authUser.UserID)
|
||||||
|
{
|
||||||
|
ByteString userID = ByteString::Build(authUser.UserID);
|
||||||
|
std::tie(dataStatus, data) = http::Request::SimpleAuth(url, userID, authUser.SessionID);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lastError = "Not authenticated";
|
||||||
|
return RequestFailure;
|
||||||
|
}
|
||||||
|
RequestStatus ret = ParseServerReturn(data, dataStatus, true);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestStatus Client::PublishSave(int saveID)
|
||||||
|
{
|
||||||
|
lastError = "";
|
||||||
|
ByteString data;
|
||||||
|
int dataStatus;
|
||||||
|
ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/View.json?ID=", saveID, "&Key=", authUser.SessionKey);
|
||||||
|
if (authUser.UserID)
|
||||||
|
{
|
||||||
|
ByteString userID = ByteString::Build(authUser.UserID);
|
||||||
|
std::tie(dataStatus, data) = http::Request::SimpleAuth(url, userID, authUser.SessionID, {
|
||||||
|
{ "ActionPublish", "bagels" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lastError = "Not authenticated";
|
||||||
|
return RequestFailure;
|
||||||
|
}
|
||||||
|
RequestStatus ret = ParseServerReturn(data, dataStatus, true);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveInfo * Client::GetSave(int saveID, int saveDate)
|
||||||
|
{
|
||||||
|
lastError = "";
|
||||||
|
ByteStringBuilder urlStream;
|
||||||
|
urlStream << SCHEME << SERVER << "/Browse/View.json?ID=" << saveID;
|
||||||
|
if(saveDate)
|
||||||
|
{
|
||||||
|
urlStream << "&Date=" << saveDate;
|
||||||
|
}
|
||||||
|
ByteString data;
|
||||||
|
int dataStatus;
|
||||||
|
if(authUser.UserID)
|
||||||
|
{
|
||||||
|
ByteString userID = ByteString::Build(authUser.UserID);
|
||||||
|
std::tie(dataStatus, data) = http::Request::SimpleAuth(urlStream.Build(), userID, authUser.SessionID);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::tie(dataStatus, data) = http::Request::Simple(urlStream.Build());
|
||||||
|
}
|
||||||
|
if(dataStatus == 200 && data.size())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::istringstream dataStream(data);
|
||||||
|
Json::Value objDocument;
|
||||||
|
dataStream >> objDocument;
|
||||||
|
|
||||||
|
int tempID = objDocument["ID"].asInt();
|
||||||
|
int tempScoreUp = objDocument["ScoreUp"].asInt();
|
||||||
|
int tempScoreDown = objDocument["ScoreDown"].asInt();
|
||||||
|
int tempMyScore = objDocument["ScoreMine"].asInt();
|
||||||
|
ByteString tempUsername = objDocument["Username"].asString();
|
||||||
|
String tempName = ByteString(objDocument["Name"].asString()).FromUtf8();
|
||||||
|
String tempDescription = ByteString(objDocument["Description"].asString()).FromUtf8();
|
||||||
|
int tempCreatedDate = objDocument["DateCreated"].asInt();
|
||||||
|
int tempUpdatedDate = objDocument["Date"].asInt();
|
||||||
|
bool tempPublished = objDocument["Published"].asBool();
|
||||||
|
bool tempFavourite = objDocument["Favourite"].asBool();
|
||||||
|
int tempComments = objDocument["Comments"].asInt();
|
||||||
|
int tempViews = objDocument["Views"].asInt();
|
||||||
|
int tempVersion = objDocument["Version"].asInt();
|
||||||
|
|
||||||
|
Json::Value tagsArray = objDocument["Tags"];
|
||||||
|
std::list<ByteString> tempTags;
|
||||||
|
for (Json::UInt j = 0; j < tagsArray.size(); j++)
|
||||||
|
tempTags.push_back(tagsArray[j].asString());
|
||||||
|
|
||||||
|
SaveInfo * tempSave = new SaveInfo(tempID, tempCreatedDate, tempUpdatedDate, tempScoreUp,
|
||||||
|
tempScoreDown, tempMyScore, tempUsername, tempName,
|
||||||
|
tempDescription, tempPublished, tempTags);
|
||||||
|
tempSave->Comments = tempComments;
|
||||||
|
tempSave->Favourite = tempFavourite;
|
||||||
|
tempSave->Views = tempViews;
|
||||||
|
tempSave->Version = tempVersion;
|
||||||
|
return tempSave;
|
||||||
|
}
|
||||||
|
catch (std::exception & e)
|
||||||
|
{
|
||||||
|
lastError = "Could not read response: " + ByteString(e.what()).FromUtf8();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lastError = http::StatusText(dataStatus);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveFile * Client::LoadSaveFile(ByteString filename)
|
||||||
{
|
{
|
||||||
ByteString err;
|
ByteString err;
|
||||||
std::unique_ptr<SaveFile> file;
|
SaveFile *file = nullptr;
|
||||||
if (Platform::FileExists(filename))
|
if (Platform::FileExists(filename))
|
||||||
{
|
{
|
||||||
file = std::make_unique<SaveFile>(filename);
|
file = new SaveFile(filename);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::vector<char> data;
|
std::vector<char> data;
|
||||||
if (Platform::ReadFile(data, filename))
|
if (Platform::ReadFile(data, filename))
|
||||||
{
|
{
|
||||||
file->SetGameSave(std::make_unique<GameSave>(std::move(data)));
|
file->SetGameSave(new GameSave(std::move(data)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -441,10 +959,89 @@ std::unique_ptr<SaveFile> Client::LoadSaveFile(ByteString filename)
|
|||||||
{
|
{
|
||||||
file->SetLoadingError(err.FromUtf8());
|
file->SetLoadingError(err.FromUtf8());
|
||||||
}
|
}
|
||||||
|
commandInterface->SetLastError(err.FromUtf8());
|
||||||
}
|
}
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::list<ByteString> * Client::RemoveTag(int saveID, ByteString tag)
|
||||||
|
{
|
||||||
|
lastError = "";
|
||||||
|
std::list<ByteString> * tags = NULL;
|
||||||
|
ByteString data;
|
||||||
|
int dataStatus;
|
||||||
|
ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/EditTag.json?Op=delete&ID=", saveID, "&Tag=", tag, "&Key=", authUser.SessionKey);
|
||||||
|
if(authUser.UserID)
|
||||||
|
{
|
||||||
|
ByteString userID = ByteString::Build(authUser.UserID);
|
||||||
|
std::tie(dataStatus, data) = http::Request::SimpleAuth(url, userID, authUser.SessionID);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lastError = "Not authenticated";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
RequestStatus ret = ParseServerReturn(data, dataStatus, true);
|
||||||
|
if (ret == RequestOkay)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::istringstream dataStream(data);
|
||||||
|
Json::Value responseObject;
|
||||||
|
dataStream >> responseObject;
|
||||||
|
|
||||||
|
Json::Value tagsArray = responseObject["Tags"];
|
||||||
|
tags = new std::list<ByteString>();
|
||||||
|
for (Json::UInt j = 0; j < tagsArray.size(); j++)
|
||||||
|
tags->push_back(tagsArray[j].asString());
|
||||||
|
}
|
||||||
|
catch (std::exception &e)
|
||||||
|
{
|
||||||
|
lastError = "Could not read response: " + ByteString(e.what()).FromUtf8();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<ByteString> * Client::AddTag(int saveID, ByteString tag)
|
||||||
|
{
|
||||||
|
lastError = "";
|
||||||
|
std::list<ByteString> * tags = NULL;
|
||||||
|
ByteString data;
|
||||||
|
int dataStatus;
|
||||||
|
ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/EditTag.json?Op=add&ID=", saveID, "&Tag=", tag, "&Key=", authUser.SessionKey);
|
||||||
|
if(authUser.UserID)
|
||||||
|
{
|
||||||
|
ByteString userID = ByteString::Build(authUser.UserID);
|
||||||
|
std::tie(dataStatus, data) = http::Request::SimpleAuth(url, userID, authUser.SessionID);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lastError = "Not authenticated";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
RequestStatus ret = ParseServerReturn(data, dataStatus, true);
|
||||||
|
if (ret == RequestOkay)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::istringstream dataStream(data);
|
||||||
|
Json::Value responseObject;
|
||||||
|
dataStream >> responseObject;
|
||||||
|
|
||||||
|
Json::Value tagsArray = responseObject["Tags"];
|
||||||
|
tags = new std::list<ByteString>();
|
||||||
|
for (Json::UInt j = 0; j < tagsArray.size(); j++)
|
||||||
|
tags->push_back(tagsArray[j].asString());
|
||||||
|
}
|
||||||
|
catch (std::exception & e)
|
||||||
|
{
|
||||||
|
lastError = "Could not read response: " + ByteString(e.what()).FromUtf8();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
|
||||||
// stamp-specific wrapper for MergeAuthorInfo
|
// stamp-specific wrapper for MergeAuthorInfo
|
||||||
// also used for clipboard and lua stamps
|
// also used for clipboard and lua stamps
|
||||||
void Client::MergeStampAuthorInfo(Json::Value stampAuthors)
|
void Client::MergeStampAuthorInfo(Json::Value stampAuthors)
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "common/String.h"
|
#include "common/String.h"
|
||||||
#include "common/ExplicitSingleton.h"
|
#include "common/ExplicitSingleton.h"
|
||||||
#include "StartupInfo.h"
|
|
||||||
#include "User.h"
|
#include "User.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@ -11,27 +10,53 @@
|
|||||||
|
|
||||||
class SaveInfo;
|
class SaveInfo;
|
||||||
class SaveFile;
|
class SaveFile;
|
||||||
|
class SaveComment;
|
||||||
class GameSave;
|
class GameSave;
|
||||||
class VideoBuffer;
|
class VideoBuffer;
|
||||||
|
|
||||||
|
enum LoginStatus {
|
||||||
|
LoginOkay, LoginError
|
||||||
|
};
|
||||||
|
|
||||||
|
enum RequestStatus {
|
||||||
|
RequestOkay, RequestFailure
|
||||||
|
};
|
||||||
|
|
||||||
|
class UpdateInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum BuildType { Stable, Beta, Snapshot };
|
||||||
|
ByteString File;
|
||||||
|
String Changelog;
|
||||||
|
int Major;
|
||||||
|
int Minor;
|
||||||
|
int Build;
|
||||||
|
int Time;
|
||||||
|
BuildType Type;
|
||||||
|
UpdateInfo() : File(""), Changelog(""), Major(0), Minor(0), Build(0), Time(0), Type(Stable) {}
|
||||||
|
UpdateInfo(int major, int minor, int build, ByteString file, String changelog, BuildType type) : File(file), Changelog(changelog), Major(major), Minor(minor), Build(build), Time(0), Type(type) {}
|
||||||
|
UpdateInfo(int time, ByteString file, String changelog, BuildType type) : File(file), Changelog(changelog), Major(0), Minor(0), Build(0), Time(time), Type(type) {}
|
||||||
|
};
|
||||||
|
|
||||||
class Prefs;
|
class Prefs;
|
||||||
class RequestListener;
|
class RequestListener;
|
||||||
class ClientListener;
|
class ClientListener;
|
||||||
namespace http
|
namespace http
|
||||||
{
|
{
|
||||||
class StartupRequest;
|
class Request;
|
||||||
}
|
}
|
||||||
class Client: public ExplicitSingleton<Client> {
|
class Client: public ExplicitSingleton<Client> {
|
||||||
private:
|
private:
|
||||||
String messageOfTheDay;
|
String messageOfTheDay;
|
||||||
std::vector<ServerNotification> serverNotifications;
|
std::vector<std::pair<String, ByteString> > serverNotifications;
|
||||||
|
|
||||||
std::unique_ptr<http::StartupRequest> versionCheckRequest;
|
std::unique_ptr<http::Request> versionCheckRequest;
|
||||||
std::unique_ptr<http::StartupRequest> alternateVersionCheckRequest;
|
std::unique_ptr<http::Request> alternateVersionCheckRequest;
|
||||||
bool usingAltUpdateServer;
|
bool usingAltUpdateServer;
|
||||||
bool updateAvailable;
|
bool updateAvailable;
|
||||||
std::optional<UpdateInfo> updateInfo;
|
UpdateInfo updateInfo;
|
||||||
|
|
||||||
|
String lastError;
|
||||||
bool firstRun;
|
bool firstRun;
|
||||||
|
|
||||||
std::vector<ByteString> stampIDs;
|
std::vector<ByteString> stampIDs;
|
||||||
@ -44,7 +69,7 @@ private:
|
|||||||
void notifyUpdateAvailable();
|
void notifyUpdateAvailable();
|
||||||
void notifyAuthUserChanged();
|
void notifyAuthUserChanged();
|
||||||
void notifyMessageOfTheDay();
|
void notifyMessageOfTheDay();
|
||||||
void notifyNewNotification(ServerNotification notification);
|
void notifyNewNotification(std::pair<String, ByteString> notification);
|
||||||
|
|
||||||
// Save stealing info
|
// Save stealing info
|
||||||
Json::Value authors;
|
Json::Value authors;
|
||||||
@ -53,9 +78,6 @@ private:
|
|||||||
void MigrateStampsDef();
|
void MigrateStampsDef();
|
||||||
void WriteStamps();
|
void WriteStamps();
|
||||||
|
|
||||||
void LoadAuthUser();
|
|
||||||
void SaveAuthUser();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
std::vector<ClientListener*> listeners;
|
std::vector<ClientListener*> listeners;
|
||||||
@ -69,7 +91,7 @@ public:
|
|||||||
void ClearAuthorInfo() { authors.clear(); }
|
void ClearAuthorInfo() { authors.clear(); }
|
||||||
bool IsAuthorsEmpty() { return authors.size() == 0; }
|
bool IsAuthorsEmpty() { return authors.size() == 0; }
|
||||||
|
|
||||||
std::optional<UpdateInfo> GetUpdateInfo();
|
UpdateInfo GetUpdateInfo();
|
||||||
|
|
||||||
Client();
|
Client();
|
||||||
~Client();
|
~Client();
|
||||||
@ -77,8 +99,8 @@ public:
|
|||||||
ByteString FileOpenDialogue();
|
ByteString FileOpenDialogue();
|
||||||
//std::string FileSaveDialogue();
|
//std::string FileSaveDialogue();
|
||||||
|
|
||||||
void AddServerNotification(ServerNotification notification);
|
void AddServerNotification(std::pair<String, ByteString> notification);
|
||||||
std::vector<ServerNotification> GetServerNotifications();
|
std::vector<std::pair<String, ByteString> > GetServerNotifications();
|
||||||
|
|
||||||
void SetMessageOfTheDay(String message);
|
void SetMessageOfTheDay(String message);
|
||||||
String GetMessageOfTheDay();
|
String GetMessageOfTheDay();
|
||||||
@ -89,19 +111,40 @@ public:
|
|||||||
void AddListener(ClientListener * listener);
|
void AddListener(ClientListener * listener);
|
||||||
void RemoveListener(ClientListener * listener);
|
void RemoveListener(ClientListener * listener);
|
||||||
|
|
||||||
std::unique_ptr<SaveFile> GetStamp(ByteString stampID);
|
RequestStatus ExecVote(int saveID, int direction);
|
||||||
|
RequestStatus UploadSave(SaveInfo & save);
|
||||||
|
|
||||||
|
SaveFile * GetStamp(ByteString stampID);
|
||||||
void DeleteStamp(ByteString stampID);
|
void DeleteStamp(ByteString stampID);
|
||||||
void RenameStamp(ByteString stampID, ByteString newName);
|
ByteString AddStamp(GameSave * saveData);
|
||||||
ByteString AddStamp(std::unique_ptr<GameSave> saveData);
|
|
||||||
void RescanStamps();
|
void RescanStamps();
|
||||||
const std::vector<ByteString> &GetStamps() const;
|
const std::vector<ByteString> &GetStamps() const;
|
||||||
void MoveStampToFront(ByteString stampID);
|
void MoveStampToFront(ByteString stampID);
|
||||||
|
|
||||||
std::unique_ptr<SaveFile> LoadSaveFile(ByteString filename);
|
RequestStatus AddComment(int saveID, String comment);
|
||||||
|
|
||||||
|
std::vector<char> GetSaveData(int saveID, int saveDate);
|
||||||
|
|
||||||
|
LoginStatus Login(ByteString username, ByteString password, User & user);
|
||||||
|
|
||||||
|
SaveInfo * GetSave(int saveID, int saveDate);
|
||||||
|
SaveFile * LoadSaveFile(ByteString filename);
|
||||||
|
|
||||||
|
RequestStatus DeleteSave(int saveID);
|
||||||
|
RequestStatus ReportSave(int saveID, String message);
|
||||||
|
RequestStatus UnpublishSave(int saveID);
|
||||||
|
RequestStatus PublishSave(int saveID);
|
||||||
|
RequestStatus FavouriteSave(int saveID, bool favourite);
|
||||||
void SetAuthUser(User user);
|
void SetAuthUser(User user);
|
||||||
User GetAuthUser();
|
User GetAuthUser();
|
||||||
|
std::list<ByteString> * RemoveTag(int saveID, ByteString tag); //TODO RequestStatus
|
||||||
|
std::list<ByteString> * AddTag(int saveID, ByteString tag);
|
||||||
|
String GetLastError() {
|
||||||
|
return lastError;
|
||||||
|
}
|
||||||
|
RequestStatus ParseServerReturn(ByteString &result, int status, bool json);
|
||||||
void Tick();
|
void Tick();
|
||||||
|
void CheckUpdate(std::unique_ptr<http::Request> &updateRequest, bool checkSession);
|
||||||
|
|
||||||
String DoMigration(ByteString fromDir, ByteString toDir);
|
String DoMigration(ByteString fromDir, ByteString toDir);
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "common/String.h"
|
#include "common/String.h"
|
||||||
#include "client/ServerNotification.h"
|
|
||||||
|
|
||||||
class Client;
|
class Client;
|
||||||
class ClientListener
|
class ClientListener
|
||||||
@ -12,6 +11,6 @@ public:
|
|||||||
virtual void NotifyUpdateAvailable(Client * sender) {}
|
virtual void NotifyUpdateAvailable(Client * sender) {}
|
||||||
virtual void NotifyAuthUserChanged(Client * sender) {}
|
virtual void NotifyAuthUserChanged(Client * sender) {}
|
||||||
virtual void NotifyMessageOfTheDay(Client * sender) {}
|
virtual void NotifyMessageOfTheDay(Client * sender) {}
|
||||||
virtual void NotifyNewNotification(Client * sender, ServerNotification notification) {}
|
virtual void NotifyNewNotification(Client * sender, std::pair<String, ByteString> notification) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "User.h"
|
|
||||||
|
|
||||||
struct Comment
|
|
||||||
{
|
|
||||||
ByteString authorName;
|
|
||||||
User::Elevation authorElevation;
|
|
||||||
bool authorIsSelf;
|
|
||||||
bool authorIsBanned;
|
|
||||||
String content;
|
|
||||||
};
|
|
File diff suppressed because it is too large
Load Diff
@ -1,15 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "common/Plane.h"
|
#include "common/Plane.h"
|
||||||
#include "common/String.h"
|
#include "common/String.h"
|
||||||
#include "common/tpt-rand.h"
|
|
||||||
#include "common/Version.h"
|
|
||||||
#include "simulation/Sign.h"
|
#include "simulation/Sign.h"
|
||||||
#include "simulation/Particle.h"
|
#include "simulation/Particle.h"
|
||||||
#include "simulation/MissingElements.h"
|
|
||||||
#include "Misc.h"
|
#include "Misc.h"
|
||||||
#include "SimulationConfig.h"
|
#include "SimulationConfig.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <array>
|
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
|
|
||||||
struct sign;
|
struct sign;
|
||||||
@ -56,39 +52,58 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename Item>
|
||||||
|
struct [[deprecated("Use PlaneAdapter<std::vector>")]] Plane: PlaneAdapter<std::vector<Item>>
|
||||||
|
{
|
||||||
|
[[deprecated("Use operator[](Vec2)")]]
|
||||||
|
Item *operator [](int y)
|
||||||
|
{
|
||||||
|
return &*PlaneAdapter<std::vector<Item>>::RowIterator(Vec2(0, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[deprecated("Use operator[](Vec2)")]]
|
||||||
|
const Item *operator [](int y) const
|
||||||
|
{
|
||||||
|
return &*PlaneAdapter<std::vector<Item>>::RowIterator(Vec2(0, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[deprecated("Use PlaneAdapter<std::vector>")]]
|
||||||
|
Plane() = default;
|
||||||
|
|
||||||
|
[[deprecated("Use PlaneAdapter<std::vector>")]]
|
||||||
|
Plane(int newWidth, int newHeight, Item defaultVal):
|
||||||
|
PlaneAdapter<std::vector<Item>>(Vec2(newWidth, newHeight), defaultVal)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class GameSave
|
class GameSave
|
||||||
{
|
{
|
||||||
// number of pixels translated. When translating CELL pixels, shift all CELL grids
|
// number of pixels translated. When translating CELL pixels, shift all CELL grids
|
||||||
|
vector2d translated = { 0, 0 };
|
||||||
void readOPS(const std::vector<char> &data);
|
void readOPS(const std::vector<char> &data);
|
||||||
void readPSv(const std::vector<char> &data);
|
void readPSv(const std::vector<char> &data);
|
||||||
std::pair<bool, std::vector<char>> serialiseOPS() const;
|
std::pair<bool, std::vector<char>> serialiseOPS() const;
|
||||||
|
|
||||||
void MapPalette();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Vec2<int> blockSize = { 0, 0 };
|
int blockWidth = 0;
|
||||||
|
int blockHeight = 0;
|
||||||
bool fromNewerVersion = false;
|
bool fromNewerVersion = false;
|
||||||
Version<2> version{};
|
int majorVersion = 0;
|
||||||
|
int minorVersion = 0;
|
||||||
bool hasPressure = false;
|
bool hasPressure = false;
|
||||||
bool hasAmbientHeat = false;
|
bool hasAmbientHeat = false;
|
||||||
bool hasBlockAirMaps = false; // only written by readOPS, never read
|
|
||||||
bool ensureDeterminism = false; // only taken seriously by serializeOPS; readOPS may set this even if the save does not have everything required for determinism
|
|
||||||
bool hasRngState = false; // only written by readOPS, never read
|
|
||||||
RNG::State rngState;
|
|
||||||
uint64_t frameCount = 0;
|
|
||||||
|
|
||||||
//Simulation data
|
//Simulation data
|
||||||
int particlesCount = 0;
|
int particlesCount = 0;
|
||||||
std::vector<Particle> particles;
|
std::vector<Particle> particles;
|
||||||
PlaneAdapter<std::vector<unsigned char>> blockMap;
|
Plane<unsigned char> blockMap;
|
||||||
PlaneAdapter<std::vector<float>> fanVelX;
|
Plane<float> fanVelX;
|
||||||
PlaneAdapter<std::vector<float>> fanVelY;
|
Plane<float> fanVelY;
|
||||||
PlaneAdapter<std::vector<float>> pressure;
|
Plane<float> pressure;
|
||||||
PlaneAdapter<std::vector<float>> velocityX;
|
Plane<float> velocityX;
|
||||||
PlaneAdapter<std::vector<float>> velocityY;
|
Plane<float> velocityY;
|
||||||
PlaneAdapter<std::vector<float>> ambientHeat;
|
Plane<float> ambientHeat;
|
||||||
PlaneAdapter<std::vector<unsigned char>> blockAir;
|
|
||||||
PlaneAdapter<std::vector<unsigned char>> blockAirh;
|
|
||||||
|
|
||||||
//Simulation Options
|
//Simulation Options
|
||||||
bool waterEEnabled = false;
|
bool waterEEnabled = false;
|
||||||
@ -104,8 +119,6 @@ public:
|
|||||||
int edgeMode = 0;
|
int edgeMode = 0;
|
||||||
bool wantAuthors = true;
|
bool wantAuthors = true;
|
||||||
|
|
||||||
MissingElements missingElements;
|
|
||||||
|
|
||||||
//Signs
|
//Signs
|
||||||
std::vector<sign> signs;
|
std::vector<sign> signs;
|
||||||
StkmData stkm;
|
StkmData stkm;
|
||||||
@ -119,12 +132,14 @@ public:
|
|||||||
|
|
||||||
int pmapbits = 8; // default to 8 bits for older saves
|
int pmapbits = 8; // default to 8 bits for older saves
|
||||||
|
|
||||||
GameSave(Vec2<int> newBlockSize);
|
GameSave(int width, int height);
|
||||||
GameSave(const std::vector<char> &data, bool newWantAuthors = true);
|
GameSave(const std::vector<char> &data, bool newWantAuthors = true);
|
||||||
void setSize(Vec2<int> newBlockSize);
|
void setSize(int width, int height);
|
||||||
// return value is [ fakeFromNewerVersion, gameData ]
|
// return value is [ fakeFromNewerVersion, gameData ]
|
||||||
std::pair<bool, std::vector<char>> Serialise() const;
|
std::pair<bool, std::vector<char>> Serialise() const;
|
||||||
void Transform(Mat2<int> transform, Vec2<int> nudge);
|
vector2d Translate(vector2d translate);
|
||||||
|
void Transform(matrix2d transform, vector2d translate);
|
||||||
|
void Transform(matrix2d transform, vector2d translate, vector2d translateReal, int newWidth, int newHeight);
|
||||||
|
|
||||||
void Expand(const std::vector<char> &data);
|
void Expand(const std::vector<char> &data);
|
||||||
|
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "User.h"
|
|
||||||
#include "ServerNotification.h"
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
struct LoginInfo
|
|
||||||
{
|
|
||||||
User user;
|
|
||||||
std::vector<ServerNotification> notifications;
|
|
||||||
};
|
|
231
src/client/MD5.cpp
Normal file
231
src/client/MD5.cpp
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
// based on public-domain code from Colin Plumb (1993)
|
||||||
|
#include "MD5.h"
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
static unsigned getu32(const unsigned char *addr)
|
||||||
|
{
|
||||||
|
return (((((unsigned long)addr[3] << 8) | addr[2]) << 8) | addr[1]) << 8 | addr[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void putu32(unsigned data, unsigned char *addr)
|
||||||
|
{
|
||||||
|
addr[0] = (unsigned char)data;
|
||||||
|
addr[1] = (unsigned char)(data >> 8);
|
||||||
|
addr[2] = (unsigned char)(data >> 16);
|
||||||
|
addr[3] = (unsigned char)(data >> 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
void md5_init(struct md5_context *ctx)
|
||||||
|
{
|
||||||
|
ctx->buf[0] = 0x67452301;
|
||||||
|
ctx->buf[1] = 0xefcdab89;
|
||||||
|
ctx->buf[2] = 0x98badcfe;
|
||||||
|
ctx->buf[3] = 0x10325476;
|
||||||
|
|
||||||
|
ctx->bits[0] = 0;
|
||||||
|
ctx->bits[1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void md5_update(struct md5_context *ctx, unsigned char const *buf, unsigned len)
|
||||||
|
{
|
||||||
|
unsigned t;
|
||||||
|
|
||||||
|
// update bit count
|
||||||
|
t = ctx->bits[0];
|
||||||
|
if ((ctx->bits[0] = (t + ((unsigned)len << 3)) & 0xffffffff) < t)
|
||||||
|
ctx->bits[1]++; // carry
|
||||||
|
ctx->bits[1] += len >> 29;
|
||||||
|
|
||||||
|
t = (t >> 3) & 0x3f;
|
||||||
|
|
||||||
|
// use leading data to top up the buffer
|
||||||
|
|
||||||
|
if (t)
|
||||||
|
{
|
||||||
|
unsigned char *p = ctx->in + t;
|
||||||
|
|
||||||
|
t = 64-t;
|
||||||
|
if (len < t)
|
||||||
|
{
|
||||||
|
memcpy(p, buf, len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(p, buf, t);
|
||||||
|
md5_transform(ctx->buf, ctx->in);
|
||||||
|
buf += t;
|
||||||
|
len -= t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// following 64-byte chunks
|
||||||
|
|
||||||
|
while (len >= 64)
|
||||||
|
{
|
||||||
|
memcpy(ctx->in, buf, 64);
|
||||||
|
md5_transform(ctx->buf, ctx->in);
|
||||||
|
buf += 64;
|
||||||
|
len -= 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
// save rest of bytes for later
|
||||||
|
|
||||||
|
memcpy(ctx->in, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void md5_final(unsigned char digest[16], struct md5_context *ctx)
|
||||||
|
{
|
||||||
|
unsigned count;
|
||||||
|
unsigned char *p;
|
||||||
|
|
||||||
|
// #bytes mod64
|
||||||
|
count = (ctx->bits[0] >> 3) & 0x3F;
|
||||||
|
|
||||||
|
// first char of padding = 0x80
|
||||||
|
p = ctx->in + count;
|
||||||
|
*p++ = 0x80;
|
||||||
|
|
||||||
|
// calculate # of bytes to pad
|
||||||
|
count = 64 - 1 - count;
|
||||||
|
|
||||||
|
// Pad out to 56 mod 64
|
||||||
|
if (count < 8)
|
||||||
|
{
|
||||||
|
// we need to finish a whole block before padding
|
||||||
|
memset(p, 0, count);
|
||||||
|
md5_transform(ctx->buf, ctx->in);
|
||||||
|
memset(ctx->in, 0, 56);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// just pad to 56 bytes
|
||||||
|
memset(p, 0, count-8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// append length & final transform
|
||||||
|
putu32(ctx->bits[0], ctx->in + 56);
|
||||||
|
putu32(ctx->bits[1], ctx->in + 60);
|
||||||
|
|
||||||
|
md5_transform(ctx->buf, ctx->in);
|
||||||
|
putu32(ctx->buf[0], digest);
|
||||||
|
putu32(ctx->buf[1], digest + 4);
|
||||||
|
putu32(ctx->buf[2], digest + 8);
|
||||||
|
putu32(ctx->buf[3], digest + 12);
|
||||||
|
memset(&ctx, 0, sizeof(ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define F1(x, y, z) (z ^ (x & (y ^ z)))
|
||||||
|
#define F2(x, y, z) F1(z, x, y)
|
||||||
|
#define F3(x, y, z) (x ^ y ^ z)
|
||||||
|
#define F4(x, y, z) (y ^ (x | ~z))
|
||||||
|
|
||||||
|
#define MD5STEP(f, w, x, y, z, data, s) \
|
||||||
|
( w += f(x, y, z) + data, w &= 0xffffffff, w = w<<s | w>>(32-s), w += x )
|
||||||
|
|
||||||
|
void md5_transform(unsigned buf[4], const unsigned char inraw[64])
|
||||||
|
{
|
||||||
|
unsigned a, b, c, d;
|
||||||
|
unsigned in[16];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 16; ++i)
|
||||||
|
in[i] = getu32 (inraw + 4 * i);
|
||||||
|
|
||||||
|
a = buf[0];
|
||||||
|
b = buf[1];
|
||||||
|
c = buf[2];
|
||||||
|
d = buf[3];
|
||||||
|
|
||||||
|
MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7);
|
||||||
|
MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
|
||||||
|
MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
|
||||||
|
MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
|
||||||
|
MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7);
|
||||||
|
MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
|
||||||
|
MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
|
||||||
|
MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
|
||||||
|
MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7);
|
||||||
|
MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
|
||||||
|
MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
|
||||||
|
MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
|
||||||
|
MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7);
|
||||||
|
MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
|
||||||
|
MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
|
||||||
|
MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
|
||||||
|
|
||||||
|
MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5);
|
||||||
|
MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9);
|
||||||
|
MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
|
||||||
|
MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
|
||||||
|
MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5);
|
||||||
|
MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9);
|
||||||
|
MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
|
||||||
|
MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
|
||||||
|
MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5);
|
||||||
|
MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9);
|
||||||
|
MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
|
||||||
|
MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
|
||||||
|
MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5);
|
||||||
|
MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9);
|
||||||
|
MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
|
||||||
|
MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
|
||||||
|
|
||||||
|
MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4);
|
||||||
|
MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
|
||||||
|
MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
|
||||||
|
MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
|
||||||
|
MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4);
|
||||||
|
MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
|
||||||
|
MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
|
||||||
|
MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
|
||||||
|
MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4);
|
||||||
|
MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
|
||||||
|
MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
|
||||||
|
MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
|
||||||
|
MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4);
|
||||||
|
MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
|
||||||
|
MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
|
||||||
|
MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
|
||||||
|
|
||||||
|
MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6);
|
||||||
|
MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
|
||||||
|
MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
|
||||||
|
MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
|
||||||
|
MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6);
|
||||||
|
MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
|
||||||
|
MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
|
||||||
|
MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
|
||||||
|
MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6);
|
||||||
|
MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
|
||||||
|
MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
|
||||||
|
MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
|
||||||
|
MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6);
|
||||||
|
MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
|
||||||
|
MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
|
||||||
|
MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
|
||||||
|
|
||||||
|
buf[0] += a;
|
||||||
|
buf[1] += b;
|
||||||
|
buf[2] += c;
|
||||||
|
buf[3] += d;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char hexChars[] = "0123456789abcdef";
|
||||||
|
void md5_ascii(char *result, unsigned char const *buf, unsigned len)
|
||||||
|
{
|
||||||
|
struct md5_context md5;
|
||||||
|
unsigned char hash[16];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (len==0)
|
||||||
|
len = strlen((char *)buf);
|
||||||
|
|
||||||
|
md5_init(&md5);
|
||||||
|
md5_update(&md5, buf, len);
|
||||||
|
md5_final(hash, &md5);
|
||||||
|
|
||||||
|
for (i=0; i<16; i++)
|
||||||
|
{
|
||||||
|
result[i*2] = hexChars[(hash[i]>>4)&0xF];
|
||||||
|
result[i*2+1] = hexChars[hash[i]&0x0F];
|
||||||
|
}
|
||||||
|
result[32] = 0;
|
||||||
|
}
|
15
src/client/MD5.h
Normal file
15
src/client/MD5.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
struct md5_context
|
||||||
|
{
|
||||||
|
unsigned buf[4];
|
||||||
|
unsigned bits[2];
|
||||||
|
unsigned char in[64];
|
||||||
|
};
|
||||||
|
|
||||||
|
void md5_init(struct md5_context *context);
|
||||||
|
void md5_update(struct md5_context *context, unsigned char const *buf, unsigned len);
|
||||||
|
void md5_final(unsigned char digest[16], struct md5_context *context);
|
||||||
|
void md5_transform(unsigned buf[4], const unsigned char in[64]);
|
||||||
|
|
||||||
|
void md5_ascii(char *result, unsigned char const *buf, unsigned len);
|
@ -2,7 +2,19 @@
|
|||||||
#include "GameSave.h"
|
#include "GameSave.h"
|
||||||
#include "common/platform/Platform.h"
|
#include "common/platform/Platform.h"
|
||||||
|
|
||||||
|
SaveFile::SaveFile(SaveFile & save):
|
||||||
|
gameSave(NULL),
|
||||||
|
filename(save.filename),
|
||||||
|
displayName(save.displayName),
|
||||||
|
loadingError(save.loadingError),
|
||||||
|
lazyLoad(save.lazyLoad)
|
||||||
|
{
|
||||||
|
if (save.gameSave)
|
||||||
|
gameSave = new GameSave(*save.gameSave);
|
||||||
|
}
|
||||||
|
|
||||||
SaveFile::SaveFile(ByteString filename, bool newLazyLoad):
|
SaveFile::SaveFile(ByteString filename, bool newLazyLoad):
|
||||||
|
gameSave(NULL),
|
||||||
filename(filename),
|
filename(filename),
|
||||||
displayName(filename.FromUtf8()),
|
displayName(filename.FromUtf8()),
|
||||||
loadingError(""),
|
loadingError(""),
|
||||||
@ -11,7 +23,7 @@ SaveFile::SaveFile(ByteString filename, bool newLazyLoad):
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const GameSave *SaveFile::LazyGetGameSave() // non-owning
|
GameSave * SaveFile::GetGameSave()
|
||||||
{
|
{
|
||||||
if (!gameSave && !loadingError.size() && lazyLoad)
|
if (!gameSave && !loadingError.size() && lazyLoad)
|
||||||
{
|
{
|
||||||
@ -20,7 +32,7 @@ const GameSave *SaveFile::LazyGetGameSave() // non-owning
|
|||||||
std::vector<char> data;
|
std::vector<char> data;
|
||||||
if (Platform::ReadFile(data, filename))
|
if (Platform::ReadFile(data, filename))
|
||||||
{
|
{
|
||||||
gameSave = std::make_unique<GameSave>(std::move(data));
|
gameSave = new GameSave(std::move(data));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -32,33 +44,24 @@ const GameSave *SaveFile::LazyGetGameSave() // non-owning
|
|||||||
loadingError = ByteString(e.what()).FromUtf8();
|
loadingError = ByteString(e.what()).FromUtf8();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return gameSave.get();
|
return gameSave;
|
||||||
}
|
|
||||||
|
|
||||||
const GameSave *SaveFile::GetGameSave() const
|
|
||||||
{
|
|
||||||
return gameSave.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<GameSave> SaveFile::TakeGameSave()
|
|
||||||
{
|
|
||||||
return std::move(gameSave);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveFile::LazyUnload()
|
void SaveFile::LazyUnload()
|
||||||
{
|
{
|
||||||
if (lazyLoad)
|
if (lazyLoad && gameSave)
|
||||||
{
|
{
|
||||||
gameSave.reset();
|
delete gameSave;
|
||||||
|
gameSave = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveFile::SetGameSave(std::unique_ptr<GameSave> newGameSave)
|
void SaveFile::SetGameSave(GameSave * save)
|
||||||
{
|
{
|
||||||
gameSave = std::move(newGameSave);
|
gameSave = save;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ByteString &SaveFile::GetName() const
|
ByteString SaveFile::GetName()
|
||||||
{
|
{
|
||||||
return filename;
|
return filename;
|
||||||
}
|
}
|
||||||
@ -68,7 +71,7 @@ void SaveFile::SetFileName(ByteString fileName)
|
|||||||
this->filename = fileName;
|
this->filename = fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
const String &SaveFile::GetDisplayName() const
|
String SaveFile::GetDisplayName()
|
||||||
{
|
{
|
||||||
return displayName;
|
return displayName;
|
||||||
}
|
}
|
||||||
@ -78,7 +81,7 @@ void SaveFile::SetDisplayName(String displayName)
|
|||||||
this->displayName = displayName;
|
this->displayName = displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
const String &SaveFile::GetError() const
|
String SaveFile::GetError()
|
||||||
{
|
{
|
||||||
return loadingError;
|
return loadingError;
|
||||||
}
|
}
|
||||||
@ -87,3 +90,10 @@ void SaveFile::SetLoadingError(String error)
|
|||||||
{
|
{
|
||||||
loadingError = error;
|
loadingError = error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SaveFile::~SaveFile() {
|
||||||
|
if (gameSave)
|
||||||
|
{
|
||||||
|
delete gameSave;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "common/String.h"
|
#include "common/String.h"
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
class GameSave;
|
class GameSave;
|
||||||
|
|
||||||
class SaveFile {
|
class SaveFile {
|
||||||
public:
|
public:
|
||||||
|
SaveFile(SaveFile & save);
|
||||||
SaveFile(ByteString filename, bool newLazyLoad = false);
|
SaveFile(ByteString filename, bool newLazyLoad = false);
|
||||||
|
|
||||||
const GameSave *LazyGetGameSave();
|
GameSave * GetGameSave();
|
||||||
const GameSave *GetGameSave() const;
|
void SetGameSave(GameSave * save);
|
||||||
std::unique_ptr<GameSave> TakeGameSave();
|
String GetDisplayName();
|
||||||
void SetGameSave(std::unique_ptr<GameSave> newSameSave);
|
|
||||||
const String &GetDisplayName() const;
|
|
||||||
void SetDisplayName(String displayName);
|
void SetDisplayName(String displayName);
|
||||||
const ByteString &GetName() const;
|
ByteString GetName();
|
||||||
void SetFileName(ByteString fileName);
|
void SetFileName(ByteString fileName);
|
||||||
const String &GetError() const;
|
String GetError();
|
||||||
void SetLoadingError(String error);
|
void SetLoadingError(String error);
|
||||||
|
|
||||||
void LazyUnload();
|
void LazyUnload();
|
||||||
|
|
||||||
|
virtual ~SaveFile();
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<GameSave> gameSave;
|
GameSave * gameSave;
|
||||||
ByteString filename;
|
ByteString filename;
|
||||||
String displayName;
|
String displayName;
|
||||||
String loadingError;
|
String loadingError;
|
||||||
|
@ -1,7 +1,31 @@
|
|||||||
#include "SaveInfo.h"
|
#include "SaveInfo.h"
|
||||||
#include "GameSave.h"
|
#include "GameSave.h"
|
||||||
|
|
||||||
SaveInfo::SaveInfo(int _id, time_t _createdDate, time_t _updatedDate, int _votesUp, int _votesDown, ByteString _userName, String _name):
|
SaveInfo::SaveInfo(SaveInfo & save):
|
||||||
|
id(save.id),
|
||||||
|
createdDate(save.createdDate),
|
||||||
|
updatedDate(save.updatedDate),
|
||||||
|
votesUp(save.votesUp),
|
||||||
|
votesDown(save.votesDown),
|
||||||
|
vote(save.vote),
|
||||||
|
Favourite(false),
|
||||||
|
Comments(save.Comments),
|
||||||
|
Views(save.Views),
|
||||||
|
Version(save.Version),
|
||||||
|
userName(save.userName),
|
||||||
|
name(save.name),
|
||||||
|
Description(save.Description),
|
||||||
|
Published(save.Published),
|
||||||
|
gameSave(NULL)
|
||||||
|
{
|
||||||
|
std::list<ByteString> tagsSorted = save.tags;
|
||||||
|
tagsSorted.sort();
|
||||||
|
tags = tagsSorted;
|
||||||
|
if (save.gameSave)
|
||||||
|
gameSave = new GameSave(*save.gameSave);
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveInfo::SaveInfo(int _id, int _createdDate, int _updatedDate, int _votesUp, int _votesDown, ByteString _userName, String _name):
|
||||||
id(_id),
|
id(_id),
|
||||||
createdDate(_createdDate),
|
createdDate(_createdDate),
|
||||||
updatedDate(_updatedDate),
|
updatedDate(_updatedDate),
|
||||||
@ -15,12 +39,14 @@ SaveInfo::SaveInfo(int _id, time_t _createdDate, time_t _updatedDate, int _votes
|
|||||||
userName(_userName),
|
userName(_userName),
|
||||||
name(_name),
|
name(_name),
|
||||||
Description(""),
|
Description(""),
|
||||||
Published(false)
|
Published(false),
|
||||||
|
tags(),
|
||||||
|
gameSave(NULL)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SaveInfo::SaveInfo(int _id, time_t _createdDate, time_t _updatedDate, int _votesUp, int _votesDown, int _vote, ByteString _userName, String _name, String description_, bool published_, std::list<ByteString> tags_):
|
SaveInfo::SaveInfo(int _id, int _createdDate, int _updatedDate, int _votesUp, int _votesDown, int _vote, ByteString _userName, String _name, String description_, bool published_, std::list<ByteString> tags_):
|
||||||
id(_id),
|
id(_id),
|
||||||
createdDate(_createdDate),
|
createdDate(_createdDate),
|
||||||
updatedDate(_updatedDate),
|
updatedDate(_updatedDate),
|
||||||
@ -34,18 +60,28 @@ SaveInfo::SaveInfo(int _id, time_t _createdDate, time_t _updatedDate, int _votes
|
|||||||
userName(_userName),
|
userName(_userName),
|
||||||
name(_name),
|
name(_name),
|
||||||
Description(description_),
|
Description(description_),
|
||||||
Published(published_)
|
Published(published_),
|
||||||
|
tags(),
|
||||||
|
gameSave(NULL)
|
||||||
{
|
{
|
||||||
std::list<ByteString> tagsSorted = tags_;
|
std::list<ByteString> tagsSorted = tags_;
|
||||||
tagsSorted.sort();
|
tagsSorted.sort();
|
||||||
tags=tagsSorted;
|
tags=tagsSorted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SaveInfo::~SaveInfo()
|
||||||
|
{
|
||||||
|
if(gameSave)
|
||||||
|
{
|
||||||
|
delete gameSave;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SaveInfo::SetName(String name)
|
void SaveInfo::SetName(String name)
|
||||||
{
|
{
|
||||||
this->name = name;
|
this->name = name;
|
||||||
}
|
}
|
||||||
const String &SaveInfo::GetName() const
|
String SaveInfo::GetName()
|
||||||
{
|
{
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
@ -54,7 +90,7 @@ void SaveInfo::SetDescription(String description)
|
|||||||
{
|
{
|
||||||
Description = description;
|
Description = description;
|
||||||
}
|
}
|
||||||
const String &SaveInfo::GetDescription() const
|
String SaveInfo::GetDescription()
|
||||||
{
|
{
|
||||||
return Description;
|
return Description;
|
||||||
}
|
}
|
||||||
@ -63,7 +99,7 @@ void SaveInfo::SetPublished(bool published)
|
|||||||
{
|
{
|
||||||
Published = published;
|
Published = published;
|
||||||
}
|
}
|
||||||
bool SaveInfo::GetPublished() const
|
bool SaveInfo::GetPublished()
|
||||||
{
|
{
|
||||||
return Published;
|
return Published;
|
||||||
}
|
}
|
||||||
@ -72,7 +108,7 @@ void SaveInfo::SetVote(int vote)
|
|||||||
{
|
{
|
||||||
this->vote = vote;
|
this->vote = vote;
|
||||||
}
|
}
|
||||||
int SaveInfo::GetVote() const
|
int SaveInfo::GetVote()
|
||||||
{
|
{
|
||||||
return vote;
|
return vote;
|
||||||
}
|
}
|
||||||
@ -82,7 +118,7 @@ void SaveInfo::SetUserName(ByteString userName)
|
|||||||
this->userName = userName;
|
this->userName = userName;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ByteString &SaveInfo::GetUserName() const
|
ByteString SaveInfo::GetUserName()
|
||||||
{
|
{
|
||||||
return userName;
|
return userName;
|
||||||
}
|
}
|
||||||
@ -91,7 +127,7 @@ void SaveInfo::SetID(int id)
|
|||||||
{
|
{
|
||||||
this->id = id;
|
this->id = id;
|
||||||
}
|
}
|
||||||
int SaveInfo::GetID() const
|
int SaveInfo::GetID()
|
||||||
{
|
{
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@ -100,7 +136,7 @@ void SaveInfo::SetVotesUp(int votesUp)
|
|||||||
{
|
{
|
||||||
this->votesUp = votesUp;
|
this->votesUp = votesUp;
|
||||||
}
|
}
|
||||||
int SaveInfo::GetVotesUp() const
|
int SaveInfo::GetVotesUp()
|
||||||
{
|
{
|
||||||
return votesUp;
|
return votesUp;
|
||||||
}
|
}
|
||||||
@ -109,7 +145,7 @@ void SaveInfo::SetVotesDown(int votesDown)
|
|||||||
{
|
{
|
||||||
this->votesDown = votesDown;
|
this->votesDown = votesDown;
|
||||||
}
|
}
|
||||||
int SaveInfo::GetVotesDown() const
|
int SaveInfo::GetVotesDown()
|
||||||
{
|
{
|
||||||
return votesDown;
|
return votesDown;
|
||||||
}
|
}
|
||||||
@ -118,7 +154,7 @@ void SaveInfo::SetVersion(int version)
|
|||||||
{
|
{
|
||||||
this->Version = version;
|
this->Version = version;
|
||||||
}
|
}
|
||||||
int SaveInfo::GetVersion() const
|
int SaveInfo::GetVersion()
|
||||||
{
|
{
|
||||||
return Version;
|
return Version;
|
||||||
}
|
}
|
||||||
@ -130,33 +166,18 @@ void SaveInfo::SetTags(std::list<ByteString> tags)
|
|||||||
this->tags=tagsSorted;
|
this->tags=tagsSorted;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::list<ByteString> SaveInfo::GetTags() const
|
std::list<ByteString> SaveInfo::GetTags()
|
||||||
{
|
{
|
||||||
return tags;
|
return tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
const GameSave *SaveInfo::GetGameSave() const
|
GameSave * SaveInfo::GetGameSave()
|
||||||
{
|
{
|
||||||
return gameSave.get();
|
return gameSave;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<GameSave> SaveInfo::TakeGameSave()
|
void SaveInfo::SetGameSave(GameSave * saveGame)
|
||||||
{
|
{
|
||||||
return std::move(gameSave);
|
delete gameSave;
|
||||||
}
|
gameSave = saveGame;
|
||||||
|
|
||||||
void SaveInfo::SetGameSave(std::unique_ptr<GameSave> newGameSave)
|
|
||||||
{
|
|
||||||
gameSave = std::move(newGameSave);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<SaveInfo> SaveInfo::CloneInfo() const
|
|
||||||
{
|
|
||||||
auto clone = std::make_unique<SaveInfo>(id, createdDate, updatedDate, votesUp, votesDown, vote, userName, name, Description, Published, tags);
|
|
||||||
clone->Favourite = false;
|
|
||||||
clone->Comments = Comments;
|
|
||||||
clone->Views = Views;
|
|
||||||
clone->Version = Version;
|
|
||||||
clone->tags.sort();
|
|
||||||
return clone;
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "common/String.h"
|
#include "common/String.h"
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
|
||||||
#include <ctime>
|
#ifdef GetUserName
|
||||||
|
# undef GetUserName // dammit windows
|
||||||
|
#endif
|
||||||
|
|
||||||
class GameSave;
|
class GameSave;
|
||||||
|
|
||||||
@ -11,8 +13,8 @@ class SaveInfo
|
|||||||
private:
|
private:
|
||||||
public:
|
public:
|
||||||
int id;
|
int id;
|
||||||
time_t createdDate;
|
int createdDate;
|
||||||
time_t updatedDate;
|
int updatedDate;
|
||||||
int votesUp, votesDown;
|
int votesUp, votesDown;
|
||||||
int vote;
|
int vote;
|
||||||
bool Favourite;
|
bool Favourite;
|
||||||
@ -27,45 +29,46 @@ public:
|
|||||||
bool Published;
|
bool Published;
|
||||||
|
|
||||||
std::list<ByteString> tags;
|
std::list<ByteString> tags;
|
||||||
std::unique_ptr<GameSave> gameSave;
|
GameSave * gameSave;
|
||||||
|
|
||||||
SaveInfo(int _id, time_t _createdDate, time_t _updatedDate, int _votesUp, int _votesDown, ByteString _userName, String _name);
|
SaveInfo(SaveInfo & save);
|
||||||
|
|
||||||
SaveInfo(int _id, time_t _createdDate, time_t _updatedDate, int _votesUp, int _votesDown, int _vote, ByteString _userName, String _name, String description_, bool published_, std::list<ByteString> tags);
|
SaveInfo(int _id, int _createdDate, int _updatedDate, int _votesUp, int _votesDown, ByteString _userName, String _name);
|
||||||
|
|
||||||
|
SaveInfo(int _id, int _createdDate, int _updatedDate, int _votesUp, int _votesDown, int _vote, ByteString _userName, String _name, String description_, bool published_, std::list<ByteString> tags);
|
||||||
|
|
||||||
|
~SaveInfo();
|
||||||
|
|
||||||
void SetName(String name);
|
void SetName(String name);
|
||||||
const String &GetName() const;
|
String GetName();
|
||||||
|
|
||||||
void SetDescription(String description);
|
void SetDescription(String description);
|
||||||
const String &GetDescription() const;
|
String GetDescription();
|
||||||
|
|
||||||
void SetPublished(bool published);
|
void SetPublished(bool published);
|
||||||
bool GetPublished() const;
|
bool GetPublished();
|
||||||
|
|
||||||
void SetUserName(ByteString userName);
|
void SetUserName(ByteString userName);
|
||||||
const ByteString &GetUserName() const;
|
ByteString GetUserName();
|
||||||
|
|
||||||
void SetID(int id);
|
void SetID(int id);
|
||||||
int GetID() const;
|
int GetID();
|
||||||
|
|
||||||
void SetVote(int vote);
|
void SetVote(int vote);
|
||||||
int GetVote() const;
|
int GetVote();
|
||||||
|
|
||||||
void SetVotesUp(int votesUp);
|
void SetVotesUp(int votesUp);
|
||||||
int GetVotesUp() const;
|
int GetVotesUp();
|
||||||
|
|
||||||
void SetVotesDown(int votesDown);
|
void SetVotesDown(int votesDown);
|
||||||
int GetVotesDown() const;
|
int GetVotesDown();
|
||||||
|
|
||||||
void SetVersion(int version);
|
void SetVersion(int version);
|
||||||
int GetVersion() const;
|
int GetVersion();
|
||||||
|
|
||||||
void SetTags(std::list<ByteString> tags);
|
void SetTags(std::list<ByteString> tags);
|
||||||
std::list<ByteString> GetTags() const;
|
std::list<ByteString> GetTags();
|
||||||
|
|
||||||
const GameSave *GetGameSave() const;
|
GameSave * GetGameSave();
|
||||||
std::unique_ptr<GameSave> TakeGameSave();
|
void SetGameSave(GameSave * gameSave);
|
||||||
void SetGameSave(std::unique_ptr<GameSave> newGameSave);
|
|
||||||
|
|
||||||
std::unique_ptr<SaveInfo> CloneInfo() const;
|
|
||||||
};
|
};
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
enum Category
|
|
||||||
{
|
|
||||||
categoryNone,
|
|
||||||
categoryMyOwn,
|
|
||||||
categoryFavourites,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum Sort
|
|
||||||
{
|
|
||||||
sortByVotes,
|
|
||||||
sortByDate,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum Period
|
|
||||||
{
|
|
||||||
allSaves,
|
|
||||||
todaySaves,
|
|
||||||
weekSaves,
|
|
||||||
monthSaves,
|
|
||||||
yearSaves,
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "common/String.h"
|
|
||||||
|
|
||||||
struct ServerNotification
|
|
||||||
{
|
|
||||||
String text;
|
|
||||||
ByteString link;
|
|
||||||
};
|
|
@ -1,29 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "common/String.h"
|
|
||||||
#include "ServerNotification.h"
|
|
||||||
#include <vector>
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
struct UpdateInfo
|
|
||||||
{
|
|
||||||
enum Channel
|
|
||||||
{
|
|
||||||
channelStable,
|
|
||||||
channelBeta,
|
|
||||||
channelSnapshot,
|
|
||||||
};
|
|
||||||
Channel channel;
|
|
||||||
ByteString file;
|
|
||||||
String changeLog;
|
|
||||||
int major = 0;
|
|
||||||
int minor = 0;
|
|
||||||
int build = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct StartupInfo
|
|
||||||
{
|
|
||||||
bool sessionGood = false;
|
|
||||||
String messageOfTheDay;
|
|
||||||
std::vector<ServerNotification> notifications;
|
|
||||||
std::optional<UpdateInfo> updateInfo;
|
|
||||||
};
|
|
@ -29,7 +29,7 @@ ThumbnailRendererTask::~ThumbnailRendererTask()
|
|||||||
|
|
||||||
bool ThumbnailRendererTask::doWork()
|
bool ThumbnailRendererTask::doWork()
|
||||||
{
|
{
|
||||||
thumbnail = SaveRenderer::Ref().Render(save.get(), decorations, fire);
|
thumbnail = std::unique_ptr<VideoBuffer>(SaveRenderer::Ref().Render(save.get(), decorations, fire));
|
||||||
if (thumbnail)
|
if (thumbnail)
|
||||||
{
|
{
|
||||||
thumbnail->ResizeToFit(size, true);
|
thumbnail->ResizeToFit(size, true);
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
#include "User.h"
|
|
||||||
|
|
||||||
static const std::vector<std::pair<User::Elevation, ByteString>> elevationStrings = {
|
|
||||||
{ User::ElevationAdmin , "Admin" },
|
|
||||||
{ User::ElevationMod , "Mod" },
|
|
||||||
{ User::ElevationHalfMod, "HalfMod" },
|
|
||||||
{ User::ElevationNone , "None" },
|
|
||||||
};
|
|
||||||
|
|
||||||
User::Elevation User::ElevationFromString(ByteString str)
|
|
||||||
{
|
|
||||||
auto it = std::find_if(elevationStrings.begin(), elevationStrings.end(), [&str](auto &item) {
|
|
||||||
return item.second == str;
|
|
||||||
});
|
|
||||||
if (it != elevationStrings.end())
|
|
||||||
{
|
|
||||||
return it->first;
|
|
||||||
}
|
|
||||||
return ElevationNone;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteString User::ElevationToString(Elevation elevation)
|
|
||||||
{
|
|
||||||
auto it = std::find_if(elevationStrings.begin(), elevationStrings.end(), [elevation](auto &item) {
|
|
||||||
return item.first == elevation;
|
|
||||||
});
|
|
||||||
if (it != elevationStrings.end())
|
|
||||||
{
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
return "None";
|
|
||||||
}
|
|
@ -7,14 +7,8 @@ class User
|
|||||||
public:
|
public:
|
||||||
enum Elevation
|
enum Elevation
|
||||||
{
|
{
|
||||||
ElevationNone,
|
ElevationAdmin, ElevationModerator, ElevationNone
|
||||||
ElevationHalfMod,
|
|
||||||
ElevationMod,
|
|
||||||
ElevationAdmin,
|
|
||||||
};
|
};
|
||||||
static Elevation ElevationFromString(ByteString str);
|
|
||||||
static ByteString ElevationToString(Elevation elevation);
|
|
||||||
|
|
||||||
int UserID;
|
int UserID;
|
||||||
ByteString Username;
|
ByteString Username;
|
||||||
ByteString SessionID;
|
ByteString SessionID;
|
||||||
|
@ -1,36 +1,39 @@
|
|||||||
#include "APIRequest.h"
|
#include "APIRequest.h"
|
||||||
|
|
||||||
#include "client/Client.h"
|
#include "client/Client.h"
|
||||||
|
|
||||||
namespace http
|
namespace http
|
||||||
{
|
{
|
||||||
APIRequest::APIRequest(ByteString url, AuthMode authMode, bool newCheckStatus) : Request(url), checkStatus(newCheckStatus)
|
APIRequest::APIRequest(ByteString url) : Request(url)
|
||||||
{
|
{
|
||||||
auto user = Client::Ref().GetAuthUser();
|
User user = Client::Ref().GetAuthUser();
|
||||||
if (authMode == authRequire && !user.UserID)
|
AuthHeaders(ByteString::Build(user.UserID), user.SessionID);
|
||||||
{
|
|
||||||
FailEarly("Not authenticated");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (authMode != authOmit && user.UserID)
|
|
||||||
{
|
|
||||||
AuthHeaders(ByteString::Build(user.UserID), user.SessionID);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::Value APIRequest::Finish()
|
APIRequest::~APIRequest()
|
||||||
{
|
{
|
||||||
auto [ status, data ] = Request::Finish();
|
}
|
||||||
ParseResponse(data, status, checkStatus ? responseJson : responseData);
|
|
||||||
Json::Value document;
|
APIRequest::Result APIRequest::Finish()
|
||||||
|
{
|
||||||
|
Result result;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::istringstream ss(data);
|
ByteString data;
|
||||||
ss >> document;
|
std::tie(result.status, data) = Request::Finish();
|
||||||
|
Client::Ref().ParseServerReturn(data, result.status, true);
|
||||||
|
if (result.status == 200 && data.size())
|
||||||
|
{
|
||||||
|
std::istringstream dataStream(data);
|
||||||
|
Json::Value objDocument;
|
||||||
|
dataStream >> objDocument;
|
||||||
|
result.document = std::unique_ptr<Json::Value>(new Json::Value(objDocument));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (const std::exception &ex)
|
catch (std::exception & e)
|
||||||
{
|
{
|
||||||
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
|
||||||
}
|
}
|
||||||
return document;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,22 +2,23 @@
|
|||||||
#include "Request.h"
|
#include "Request.h"
|
||||||
#include "common/String.h"
|
#include "common/String.h"
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
namespace http
|
namespace http
|
||||||
{
|
{
|
||||||
class APIRequest : public Request
|
class APIRequest : public Request
|
||||||
{
|
{
|
||||||
bool checkStatus;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum AuthMode
|
struct Result
|
||||||
{
|
{
|
||||||
authRequire,
|
int status;
|
||||||
authUse,
|
std::unique_ptr<Json::Value> document;
|
||||||
authOmit,
|
|
||||||
};
|
};
|
||||||
APIRequest(ByteString url, AuthMode authMode, bool newCheckStatus);
|
|
||||||
|
|
||||||
Json::Value Finish();
|
APIRequest(ByteString url);
|
||||||
|
virtual ~APIRequest();
|
||||||
|
|
||||||
|
Result Finish();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
#include "AddCommentRequest.h"
|
|
||||||
#include "client/Client.h"
|
|
||||||
#include "Config.h"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
AddCommentRequest::AddCommentRequest(int saveID, String comment) :
|
|
||||||
APIRequest(ByteString::Build(SCHEME, SERVER, "/Browse/Comments.json?ID=", saveID), authRequire, true)
|
|
||||||
{
|
|
||||||
auto user = Client::Ref().GetAuthUser();
|
|
||||||
AddPostData(FormData{
|
|
||||||
{ "Comment", comment.ToUtf8() },
|
|
||||||
{ "Key", user.SessionKey },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddCommentRequest::Finish()
|
|
||||||
{
|
|
||||||
APIRequest::Finish();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "APIRequest.h"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
class AddCommentRequest : public APIRequest
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
AddCommentRequest(int saveID, String comment);
|
|
||||||
|
|
||||||
void Finish();
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
#include "AddTagRequest.h"
|
|
||||||
#include "client/Client.h"
|
|
||||||
#include "Config.h"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
AddTagRequest::AddTagRequest(int saveID, ByteString tag) :
|
|
||||||
APIRequest(ByteString::Build(SCHEME, SERVER, "/Browse/EditTag.json?Op=add&ID=", saveID, "&Tag=", tag, "&Key=", Client::Ref().GetAuthUser().SessionKey), authRequire, true)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::list<ByteString> AddTagRequest::Finish()
|
|
||||||
{
|
|
||||||
auto result = APIRequest::Finish();
|
|
||||||
std::list<ByteString> tags;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for (auto &tag : result["Tags"])
|
|
||||||
{
|
|
||||||
tags.push_back(tag.asString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception &ex)
|
|
||||||
{
|
|
||||||
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
|
||||||
}
|
|
||||||
return tags;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "APIRequest.h"
|
|
||||||
#include <list>
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
class AddTagRequest : public APIRequest
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
AddTagRequest(int saveID, ByteString tag);
|
|
||||||
|
|
||||||
std::list<ByteString> Finish();
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
#include "DeleteSaveRequest.h"
|
|
||||||
#include "client/Client.h"
|
|
||||||
#include "Config.h"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
DeleteSaveRequest::DeleteSaveRequest(int saveID) :
|
|
||||||
APIRequest(ByteString::Build(SCHEME, SERVER, "/Browse/Delete.json?ID=", saveID, "&Mode=Delete&Key=", Client::Ref().GetAuthUser().SessionKey), authRequire, true)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeleteSaveRequest::Finish()
|
|
||||||
{
|
|
||||||
APIRequest::Finish();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "APIRequest.h"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
class DeleteSaveRequest : public APIRequest
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DeleteSaveRequest(int saveID);
|
|
||||||
|
|
||||||
void Finish();
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
#include "ExecVoteRequest.h"
|
|
||||||
#include "client/Client.h"
|
|
||||||
#include "Config.h"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
ExecVoteRequest::ExecVoteRequest(int saveID, int newDirection) :
|
|
||||||
APIRequest(ByteString::Build(SCHEME, SERVER, "/Vote.api"), authRequire, false),
|
|
||||||
direction(newDirection)
|
|
||||||
{
|
|
||||||
AddPostData(FormData{
|
|
||||||
{ "ID", ByteString::Build(saveID) },
|
|
||||||
{ "Action", direction ? (direction == 1 ? "Up" : "Down") : "Reset" },
|
|
||||||
{ "Key", Client::Ref().GetAuthUser().SessionKey },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void ExecVoteRequest::Finish()
|
|
||||||
{
|
|
||||||
auto [ status, data ] = Request::Finish();
|
|
||||||
ParseResponse(data, status, responseOk);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "APIRequest.h"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
class ExecVoteRequest : public APIRequest
|
|
||||||
{
|
|
||||||
int direction;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ExecVoteRequest(int saveID, int newDirection);
|
|
||||||
|
|
||||||
void Finish();
|
|
||||||
|
|
||||||
int Direction() const
|
|
||||||
{
|
|
||||||
return direction;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
#include "FavouriteSaveRequest.h"
|
|
||||||
#include "client/Client.h"
|
|
||||||
#include "Config.h"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
static ByteString Url(int saveID, bool favourite)
|
|
||||||
{
|
|
||||||
ByteStringBuilder builder;
|
|
||||||
builder << SCHEME << SERVER << "/Browse/Favourite.json?ID=" << saveID << "&Key=" << Client::Ref().GetAuthUser().SessionKey;
|
|
||||||
if (!favourite)
|
|
||||||
{
|
|
||||||
builder << "&Mode=Remove";
|
|
||||||
}
|
|
||||||
return builder.Build();
|
|
||||||
}
|
|
||||||
|
|
||||||
FavouriteSaveRequest::FavouriteSaveRequest(int saveID, bool newFavourite) :
|
|
||||||
APIRequest(Url(saveID, newFavourite), authRequire, true),
|
|
||||||
favourite(newFavourite)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void FavouriteSaveRequest::Finish()
|
|
||||||
{
|
|
||||||
APIRequest::Finish();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "APIRequest.h"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
class FavouriteSaveRequest : public APIRequest
|
|
||||||
{
|
|
||||||
bool favourite;
|
|
||||||
|
|
||||||
public:
|
|
||||||
FavouriteSaveRequest(int saveID, bool newFavourite);
|
|
||||||
|
|
||||||
void Finish();
|
|
||||||
|
|
||||||
bool Favourite() const
|
|
||||||
{
|
|
||||||
return favourite;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
#include "GetCommentsRequest.h"
|
|
||||||
#include "client/Client.h"
|
|
||||||
#include "Config.h"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
GetCommentsRequest::GetCommentsRequest(int saveID, int start, int count) :
|
|
||||||
APIRequest(ByteString::Build(SCHEME, SERVER, "/Browse/Comments.json?ID=", saveID, "&Start=", start, "&Count=", count), authOmit, false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Comment> GetCommentsRequest::Finish()
|
|
||||||
{
|
|
||||||
auto result = APIRequest::Finish();
|
|
||||||
std::vector<Comment> comments;
|
|
||||||
auto user = Client::Ref().GetAuthUser();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for (auto &comment : result)
|
|
||||||
{
|
|
||||||
comments.push_back({
|
|
||||||
comment["Username"].asString(),
|
|
||||||
User::ElevationFromString(comment["Elevation"].asString()),
|
|
||||||
comment["Username"].asString() == user.Username,
|
|
||||||
comment["IsBanned"].asBool(),
|
|
||||||
ByteString(comment["Text"].asString()).FromUtf8(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception &ex)
|
|
||||||
{
|
|
||||||
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
|
||||||
}
|
|
||||||
return comments;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "APIRequest.h"
|
|
||||||
#include "client/Comment.h"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
class GetCommentsRequest : public APIRequest
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
GetCommentsRequest(int saveID, int start, int count);
|
|
||||||
|
|
||||||
std::vector<Comment> Finish();
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
#include "GetSaveDataRequest.h"
|
|
||||||
#include "Config.h"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
static ByteString Url(int saveID, int saveDate)
|
|
||||||
{
|
|
||||||
ByteStringBuilder builder;
|
|
||||||
builder << STATICSCHEME << STATICSERVER << "/" << saveID;
|
|
||||||
if (saveDate)
|
|
||||||
{
|
|
||||||
builder << "_" << saveDate;
|
|
||||||
}
|
|
||||||
builder << ".cps";
|
|
||||||
return builder.Build();
|
|
||||||
}
|
|
||||||
|
|
||||||
GetSaveDataRequest::GetSaveDataRequest(int saveID, int saveDate) : Request(Url(saveID, saveDate))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<char> GetSaveDataRequest::Finish()
|
|
||||||
{
|
|
||||||
auto [ status, data ] = Request::Finish();
|
|
||||||
ParseResponse(data, status, responseData);
|
|
||||||
return std::vector<char>(data.begin(), data.end());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "Request.h"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
class GetSaveDataRequest : public Request
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
GetSaveDataRequest(int saveID, int saveDate);
|
|
||||||
|
|
||||||
std::vector<char> Finish();
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
#include "GetSaveRequest.h"
|
|
||||||
#include "client/Client.h"
|
|
||||||
#include "client/SaveInfo.h"
|
|
||||||
#include "client/GameSave.h"
|
|
||||||
#include "Config.h"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
static ByteString Url(int saveID, int saveDate)
|
|
||||||
{
|
|
||||||
ByteStringBuilder builder;
|
|
||||||
builder << SCHEME << SERVER << "/Browse/View.json?ID=" << saveID;
|
|
||||||
if (saveDate)
|
|
||||||
{
|
|
||||||
builder << "&Date=" << saveDate;
|
|
||||||
}
|
|
||||||
return builder.Build();
|
|
||||||
}
|
|
||||||
|
|
||||||
GetSaveRequest::GetSaveRequest(int saveID, int saveDate) : Request(Url(saveID, saveDate))
|
|
||||||
{
|
|
||||||
auto user = Client::Ref().GetAuthUser();
|
|
||||||
if (user.UserID)
|
|
||||||
{
|
|
||||||
// This is needed so we know how we rated this save.
|
|
||||||
AuthHeaders(ByteString::Build(user.UserID), user.SessionID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<SaveInfo> GetSaveRequest::Finish()
|
|
||||||
{
|
|
||||||
auto [ status, data ] = Request::Finish();
|
|
||||||
ParseResponse(data, status, responseData);
|
|
||||||
std::unique_ptr<SaveInfo> saveInfo;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Json::Value document;
|
|
||||||
std::istringstream ss(data);
|
|
||||||
ss >> document;
|
|
||||||
std::list<ByteString> tags;
|
|
||||||
for (auto &tag : document["Tags"])
|
|
||||||
{
|
|
||||||
tags.push_back(tag.asString());
|
|
||||||
}
|
|
||||||
saveInfo = std::make_unique<SaveInfo>(
|
|
||||||
document["ID"].asInt(),
|
|
||||||
document["DateCreated"].asInt64(),
|
|
||||||
document["Date"].asInt64(),
|
|
||||||
document["ScoreUp"].asInt(),
|
|
||||||
document["ScoreDown"].asInt(),
|
|
||||||
document["ScoreMine"].asInt(),
|
|
||||||
document["Username"].asString(),
|
|
||||||
ByteString(document["Name"].asString()).FromUtf8(),
|
|
||||||
ByteString(document["Description"].asString()).FromUtf8(),
|
|
||||||
document["Published"].asBool(),
|
|
||||||
tags
|
|
||||||
);
|
|
||||||
saveInfo->Comments = document["Comments"].asInt();
|
|
||||||
saveInfo->Favourite = document["Favourite"].asBool();
|
|
||||||
saveInfo->Views = document["Views"].asInt();
|
|
||||||
saveInfo->Version = document["Version"].asInt();
|
|
||||||
}
|
|
||||||
catch (const std::exception &ex)
|
|
||||||
{
|
|
||||||
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
|
||||||
}
|
|
||||||
return saveInfo;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "Request.h"
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
class SaveInfo;
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
class GetSaveRequest : public Request
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
GetSaveRequest(int saveID, int saveDate);
|
|
||||||
|
|
||||||
std::unique_ptr<SaveInfo> Finish();
|
|
||||||
};
|
|
||||||
}
|
|
@ -5,18 +5,22 @@
|
|||||||
namespace http
|
namespace http
|
||||||
{
|
{
|
||||||
GetUserInfoRequest::GetUserInfoRequest(ByteString username) :
|
GetUserInfoRequest::GetUserInfoRequest(ByteString username) :
|
||||||
APIRequest(ByteString::Build(SCHEME, SERVER, "/User.json?Name=", username), authOmit, false)
|
APIRequest(ByteString::Build(SCHEME, SERVER, "/User.json?Name=", username))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
UserInfo GetUserInfoRequest::Finish()
|
GetUserInfoRequest::~GetUserInfoRequest()
|
||||||
{
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<UserInfo> GetUserInfoRequest::Finish()
|
||||||
|
{
|
||||||
|
std::unique_ptr<UserInfo> user_info;
|
||||||
auto result = APIRequest::Finish();
|
auto result = APIRequest::Finish();
|
||||||
UserInfo userInfo;
|
if (result.document)
|
||||||
try
|
|
||||||
{
|
{
|
||||||
auto &user = result["User"];
|
auto &user = (*result.document)["User"];
|
||||||
userInfo = UserInfo(
|
user_info = std::unique_ptr<UserInfo>(new UserInfo(
|
||||||
user["ID"].asInt(),
|
user["ID"].asInt(),
|
||||||
user["Age"].asInt(),
|
user["Age"].asInt(),
|
||||||
user["Username"].asString(),
|
user["Username"].asString(),
|
||||||
@ -29,13 +33,9 @@ namespace http
|
|||||||
user["Forum"]["Topics"].asInt(),
|
user["Forum"]["Topics"].asInt(),
|
||||||
user["Forum"]["Replies"].asInt(),
|
user["Forum"]["Replies"].asInt(),
|
||||||
user["Forum"]["Reputation"].asInt()
|
user["Forum"]["Reputation"].asInt()
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
catch (const std::exception &ex)
|
return user_info;
|
||||||
{
|
|
||||||
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
|
||||||
}
|
|
||||||
return userInfo;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "APIRequest.h"
|
#include "APIRequest.h"
|
||||||
#include "client/UserInfo.h"
|
|
||||||
|
class UserInfo;
|
||||||
|
|
||||||
namespace http
|
namespace http
|
||||||
{
|
{
|
||||||
@ -8,7 +9,8 @@ namespace http
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GetUserInfoRequest(ByteString username);
|
GetUserInfoRequest(ByteString username);
|
||||||
|
virtual ~GetUserInfoRequest();
|
||||||
|
|
||||||
UserInfo Finish();
|
std::unique_ptr<UserInfo> Finish();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,32 @@
|
|||||||
#include "ImageRequest.h"
|
#include "ImageRequest.h"
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
#include "client/Client.h"
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
namespace http
|
namespace http
|
||||||
{
|
{
|
||||||
ImageRequest::ImageRequest(ByteString url, Vec2<int> newRequestedSize) : Request(url), requestedSize(newRequestedSize)
|
ImageRequest::ImageRequest(ByteString url, Vec2<int> size):
|
||||||
{
|
Request(std::move(url)),
|
||||||
}
|
size(size)
|
||||||
|
{}
|
||||||
|
|
||||||
|
ImageRequest::~ImageRequest()
|
||||||
|
{}
|
||||||
|
|
||||||
std::unique_ptr<VideoBuffer> ImageRequest::Finish()
|
std::unique_ptr<VideoBuffer> ImageRequest::Finish()
|
||||||
{
|
{
|
||||||
auto [ status, data ] = Request::Finish();
|
auto [ status, data ] = Request::Finish();
|
||||||
ParseResponse(data, status, responseData);
|
(void)status; // We don't use this for anything, not ideal >_>
|
||||||
auto vb = VideoBuffer::FromPNG(std::vector<char>(data.begin(), data.end()));
|
std::unique_ptr<VideoBuffer> vb;
|
||||||
if (vb)
|
if (data.size())
|
||||||
{
|
{
|
||||||
vb->Resize(requestedSize, true);
|
vb = VideoBuffer::FromPNG(std::vector<char>(data.begin(), data.end()));
|
||||||
}
|
if (vb)
|
||||||
else
|
vb->Resize(size, true);
|
||||||
{
|
else
|
||||||
vb = std::make_unique<VideoBuffer>(Vec2(15, 16));
|
{
|
||||||
vb->BlendChar(Vec2(2, 4), 0xE06E, 0xFFFFFF_rgb .WithAlpha(0xFF));
|
vb = std::make_unique<VideoBuffer>(Vec2(15, 16));
|
||||||
|
vb->BlendChar(Vec2(2, 4), 0xE06E, 0xFFFFFF_rgb .WithAlpha(0xFF));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return vb;
|
return vb;
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "common/String.h"
|
#include "common/String.h"
|
||||||
#include "common/Vec2.h"
|
#include "common/Vec2.h"
|
||||||
#include "Request.h"
|
#include "Request.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
class VideoBuffer;
|
class VideoBuffer;
|
||||||
@ -10,10 +11,11 @@ namespace http
|
|||||||
{
|
{
|
||||||
class ImageRequest : public Request
|
class ImageRequest : public Request
|
||||||
{
|
{
|
||||||
Vec2<int> requestedSize;
|
Vec2<int> size;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ImageRequest(ByteString url, Vec2<int> newRequestedSize);
|
ImageRequest(ByteString url, Vec2<int> size);
|
||||||
|
virtual ~ImageRequest();
|
||||||
|
|
||||||
std::unique_ptr<VideoBuffer> Finish();
|
std::unique_ptr<VideoBuffer> Finish();
|
||||||
};
|
};
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
#include "LoginRequest.h"
|
|
||||||
#include "Config.h"
|
|
||||||
#include "client/Client.h"
|
|
||||||
#include <json/json.h>
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
LoginRequest::LoginRequest(ByteString username, ByteString password) : Request(ByteString::Build("https://", SERVER, "/Login.json"))
|
|
||||||
{
|
|
||||||
AddPostData(FormData{
|
|
||||||
{ "name", username },
|
|
||||||
{ "pass", password },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
LoginInfo LoginRequest::Finish()
|
|
||||||
{
|
|
||||||
auto [ status, data ] = Request::Finish();
|
|
||||||
ParseResponse(data, status, responseJson);
|
|
||||||
LoginInfo loginInfo = { { 0, "" }, {} };
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Json::Value document;
|
|
||||||
std::istringstream ss(data);
|
|
||||||
ss >> document;
|
|
||||||
loginInfo.user.Username = document["Username"].asString();
|
|
||||||
loginInfo.user.UserID = document["UserID"].asInt();
|
|
||||||
loginInfo.user.SessionID = document["SessionID"].asString();
|
|
||||||
loginInfo.user.SessionKey = document["SessionKey"].asString();
|
|
||||||
loginInfo.user.UserElevation = User::ElevationFromString(document["Elevation"].asString());
|
|
||||||
for (auto &item : document["Notifications"])
|
|
||||||
{
|
|
||||||
loginInfo.notifications.push_back({
|
|
||||||
ByteString(item["Text"].asString()).FromUtf8(),
|
|
||||||
item["Link"].asString(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception &ex)
|
|
||||||
{
|
|
||||||
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
|
||||||
}
|
|
||||||
return loginInfo;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "Request.h"
|
|
||||||
#include "client/LoginInfo.h"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
class LoginRequest : public Request
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
LoginRequest(ByteString username, ByteString password);
|
|
||||||
|
|
||||||
LoginInfo Finish();
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
#include "LogoutRequest.h"
|
|
||||||
#include "client/Client.h"
|
|
||||||
#include "Config.h"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
LogoutRequest::LogoutRequest() :
|
|
||||||
APIRequest(ByteString::Build(SCHEME, SERVER, "/Logout.json?Key=" + Client::Ref().GetAuthUser().SessionKey), authRequire, true)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void LogoutRequest::Finish()
|
|
||||||
{
|
|
||||||
APIRequest::Finish();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "APIRequest.h"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
class LogoutRequest : public APIRequest
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
LogoutRequest();
|
|
||||||
|
|
||||||
void Finish();
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "common/String.h"
|
|
||||||
#include <vector>
|
|
||||||
#include <variant>
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
struct Header
|
|
||||||
{
|
|
||||||
ByteString name;
|
|
||||||
ByteString value;
|
|
||||||
};
|
|
||||||
struct FormItem
|
|
||||||
{
|
|
||||||
ByteString name;
|
|
||||||
ByteString value;
|
|
||||||
std::optional<ByteString> filename;
|
|
||||||
};
|
|
||||||
using StringData = ByteString;
|
|
||||||
using FormData = std::vector<FormItem>;
|
|
||||||
using PostData = std::variant<StringData, FormData>;
|
|
||||||
};
|
|
@ -1,19 +0,0 @@
|
|||||||
#include "PublishSaveRequest.h"
|
|
||||||
#include "client/Client.h"
|
|
||||||
#include "Config.h"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
PublishSaveRequest::PublishSaveRequest(int saveID) :
|
|
||||||
APIRequest(ByteString::Build(SCHEME, SERVER, "/Browse/View.json?ID=", saveID, "&Key=", Client::Ref().GetAuthUser().SessionKey), authRequire, true)
|
|
||||||
{
|
|
||||||
AddPostData(FormData{
|
|
||||||
{ "ActionPublish", "bagels" },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void PublishSaveRequest::Finish()
|
|
||||||
{
|
|
||||||
APIRequest::Finish();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "APIRequest.h"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
class PublishSaveRequest : public APIRequest
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
PublishSaveRequest(int saveID);
|
|
||||||
|
|
||||||
void Finish();
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
#include "RemoveTagRequest.h"
|
|
||||||
#include "client/Client.h"
|
|
||||||
#include "Config.h"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
RemoveTagRequest::RemoveTagRequest(int saveID, ByteString tag) :
|
|
||||||
APIRequest(ByteString::Build(SCHEME, SERVER, "/Browse/EditTag.json?Op=delete&ID=", saveID, "&Tag=", tag, "&Key=", Client::Ref().GetAuthUser().SessionKey), authRequire, true)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::list<ByteString> RemoveTagRequest::Finish()
|
|
||||||
{
|
|
||||||
auto result = APIRequest::Finish();
|
|
||||||
std::list<ByteString> tags;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for (auto &tag : result["Tags"])
|
|
||||||
{
|
|
||||||
tags.push_back(tag.asString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception &ex)
|
|
||||||
{
|
|
||||||
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
|
||||||
}
|
|
||||||
return tags;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "APIRequest.h"
|
|
||||||
#include <list>
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
class RemoveTagRequest : public APIRequest
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
RemoveTagRequest(int saveID, ByteString tag);
|
|
||||||
|
|
||||||
std::list<ByteString> Finish();
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
#include "ReportSaveRequest.h"
|
|
||||||
#include "client/Client.h"
|
|
||||||
#include "Config.h"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
ReportSaveRequest::ReportSaveRequest(int saveID, String message) :
|
|
||||||
APIRequest(ByteString::Build(SCHEME, SERVER, "/Browse/Report.json?ID=", saveID, "&Key=", Client::Ref().GetAuthUser().SessionKey), authRequire, true)
|
|
||||||
{
|
|
||||||
AddPostData(FormData{
|
|
||||||
{ "Reason", message.ToUtf8() },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReportSaveRequest::Finish()
|
|
||||||
{
|
|
||||||
APIRequest::Finish();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "APIRequest.h"
|
|
||||||
|
|
||||||
namespace http
|
|
||||||
{
|
|
||||||
class ReportSaveRequest : public APIRequest
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ReportSaveRequest(int saveID, String message);
|
|
||||||
|
|
||||||
void Finish();
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,9 +1,6 @@
|
|||||||
#include "Request.h"
|
#include "Request.h"
|
||||||
#include "requestmanager/RequestManager.h"
|
#include "requestmanager/RequestManager.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <iostream>
|
|
||||||
#include <cstring>
|
|
||||||
#include <json/json.h>
|
|
||||||
|
|
||||||
namespace http
|
namespace http
|
||||||
{
|
{
|
||||||
@ -15,61 +12,45 @@ namespace http
|
|||||||
|
|
||||||
Request::~Request()
|
Request::~Request()
|
||||||
{
|
{
|
||||||
bool tryUnregister;
|
if (handle->state != RequestHandle::ready)
|
||||||
{
|
{
|
||||||
std::lock_guard lk(handle->stateMx);
|
|
||||||
tryUnregister = handle->state == RequestHandle::running;
|
|
||||||
}
|
|
||||||
if (tryUnregister)
|
|
||||||
{
|
|
||||||
// At this point it may have already finished and been unregistered but that's ok,
|
|
||||||
// attempting to unregister a request multiple times is allowed. We only do the
|
|
||||||
// state-checking dance so we don't wake up RequestManager if we don't have to.
|
|
||||||
// In fact, we could just not unregister requests here at all, they'd just run to
|
|
||||||
// completion and be unregistered later. All this does is cancel them early.
|
|
||||||
RequestManager::Ref().UnregisterRequest(*this);
|
RequestManager::Ref().UnregisterRequest(*this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Request::FailEarly(ByteString error)
|
|
||||||
{
|
|
||||||
assert(handle->state == RequestHandle::ready);
|
|
||||||
handle->failEarly = error;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Request::Verb(ByteString newVerb)
|
void Request::Verb(ByteString newVerb)
|
||||||
{
|
{
|
||||||
assert(handle->state == RequestHandle::ready);
|
assert(handle->state == RequestHandle::ready);
|
||||||
handle->verb = newVerb;
|
handle->verb = newVerb;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Request::AddHeader(Header header)
|
void Request::AddHeader(ByteString header)
|
||||||
{
|
{
|
||||||
assert(handle->state == RequestHandle::ready);
|
assert(handle->state == RequestHandle::ready);
|
||||||
handle->headers.push_back(header);
|
handle->headers.push_back(header);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Request::AddPostData(PostData data)
|
void Request::AddPostData(std::map<ByteString, ByteString> data)
|
||||||
{
|
{
|
||||||
assert(handle->state == RequestHandle::ready);
|
assert(handle->state == RequestHandle::ready);
|
||||||
// Even if the map is empty, calling this function signifies you want to do a POST request
|
// Even if the map is empty, calling this function signifies you want to do a POST request
|
||||||
handle->isPost = true;
|
handle->isPost = true;
|
||||||
handle->postData = data;
|
handle->postData.insert(data.begin(), data.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Request::AuthHeaders(ByteString ID, ByteString session)
|
void Request::AuthHeaders(ByteString ID, ByteString session)
|
||||||
{
|
{
|
||||||
assert(handle->state == RequestHandle::ready);
|
assert(handle->state == RequestHandle::ready);
|
||||||
if (ID.size() && ID != "-1") // -1 is an emscripten hack, see AuthUserEmscripten.cpp
|
if (ID.size())
|
||||||
{
|
{
|
||||||
if (session.size())
|
if (session.size())
|
||||||
{
|
{
|
||||||
AddHeader({ "X-Auth-User-Id", ID });
|
AddHeader("X-Auth-User-Id: " + ID);
|
||||||
AddHeader({ "X-Auth-Session-Key", session });
|
AddHeader("X-Auth-Session-Key: " + session);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AddHeader({ "X-Auth-User", ID });
|
AddHeader("X-Auth-User: " + ID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,71 +69,52 @@ namespace http
|
|||||||
return handle->state == RequestHandle::done;
|
return handle->state == RequestHandle::done;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<int64_t, int64_t> Request::CheckProgress() const
|
std::pair<int, int> Request::CheckProgress() const
|
||||||
{
|
{
|
||||||
std::lock_guard lk(handle->stateMx);
|
std::lock_guard lk(handle->stateMx);
|
||||||
assert(handle->state == RequestHandle::running || handle->state == RequestHandle::done);
|
assert(handle->state == RequestHandle::running || handle->state == RequestHandle::done);
|
||||||
return { handle->bytesTotal, handle->bytesDone };
|
return { handle->bytesTotal, handle->bytesDone };
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<Header> &Request::ResponseHeaders() const
|
const std::vector<ByteString> &Request::ResponseHeaders() const
|
||||||
{
|
{
|
||||||
std::lock_guard lk(handle->stateMx);
|
std::lock_guard lk(handle->stateMx);
|
||||||
assert(handle->state == RequestHandle::done);
|
assert(handle->state == RequestHandle::done);
|
||||||
return handle->responseHeaders;
|
return handle->responseHeaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Request::Wait()
|
|
||||||
{
|
|
||||||
std::unique_lock lk(handle->stateMx);
|
|
||||||
assert(handle->state == RequestHandle::running);
|
|
||||||
handle->stateCv.wait(lk, [this]() {
|
|
||||||
return handle->state == RequestHandle::done;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
int Request::StatusCode() const
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::unique_lock lk(handle->stateMx);
|
|
||||||
assert(handle->state == RequestHandle::done);
|
|
||||||
}
|
|
||||||
return handle->statusCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<int, ByteString> Request::Finish()
|
std::pair<int, ByteString> Request::Finish()
|
||||||
{
|
{
|
||||||
|
std::unique_lock lk(handle->stateMx);
|
||||||
|
if (handle->state == RequestHandle::running)
|
||||||
{
|
{
|
||||||
std::unique_lock lk(handle->stateMx);
|
handle->stateCv.wait(lk, [this]() {
|
||||||
assert(handle->state == RequestHandle::done);
|
return handle->state == RequestHandle::done;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
assert(handle->state == RequestHandle::done);
|
||||||
handle->state = RequestHandle::dead;
|
handle->state = RequestHandle::dead;
|
||||||
if (handle->error)
|
return { handle->statusCode, std::move(handle->responseData) };
|
||||||
{
|
|
||||||
throw RequestError(*handle->error);
|
|
||||||
}
|
|
||||||
return std::pair{ handle->statusCode, std::move(handle->responseData) };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RequestHandle::MarkDone()
|
std::pair<int, ByteString> Request::Simple(ByteString uri, std::map<ByteString, ByteString> post_data)
|
||||||
{
|
{
|
||||||
{
|
return SimpleAuth(uri, "", "", post_data);
|
||||||
std::lock_guard lk(stateMx);
|
|
||||||
assert(state == RequestHandle::running);
|
|
||||||
state = RequestHandle::done;
|
|
||||||
}
|
|
||||||
stateCv.notify_one();
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
std::cerr << *error << std::endl;
|
|
||||||
}
|
|
||||||
else if (statusCode >= 400)
|
|
||||||
{
|
|
||||||
std::cerr << "status code " << statusCode << " for request to " << uri << std::endl;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *StatusText(int ret)
|
std::pair<int, ByteString> Request::SimpleAuth(ByteString uri, ByteString ID, ByteString session, std::map<ByteString, ByteString> post_data)
|
||||||
|
{
|
||||||
|
auto request = std::make_unique<Request>(uri);
|
||||||
|
if (!post_data.empty())
|
||||||
|
{
|
||||||
|
request->AddPostData(post_data);
|
||||||
|
}
|
||||||
|
request->AuthHeaders(ID, session);
|
||||||
|
request->Start();
|
||||||
|
return request->Finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
String StatusText(int ret)
|
||||||
{
|
{
|
||||||
switch (ret)
|
switch (ret)
|
||||||
{
|
{
|
||||||
@ -234,69 +196,7 @@ namespace http
|
|||||||
case 619: return "SSL: Failed to Load CRL File";
|
case 619: return "SSL: Failed to Load CRL File";
|
||||||
case 620: return "SSL: Issuer Check Failed";
|
case 620: return "SSL: Issuer Check Failed";
|
||||||
case 621: return "SSL: Pinned Public Key Mismatch";
|
case 621: return "SSL: Pinned Public Key Mismatch";
|
||||||
}
|
default: return "Unknown Status Code";
|
||||||
return "Unknown Status Code";
|
|
||||||
}
|
|
||||||
|
|
||||||
void Request::ParseResponse(const ByteString &result, int status, ResponseType responseType)
|
|
||||||
{
|
|
||||||
// no server response, return "Malformed Response"
|
|
||||||
if (status == 200 && !result.size())
|
|
||||||
{
|
|
||||||
status = 603;
|
|
||||||
}
|
|
||||||
if (status == 302)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (status != 200)
|
|
||||||
{
|
|
||||||
throw RequestError(ByteString::Build("HTTP Error ", status, ": ", http::StatusText(status)));
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (responseType)
|
|
||||||
{
|
|
||||||
case responseOk:
|
|
||||||
if (strncmp(result.c_str(), "OK", 2))
|
|
||||||
{
|
|
||||||
throw RequestError(result);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case responseJson:
|
|
||||||
{
|
|
||||||
std::istringstream ss(result);
|
|
||||||
Json::Value root;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ss >> root;
|
|
||||||
// assume everything is fine if an empty [] is returned
|
|
||||||
if (root.size() == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int status = root.get("Status", 1).asInt();
|
|
||||||
if (status != 1)
|
|
||||||
{
|
|
||||||
throw RequestError(ByteString(root.get("Error", "Unspecified Error").asString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const std::exception &ex)
|
|
||||||
{
|
|
||||||
// sometimes the server returns a 200 with the text "Error: 401"
|
|
||||||
if (!strncmp(result.c_str(), "Error: ", 7))
|
|
||||||
{
|
|
||||||
status = ByteString(result.begin() + 7, result.end()).ToNumber<int>();
|
|
||||||
throw RequestError(ByteString::Build("HTTP Error ", status, ": ", http::StatusText(status)));
|
|
||||||
}
|
|
||||||
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case responseData:
|
|
||||||
// no further processing required
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user