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) ;;
|
||||
arm-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;;
|
||||
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
|
||||
case $BSH_HOST_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
|
||||
;;
|
||||
case $BSH_BUILD_PLATFORM in
|
||||
linux)
|
||||
sudo apt update
|
||||
if [[ $BSH_STATIC_DYNAMIC == static ]]; then
|
||||
@ -86,6 +52,9 @@ if [[ -z ${BSH_NO_PACKAGES-} ]]; then
|
||||
else
|
||||
sudo apt install libluajit-5.1-dev libcurl4-openssl-dev libfftw3-dev zlib1g-dev libsdl2-dev libbz2-dev libjsoncpp-dev
|
||||
fi
|
||||
if [[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC == windows-mingw ]]; then
|
||||
sudo apt install g++-mingw-w64-x86-64
|
||||
fi
|
||||
;;
|
||||
darwin)
|
||||
brew install pkg-config binutils
|
||||
@ -93,14 +62,6 @@ if [[ -z ${BSH_NO_PACKAGES-} ]]; then
|
||||
brew install luajit curl fftw zlib sdl2 bzip2 jsoncpp
|
||||
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
|
||||
fi
|
||||
|
||||
@ -114,6 +75,34 @@ function inplace_sed() {
|
||||
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
|
||||
case $BSH_HOST_ARCH in
|
||||
x86_64) vs_env_arch=x64;;
|
||||
@ -142,9 +131,9 @@ elif [[ $BSH_HOST_PLATFORM == darwin ]]; then
|
||||
elif [[ $BSH_HOST_PLATFORM == android ]]; then
|
||||
case $BSH_HOST_ARCH in
|
||||
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 ;;
|
||||
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
|
||||
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
|
||||
@ -188,18 +177,13 @@ meson_configure=meson$'\t'setup
|
||||
if [[ $BSH_DEBUG_RELEASE == release ]]; then
|
||||
meson_configure+=$'\t'-Dbuildtype=debugoptimized
|
||||
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_comment=$APP_COMMENT
|
||||
meson_configure+=$'\t'-Dapp_exe=$APP_EXE
|
||||
meson_configure+=$'\t'-Dapp_id=$APP_ID
|
||||
meson_configure+=$'\t'-Dapp_data=$APP_DATA
|
||||
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'-Dmod_id=$MOD_ID
|
||||
case $BSH_HOST_ARCH-$BSH_HOST_PLATFORM-$BSH_HOST_LIBC-$BSH_DEBUG_RELEASE in
|
||||
@ -217,12 +201,6 @@ fi
|
||||
if [[ $PACKAGE_MODE == nolua ]]; then
|
||||
meson_configure+=$'\t'-Dlua=none
|
||||
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
|
||||
meson_configure+=$'\t'-Dstatic=prebuilt
|
||||
if [[ $BSH_HOST_PLATFORM == windows ]]; then
|
||||
@ -238,11 +216,6 @@ if [[ $BSH_STATIC_DYNAMIC == static ]]; then
|
||||
c_link_args+=\'-static-libstdc++\',
|
||||
fi
|
||||
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
|
||||
meson_configure+=$'\t'-Dworkaround_elusive_bzip2=true
|
||||
fi
|
||||
@ -266,54 +239,52 @@ fi
|
||||
if [[ $RELEASE_TYPE == stable ]]; then
|
||||
stable_or_beta=yes
|
||||
fi
|
||||
if [[ $stable_or_beta == yes ]]; then
|
||||
xyz=$(echo $RELEASE_NAME | cut -d 'v' -f 2 | cut -d 'b' -f 1) # $RELEASE_NAME is vX.Y.Z or vX.Y.Zb
|
||||
display_version_major=$(echo $xyz | cut -d '.' -f 1)
|
||||
display_version_minor=$(echo $xyz | cut -d '.' -f 2)
|
||||
build_num=$(echo $xyz | cut -d '.' -f 3)
|
||||
if [[ $MOD_ID != 0 ]]; then
|
||||
meson_configure+=$'\t'-Ddisplay_version_major=$display_version_major
|
||||
meson_configure+=$'\t'-Ddisplay_version_minor=$display_version_minor
|
||||
meson_configure+=$'\t'-Dbuild_num=$build_num
|
||||
fi
|
||||
set +e
|
||||
save_version=$(cat src/Config.template.h | sed -n 's/constexpr int SAVE_VERSION * = \([^;]*\);/\1/p')
|
||||
minor_version=$(cat src/Config.template.h | sed -n 's/constexpr int MINOR_VERSION * = \([^;]*\);/\1/p')
|
||||
build_num=$(cat src/Config.template.h | sed -n 's/constexpr int BUILD_NUM * = \([^;]*\);/\1/p')
|
||||
if [[ -z ${save_version-} ]] || [[ -z ${minor_version-} ]] || [[ -z ${build_num-} ]]; then
|
||||
>&2 echo "failed to extract version from Config.template.h"
|
||||
exit 1
|
||||
fi
|
||||
set -e
|
||||
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
|
||||
if [[ $RELEASE_TYPE == snapshot ]]; then
|
||||
build_num=$(echo $RELEASE_NAME | cut -d '-' -f 2) # $RELEASE_NAME is snapshot-X
|
||||
meson_configure+=$'\t'-Dsnapshot=true
|
||||
if [[ $MOD_ID != 0 ]]; then
|
||||
meson_configure+=$'\t'-Dbuild_num=$build_num
|
||||
fi
|
||||
meson_configure+=$'\t'-Dsnapshot_id=$(echo $RELEASE_NAME | cut -d '-' -f 2) # $RELEASE_NAME is snapshot-X
|
||||
fi
|
||||
if [[ $RELEASE_TYPE == snapshot ]] && [[ $MOD_ID != 0 ]]; then
|
||||
>&2 echo "mods and snapshots do not mix"
|
||||
exit 1
|
||||
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
|
||||
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
|
||||
if [[ $RELEASE_TYPE != dev ]]; then
|
||||
meson_configure+=$'\t'-Dignore_updates=false
|
||||
fi
|
||||
if [[ "$BSH_HOST_PLATFORM-$BSH_HOST_LIBC" == "windows-mingw" ]]; then
|
||||
meson_configure+=$'\t'--cross-file=.github/mingw-ghactions.ini
|
||||
# there is some mingw bug that only ever manifests on ghactions which makes MakeIco.exe use tons of memory and fail
|
||||
# TODO: remove this hack once we figure out how to fix that
|
||||
meson_configure+=$'\t'-Dwindows_icons=false
|
||||
if [[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC == windows-mingw ]]; then
|
||||
if [[ $BSH_BUILD_PLATFORM == linux ]]; then
|
||||
meson_configure+=$'\t'--cross-file=.github/mingw-ghactions.ini
|
||||
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
|
||||
fi
|
||||
if [[ $BSH_HOST_PLATFORM-$BSH_HOST_ARCH == darwin-aarch64 ]]; then
|
||||
meson_configure+=$'\t'--cross-file=.github/macaa64-ghactions.ini
|
||||
fi
|
||||
if [[ $BSH_HOST_PLATFORM == emscripten ]]; 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 [[ $RELEASE_TYPE == tptlibsdev ]] && ([[ $BSH_HOST_PLATFORM == windows ]] || [[ $BSH_STATIC_DYNAMIC == static ]]); then
|
||||
if [[ -z ${TPTLIBSREMOTE-} ]]; then
|
||||
if [[ -z "${GITHUB_REPOSITORY_OWNER-}" ]]; then
|
||||
>&2 echo "GITHUB_REPOSITORY_OWNER not set"
|
||||
@ -323,6 +294,11 @@ if [[ $RELEASE_TYPE == tptlibsdev ]] && ([[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC ==
|
||||
else
|
||||
tptlibsremote=$TPTLIBSREMOTE
|
||||
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
|
||||
if [[ -d build-tpt-libs ]] && [[ ${TPTLIBSRESET-} == yes ]]; then
|
||||
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
|
||||
fi
|
||||
if [[ $BSH_HOST_PLATFORM == android ]]; then
|
||||
android_platform_jar=$ANDROID_SDK_ROOT/platforms/$android_platform/android.jar
|
||||
if ! [[ -f $android_platform_jar ]]; then
|
||||
>&2 echo "$android_platform_jar not found"
|
||||
exit 1
|
||||
fi
|
||||
android_platform=android-30
|
||||
meson_configure+=$'\t'--cross-file=android/cross/$BSH_HOST_ARCH.ini
|
||||
cat << ANDROID_INI > .github/android-ghactions.ini
|
||||
[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 = android_ndk_toolchain_prefix
|
||||
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'
|
||||
|
||||
[binaries]
|
||||
@ -382,6 +354,7 @@ zipalign = andriod_sdk_build_tools / 'zipalign'
|
||||
apksigner = andriod_sdk_build_tools / 'apksigner'
|
||||
ANDROID_INI
|
||||
meson_configure+=$'\t'--cross-file=.github/android-ghactions.ini
|
||||
meson_configure+=$'\t'-Dhttp=false
|
||||
fi
|
||||
meson_configure+=$'\t'-Dc_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 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
|
||||
objcopy=objcopy
|
||||
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
|
||||
strip_target=$APP_EXE
|
||||
fi
|
||||
meson_compile=meson$'\t'compile
|
||||
meson_compile+=$'\t'-v
|
||||
if [[ $BSH_BUILD_PLATFORM == windows ]] && [[ $PACKAGE_MODE != backendvs ]]; then
|
||||
if [[ $BSH_BUILD_PLATFORM == windows ]]; then
|
||||
set +e
|
||||
meson_compile+=$'\t'--ninja-args='["-d","keeprsp"]'
|
||||
$meson_compile
|
||||
ninja -v -d keeprsp
|
||||
ninja_code=$?
|
||||
set -e
|
||||
cat $APP_EXE.exe.rsp
|
||||
@ -444,7 +391,7 @@ if [[ $BSH_BUILD_PLATFORM == windows ]] && [[ $PACKAGE_MODE != backendvs ]]; the
|
||||
fi
|
||||
fi
|
||||
else
|
||||
$meson_compile
|
||||
ninja -v
|
||||
fi
|
||||
|
||||
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/Contents
|
||||
cp resources/Info.plist $appdir/Contents/Info.plist
|
||||
subst_version $appdir/Contents/Info.plist
|
||||
mkdir $appdir/Contents/MacOS
|
||||
cp $APP_EXE $appdir/Contents/MacOS/$APP_EXE
|
||||
mkdir $appdir/Contents/Resources
|
||||
@ -485,8 +433,6 @@ if [[ $PACKAGE_MODE == dmg ]]; then
|
||||
cp ../LICENSE dmgroot/LICENSE
|
||||
cp ../README.md dmgroot/README.md
|
||||
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
|
||||
# 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
|
||||
@ -509,6 +455,7 @@ elif [[ $PACKAGE_MODE == appimage ]]; then
|
||||
cp ../resources/icon_exe.svg $appdir/$APP_VENDOR-$APP_EXE.svg
|
||||
cp resources/powder.desktop $appdir/$APP_ID.desktop
|
||||
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_ID.desktop $appdir/usr/share/applications/$APP_ID.desktop
|
||||
./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]
|
||||
c = [ 'clang', '-arch', 'arm64' ]
|
||||
cpp = [ 'clang++', '-arch', 'arm64' ]
|
||||
objcpp = [ 'clang++', '-arch', 'arm64' ]
|
||||
strip = 'strip'
|
||||
|
||||
[host_machine]
|
||||
|
4
.github/mingw-ghactions.ini
vendored
4
.github/mingw-ghactions.ini
vendored
@ -2,8 +2,8 @@
|
||||
prefix = 'x86_64-w64-mingw32'
|
||||
|
||||
[binaries]
|
||||
c = prefix + '-gcc'
|
||||
cpp = prefix + '-g++'
|
||||
c = prefix + '-gcc-posix'
|
||||
cpp = prefix + '-g++-posix'
|
||||
strip = prefix + '-strip'
|
||||
windres = prefix + '-windres'
|
||||
|
||||
|
123
.github/prepare.py
vendored
123
.github/prepare.py
vendored
@ -6,7 +6,6 @@ import subprocess
|
||||
import sys
|
||||
|
||||
ref = os.getenv('GITHUB_REF')
|
||||
event_name = os.getenv('GITHUB_EVENT_NAME')
|
||||
publish_hostport = os.getenv('PUBLISH_HOSTPORT')
|
||||
|
||||
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_snapshot = re.fullmatch(r'refs/tags/snapshot-([0-9]+)', ref)
|
||||
match_tptlibsdev = re.fullmatch(r'refs/heads/tptlibsdev-(.*)', ref)
|
||||
match_alljobs = re.fullmatch(r'refs/heads/(.*)-alljobs', ref)
|
||||
do_release = False
|
||||
do_priority = 10
|
||||
if event_name == 'pull_request':
|
||||
do_priority = 0
|
||||
if match_stable:
|
||||
release_type = 'stable'
|
||||
release_name = 'v%s.%s.%s' % (match_stable.group(1), match_stable.group(2), match_stable.group(3))
|
||||
do_release = True
|
||||
do_priority = 0
|
||||
elif match_beta:
|
||||
release_type = 'beta'
|
||||
release_name = 'v%s.%s.%sb' % (match_beta.group(1), match_beta.group(2), match_beta.group(3))
|
||||
do_release = True
|
||||
do_priority = 0
|
||||
elif match_snapshot:
|
||||
release_type = 'snapshot'
|
||||
release_name = 'snapshot-%s' % match_snapshot.group(1)
|
||||
do_release = True
|
||||
do_priority = 0
|
||||
elif match_tptlibsdev:
|
||||
release_type = 'tptlibsdev'
|
||||
release_name = 'tptlibsdev-%s' % match_tptlibsdev.group(1)
|
||||
do_priority = 0
|
||||
else:
|
||||
release_type = 'dev'
|
||||
release_name = 'dev'
|
||||
if match_alljobs:
|
||||
do_priority = 0
|
||||
do_publish = publish_hostport and do_release
|
||||
|
||||
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()
|
||||
|
||||
if int(build_options['mod_id']) == 0:
|
||||
if release_type == 'stable':
|
||||
pass
|
||||
elif release_type == 'beta':
|
||||
if release_type == 'beta':
|
||||
build_options['app_name' ] += ' Beta'
|
||||
build_options['app_comment'] += ' - Beta'
|
||||
build_options['app_exe' ] += 'beta'
|
||||
build_options['app_id' ] += 'beta'
|
||||
elif release_type == 'snapshot':
|
||||
if release_type == 'snapshot':
|
||||
build_options['app_name' ] += ' Snapshot'
|
||||
build_options['app_comment'] += ' - Snapshot'
|
||||
build_options['app_exe' ] += '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('app_name' , build_options['app_name' ])
|
||||
@ -95,61 +77,47 @@ app_name_slug = re.sub('[^A-Za-z0-9]', '_', app_name)
|
||||
build_matrix = []
|
||||
publish_matrix = []
|
||||
# consider disabling line wrapping to edit this monstrosity
|
||||
for arch, platform, libc, statdyn, bplatform, runson, suffix, publish, artifact, dbgsuffix, mode, starcatcher, dbgrel, priority 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', '', True, True, '.dbg', None, 'x86_64-lin-gcc-static', 'release', 10 ),
|
||||
( 'x86_64', 'linux', 'gnu', 'static', 'linux', 'ubuntu-20.04', '', False, True, '.dbg', 'appimage', None, 'release', 0 ), # priority = 0: appimage 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, 'nohttp', None, 'debug', 10 ),
|
||||
( '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, None, None, 'release', 10 ),
|
||||
# ( '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, 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', '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, 'release', 10 ), # ubuntu-20.04 doesn't have ucrt64-capable mingw >_>
|
||||
( '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, True, '.dbg', None, None, 'release', 10 ),
|
||||
( '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, 'release', 10 ),
|
||||
( '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', True, True, '.pdb', None,'x86_64-win-msvc-static', 'release', 10 ),
|
||||
( '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, 'backendvs', None, 'debug', 0 ), # priority = 0: backend=vs build
|
||||
( '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', 0 ), # priority = 0: static debug build
|
||||
( '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', 10 ),
|
||||
( 'x86', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'release', 10 ),
|
||||
( '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-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-12', '.dmg', False, False, None, 'dmg', None, 'debug', 10 ),
|
||||
( 'x86_64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-12', '.dmg', False, False, None, 'dmg', None, 'release', 10 ),
|
||||
( '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-12', '.dmg', True, True, None, 'dmg', 'arm64-mac-gcc-static', 'release', 10 ),
|
||||
# ( '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-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', 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', 10 ),
|
||||
( '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', 10 ),
|
||||
( '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', 10 ),
|
||||
( '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', 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
|
||||
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' ),
|
||||
( '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' ),
|
||||
( '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' ),
|
||||
( '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' ),
|
||||
# ( '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' ), # 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' ),
|
||||
( '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' ),
|
||||
( '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' ),
|
||||
( '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' ),
|
||||
( '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' ),
|
||||
( 'x86_64', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'release' ),
|
||||
( 'x86', 'windows', 'msvc', 'static', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug' ),
|
||||
( 'x86', 'windows', 'msvc', 'static', 'windows', 'windows-2019', '.exe', True, True, '.pdb', None, 'i686-win-msvc-static', 'release' ),
|
||||
( '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, 'release' ),
|
||||
( 'x86_64', 'darwin', 'macos', 'static', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'debug' ),
|
||||
( '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', 'dynamic', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'debug' ),
|
||||
( 'x86_64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'release' ),
|
||||
( 'aarch64', 'darwin', 'macos', 'static', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'debug' ),
|
||||
( 'aarch64', 'darwin', 'macos', 'static', 'darwin', 'macos-11.0', '.dmg', True, True, None, 'dmg', 'arm64-mac-gcc-static', 'release' ),
|
||||
# ( '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-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
|
||||
( 'x86', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', False, False, None, None, None, 'debug' ),
|
||||
( 'x86', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', True, True, '.dbg', None, 'i686-and-gcc-static', 'release' ),
|
||||
( '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', True, True, '.dbg', None, 'x86_64-and-gcc-static', 'release' ),
|
||||
( 'arm', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', False, False, None, None, None, 'debug' ),
|
||||
( 'arm', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', True, True, '.dbg', None, 'arm-and-gcc-static', 'release' ),
|
||||
( 'aarch64', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', False, False, None, None, None, 'debug' ),
|
||||
( 'aarch64', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', True, True, '.dbg', None, 'arm64-and-gcc-static', 'release' ),
|
||||
]:
|
||||
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:
|
||||
mode = 'default'
|
||||
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_name = f'{app_name_slug}-{arch}.AppImage.dbg'
|
||||
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({
|
||||
'bsh_build_platform': bplatform, # 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_debug_release': dbgrel, # part of the unique portion of the matrix
|
||||
'runs_on': runson,
|
||||
'force_msys2_bash': msys2_bash and 'yes' or 'no',
|
||||
'package_suffix': suffix,
|
||||
'package_mode': mode,
|
||||
'publish': publish and 'yes' or 'no',
|
||||
@ -195,8 +158,6 @@ for arch, platform, libc, statdyn, bplatform, runso
|
||||
'asset_name': asset_name,
|
||||
'debug_asset_path': debug_asset_path,
|
||||
'debug_asset_name': debug_asset_name,
|
||||
'job_name': job_name,
|
||||
'shell': shell,
|
||||
})
|
||||
if publish:
|
||||
publish_matrix.append({
|
||||
|
16
.github/starcatcher-publish.sh
vendored
16
.github/starcatcher-publish.sh
vendored
@ -3,18 +3,4 @@
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
cat << NETRC > ~/.netrc
|
||||
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
|
||||
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"
|
||||
|
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
|
||||
set +e
|
||||
export "$i" 2>/dev/null
|
||||
echo $i | grep ERROR
|
||||
set -e
|
||||
done
|
||||
|
||||
cl
|
||||
|
96
.github/workflows/build.yaml
vendored
96
.github/workflows/build.yaml
vendored
@ -16,6 +16,7 @@ jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
release_upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
do_release: ${{ steps.prepare.outputs.do_release }}
|
||||
build_matrix: ${{ steps.prepare.outputs.build_matrix }}
|
||||
publish_matrix: ${{ steps.prepare.outputs.publish_matrix }}
|
||||
@ -31,76 +32,39 @@ jobs:
|
||||
do_publish: ${{ steps.prepare.outputs.do_publish }}
|
||||
steps:
|
||||
- run: git config --global core.autocrlf false
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
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
|
||||
run: python ./.github/prepare.py
|
||||
env:
|
||||
PUBLISH_HOSTPORT: ${{ secrets.STARCATCHER_PUBLISH_HOSTPORT }}
|
||||
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'
|
||||
id: create_release
|
||||
uses: LBPHacker/create-release@v2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
RELEASE_NAME: ${{ steps.prepare.outputs.release_name }}
|
||||
run: bash -c './.github/create-release.sh'
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: ${{ steps.prepare.outputs.release_name }}
|
||||
draft: true
|
||||
prerelease: false
|
||||
build:
|
||||
runs-on: ${{ matrix.runs_on }}
|
||||
name: ${{ matrix.job_name }}
|
||||
needs: [prepare]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJSON(needs.prepare.outputs.build_matrix) }}
|
||||
defaults:
|
||||
run:
|
||||
shell: ${{ matrix.shell }}
|
||||
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
|
||||
- uses: actions/checkout@v4
|
||||
- if: matrix.force_msys2_bash != 'yes'
|
||||
uses: actions/setup-python@v5
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- if: matrix.force_msys2_bash != 'yes'
|
||||
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
|
||||
- if: matrix.bsh_build_platform == 'darwin'
|
||||
run: brew install bash coreutils
|
||||
- run: bash -c './.github/build.sh'
|
||||
@ -124,24 +88,30 @@ jobs:
|
||||
PACKAGE_MODE: ${{ matrix.package_mode }}
|
||||
ASSET_PATH: ${{ matrix.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:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ASSET_PATH: build/${{ matrix.asset_path }}
|
||||
ASSET_NAME: ${{ matrix.asset_name }}
|
||||
run: bash -c './.github/upload-release-asset.sh'
|
||||
- if: needs.prepare.outputs.do_release == 'yes' && matrix.publish == 'yes' && matrix.separate_debug == 'yes'
|
||||
with:
|
||||
upload_url: ${{ needs.prepare.outputs.release_upload_url }}
|
||||
asset_path: build/${{ matrix.asset_path }}
|
||||
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:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ASSET_PATH: build/${{ matrix.debug_asset_path }}
|
||||
ASSET_NAME: ${{ matrix.debug_asset_name }}
|
||||
run: bash -c './.github/upload-release-asset.sh'
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
upload_url: ${{ needs.prepare.outputs.release_upload_url }}
|
||||
asset_path: build/${{ matrix.debug_asset_path }}
|
||||
asset_name: ${{ matrix.debug_asset_name }}
|
||||
asset_content_type: application/zip
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: matrix.artifact == 'yes'
|
||||
with:
|
||||
path: build/${{ matrix.asset_path }}
|
||||
name: ${{ matrix.asset_name }}
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: matrix.artifact == 'yes' && matrix.separate_debug == 'yes'
|
||||
with:
|
||||
path: build/${{ matrix.debug_asset_path }}
|
||||
@ -155,12 +125,12 @@ jobs:
|
||||
if: needs.prepare.outputs.do_publish == 'yes'
|
||||
steps:
|
||||
- run: git config --global core.autocrlf false
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ${{ matrix.asset_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:
|
||||
PUBLISH_HOSTPORT: ${{ secrets.STARCATCHER_PUBLISH_HOSTPORT }}
|
||||
PUBLISH_USERNAME: ${{ secrets.STARCATCHER_PUBLISH_USERNAME }}
|
||||
@ -173,7 +143,7 @@ jobs:
|
||||
if: needs.prepare.outputs.do_publish == 'yes'
|
||||
steps:
|
||||
- run: git config --global core.autocrlf false
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- run: ./.github/starcatcher-release.sh
|
||||
env:
|
||||
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).
|
||||
@ -25,7 +25,7 @@ Thanks
|
||||
* Skresanov Savely
|
||||
* cracker64
|
||||
* Catelite
|
||||
* Victoria Hoyle
|
||||
* Bryan Hoyle
|
||||
* Nathan Cousins
|
||||
* jacksonmj
|
||||
* Felix Wallin
|
||||
@ -82,7 +82,6 @@ Controls
|
||||
| I | Invert Pressure and Velocity map |
|
||||
| W | Cycle gravity modes (use with Ctrl when STK2 is out) |
|
||||
| Y | Cycle air modes |
|
||||
| Ctrl + E | Cycle edge modes |
|
||||
| B | Enter decoration editor menu |
|
||||
| Ctrl + B | Toggle decorations on/off |
|
||||
| N | Toggle Newtonian Gravity on/off |
|
||||
@ -108,7 +107,6 @@ Controls
|
||||
| Shift + R | Horizontal 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 |
|
||||
| F11 | Toggle fullscreen |
|
||||
|
||||
Command Line
|
||||
---------------------------------------------------------------------------
|
||||
|
@ -2,12 +2,12 @@
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="@APPID@"
|
||||
android:versionCode="@BUILD_NUM@"
|
||||
android:versionName="@DISPLAY_VERSION_MAJOR@.@DISPLAY_VERSION_MINOR@.@BUILD_NUM@"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0"
|
||||
android:installLocation="auto"
|
||||
>
|
||||
<uses-sdk
|
||||
android:minSdkVersion="21"
|
||||
android:minSdkVersion="19"
|
||||
android:targetSdkVersion="30"
|
||||
/>
|
||||
<uses-feature
|
||||
@ -33,18 +33,16 @@
|
||||
android:name="android.hardware.type.pc"
|
||||
android:required="false"
|
||||
/>
|
||||
@ANDROID_PERMISSIONS@
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<application
|
||||
android:label="@string/app_name"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:allowBackup="true"
|
||||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
|
||||
android:hardwareAccelerated="true"
|
||||
@ANDROID_PROPERTIES@
|
||||
>
|
||||
<activity
|
||||
android:name=".PowderActivity"
|
||||
android:screenOrientation="landscape"
|
||||
android:label="@string/app_name"
|
||||
android:alwaysRetainTaskState="true"
|
||||
android:launchMode="singleInstance"
|
||||
|
@ -1,42 +1,7 @@
|
||||
package @APPID@;
|
||||
|
||||
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 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]
|
||||
android_ndk_toolchain_prefix = 'armv7a-linux-androideabi21-'
|
||||
android_ndk_toolchain_prefix = 'armv7a-linux-androideabi19-'
|
||||
|
||||
[host_machine]
|
||||
system = 'android'
|
||||
|
@ -1,5 +1,5 @@
|
||||
[constants]
|
||||
android_ndk_toolchain_prefix = 'i686-linux-android21-'
|
||||
android_ndk_toolchain_prefix = 'i686-linux-android19-'
|
||||
|
||||
[host_machine]
|
||||
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,
|
||||
]).returncode:
|
||||
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]
|
||||
c = [ 'clang', '-arch', 'arm64' ]
|
||||
cpp = [ 'clang++', '-arch', 'arm64' ]
|
||||
objcpp = [ 'clang++', '-arch', 'arm64' ]
|
||||
strip = 'strip'
|
||||
|
||||
[host_machine]
|
||||
|
136
meson.build
136
meson.build
@ -10,7 +10,7 @@ project(
|
||||
'build.cpp_std=c++17', # 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')
|
||||
@ -18,7 +18,6 @@ if get_option('prepare')
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
fs = import('fs')
|
||||
to_array = generator(
|
||||
executable('toarray', sources: 'resources/ToArray.cpp', native: true),
|
||||
output: [ '@PLAINNAME@.cpp', '@PLAINNAME@.h' ],
|
||||
@ -44,11 +43,11 @@ if c_compiler.get_id() in [ 'msvc' ]
|
||||
host_libc = 'msvc'
|
||||
elif c_compiler.get_id() in [ 'gcc' ] and host_platform == 'windows'
|
||||
host_libc = 'mingw'
|
||||
if get_option('b_lto')
|
||||
warning('mingw does not like static + lto')
|
||||
endif
|
||||
elif host_platform in [ 'darwin' ]
|
||||
host_libc = 'macos'
|
||||
elif host_platform in [ 'emscripten' ]
|
||||
host_platform = 'emscripten'
|
||||
host_libc = 'emscripten'
|
||||
elif host_platform in [ 'android' ]
|
||||
host_platform = 'android'
|
||||
host_libc = 'bionic'
|
||||
@ -66,8 +65,8 @@ if static_variant != 'prebuilt' and host_platform == 'android'
|
||||
warning('only prebuilt libs are supported for android')
|
||||
static_variant = 'prebuilt'
|
||||
endif
|
||||
if static_variant == 'system' and host_platform == 'windows' and host_libc == 'msvc'
|
||||
warning('no way to find system libs for msvc on windows')
|
||||
if static_variant == 'system' and host_platform == 'windows'
|
||||
warning('no way to find system libs on windows')
|
||||
static_variant = 'prebuilt'
|
||||
endif
|
||||
|
||||
@ -79,19 +78,20 @@ tpt_libs_static = 'none'
|
||||
if static_variant == 'prebuilt'
|
||||
tpt_libs_static = 'static'
|
||||
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'
|
||||
endif
|
||||
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_vtag = get_option('tpt_libs_vtag')
|
||||
if tpt_libs_vtag == ''
|
||||
tpt_libs_vtag = 'v20240112165024'
|
||||
tpt_libs_vtag = 'v20230205154205'
|
||||
endif
|
||||
if tpt_libs_static != 'none'
|
||||
if tpt_libs_variant not in [
|
||||
'x86_64-linux-gnu-static',
|
||||
'x86_64-windows-mingw-static',
|
||||
'x86_64-windows-mingw-dynamic',
|
||||
'x86_64-windows-msvc-static',
|
||||
'x86_64-windows-msvc-dynamic',
|
||||
'x86-windows-msvc-static',
|
||||
@ -102,7 +102,6 @@ if tpt_libs_static != 'none'
|
||||
'x86_64-android-bionic-static',
|
||||
'arm-android-bionic-static',
|
||||
'aarch64-android-bionic-static',
|
||||
'wasm32-emscripten-emscripten-static',
|
||||
]
|
||||
error('no prebuilt @0@ libraries are currently provided'.format(tpt_libs_variant))
|
||||
endif
|
||||
@ -145,14 +144,7 @@ endif
|
||||
|
||||
lua_variant = get_option('lua')
|
||||
if lua_variant == 'auto'
|
||||
if host_platform == 'emscripten'
|
||||
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')
|
||||
lua_variant = 'luajit'
|
||||
endif
|
||||
if lua_variant == 'none'
|
||||
lua_dep = []
|
||||
@ -169,6 +161,9 @@ elif lua_variant == 'luajit'
|
||||
endif
|
||||
|
||||
enable_http = get_option('http')
|
||||
if host_platform == 'android'
|
||||
enable_http = false
|
||||
endif
|
||||
if host_platform == 'android'
|
||||
android_ndk_toolchain_prefix = meson.get_external_property('android_ndk_toolchain_prefix')
|
||||
android_platform = meson.get_external_property('android_platform')
|
||||
@ -182,57 +177,19 @@ if host_platform == 'android'
|
||||
error('tpt-libs android platform mismatch')
|
||||
endif
|
||||
endif
|
||||
curl_dep = []
|
||||
if enable_http and host_platform != 'emscripten'
|
||||
curl_dep = dependency('libcurl', static: is_static)
|
||||
endif
|
||||
curl_dep = enable_http ? dependency('libcurl', static: is_static) : []
|
||||
|
||||
fftw_dep = dependency('fftw3f', static: is_static)
|
||||
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_c_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 x86_sse_level >= 30
|
||||
warning('SSE3 configured to be enabled but unavailable in msvc')
|
||||
@ -318,15 +275,14 @@ else
|
||||
endif
|
||||
|
||||
if host_platform == 'windows'
|
||||
args_ccomp_win = [ '-D_WIN32_WINNT=0x0501', '-DNOMINMAX' ]
|
||||
args_ccomp_win = [ '-D_WIN32_WINNT=0x0501' ]
|
||||
windows_mod = import('windows')
|
||||
if is_static
|
||||
args_ccomp_win += [ '-DCURL_STATICLIB' ]
|
||||
if host_arch == 'x86_64'
|
||||
args_ccomp_win += [ '-DZLIB_WINAPI' ]
|
||||
endif
|
||||
endif
|
||||
if tpt_libs_static == 'dynamic'
|
||||
else
|
||||
foreach input_output_condition : tpt_libs.get_variable('config_dlls')
|
||||
dll_input = input_output_condition[0]
|
||||
dll_output = input_output_condition[1]
|
||||
@ -338,7 +294,7 @@ if host_platform == 'windows'
|
||||
do_copy = true
|
||||
endif
|
||||
if do_copy
|
||||
fs.copyfile(dll_input, dll_output)
|
||||
configure_file(input: dll_input, output: dll_output, copy: true)
|
||||
endif
|
||||
endforeach
|
||||
endif
|
||||
@ -358,11 +314,7 @@ else
|
||||
ident_platform = 'UNKNOWN'
|
||||
endif
|
||||
|
||||
project_deps = []
|
||||
data_files = []
|
||||
powder_deps = []
|
||||
|
||||
project_export_dynamic = false
|
||||
|
||||
subdir('src')
|
||||
subdir('resources')
|
||||
@ -371,30 +323,8 @@ powder_files += data_files
|
||||
render_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')
|
||||
powder_deps += project_deps + [
|
||||
powder_deps = [
|
||||
threads_dep,
|
||||
zlib_dep,
|
||||
png_dep,
|
||||
@ -426,17 +356,12 @@ if get_option('build_powder')
|
||||
win_subsystem: is_debug ? 'console' : 'windows',
|
||||
link_args: project_link_args,
|
||||
dependencies: powder_deps,
|
||||
export_dynamic: project_export_dynamic,
|
||||
install: true,
|
||||
)
|
||||
endif
|
||||
endif
|
||||
|
||||
if get_option('build_render')
|
||||
if host_platform == 'emscripten'
|
||||
error('render does not target emscripten')
|
||||
endif
|
||||
render_deps = project_deps + [
|
||||
render_deps = [
|
||||
threads_dep,
|
||||
zlib_dep,
|
||||
bzip2_dep,
|
||||
@ -455,15 +380,11 @@ if get_option('build_render')
|
||||
cpp_args: project_cpp_args,
|
||||
link_args: render_link_args,
|
||||
dependencies: render_deps,
|
||||
export_dynamic: project_export_dynamic,
|
||||
)
|
||||
endif
|
||||
|
||||
if get_option('build_font')
|
||||
if host_platform == 'emscripten'
|
||||
error('font does not target emscripten')
|
||||
endif
|
||||
font_deps = project_deps + [
|
||||
font_deps = [
|
||||
threads_dep,
|
||||
zlib_dep,
|
||||
png_dep,
|
||||
@ -479,6 +400,5 @@ if get_option('build_font')
|
||||
cpp_args: project_cpp_args,
|
||||
link_args: project_link_args,
|
||||
dependencies: font_deps,
|
||||
export_dynamic: project_export_dynamic,
|
||||
)
|
||||
endif
|
||||
|
@ -37,46 +37,11 @@ option(
|
||||
description: 'Snapshot build'
|
||||
)
|
||||
option(
|
||||
'display_version_major',
|
||||
'snapshot_id',
|
||||
type: 'integer',
|
||||
min: 0,
|
||||
value: 98,
|
||||
description: 'Major component of the display version, should more or less map to the MINOR version in semantic versioning'
|
||||
)
|
||||
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'
|
||||
value: 0,
|
||||
description: 'Snapshot ID, only relevant if \'snapshot\' is true'
|
||||
)
|
||||
option(
|
||||
'mod_id',
|
||||
@ -133,7 +98,7 @@ option(
|
||||
'update_server',
|
||||
type: 'string',
|
||||
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(
|
||||
'workaround_noncpp_lua',
|
||||
@ -237,12 +202,6 @@ option(
|
||||
value: true,
|
||||
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(
|
||||
'prepare',
|
||||
type: 'boolean',
|
||||
@ -255,59 +214,3 @@ option(
|
||||
value: 'disabled',
|
||||
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>
|
||||
<string>icon_exe.icns</string>
|
||||
<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>
|
||||
<string>English</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
@ -57,13 +57,13 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>@DISPLAY_VERSION_MAJOR@.@DISPLAY_VERSION_MINOR@</string>
|
||||
<string>SUBST_SAVE_VERSION.SUBST_MINOR_VERSION</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<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>
|
||||
<string>@MANIFEST_MACOS_MIN_VER@</string>
|
||||
<string>SUBST_MACOS_MIN_VER</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
|
@ -1,137 +1,67 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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);
|
||||
}
|
||||
#include <cstdint>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 3)
|
||||
{
|
||||
std::cerr << "usage: " << argv[0] << " OUTPUT INPUT..." << std::endl;
|
||||
exit(1);
|
||||
return 1;
|
||||
}
|
||||
auto *outputIcoPath = argv[1];
|
||||
std::ofstream outputIco(outputIcoPath, std::ios::binary);
|
||||
if (!outputIco)
|
||||
{
|
||||
return 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;
|
||||
exit(1);
|
||||
auto *inputAnyPath = argv[i + 2];
|
||||
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];
|
||||
std::ofstream output(outputPath, std::ios::binary);
|
||||
auto outputFailure = [&outputPath](std::string action) {
|
||||
std::cerr << "failed to " << action << " " << outputPath << ": " << strerror(errno) << std::endl;
|
||||
exit(1);
|
||||
};
|
||||
if (!output)
|
||||
outputIco.write(&header[0], header.size());
|
||||
outputIco.write(&allData[0], allData.size());
|
||||
if (!outputIco)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
@ -29,6 +29,6 @@
|
||||
<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>
|
||||
<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>
|
||||
</component>
|
||||
|
Binary file not shown.
@ -32,65 +32,35 @@ else
|
||||
endif
|
||||
|
||||
if host_platform == 'windows'
|
||||
windows_icons = get_option('windows_icons')
|
||||
windows_utf8cp = get_option('windows_utf8cp')
|
||||
rc_conf_depends = []
|
||||
rc_conf_depend_files = [
|
||||
'resource.h',
|
||||
]
|
||||
icon_exe_ico_path = ''
|
||||
icon_cps_ico_path = ''
|
||||
winutf8_xml_path = ''
|
||||
if windows_icons
|
||||
make_ico = executable('makeico', sources: 'MakeIco.cpp', native: true)
|
||||
generated_win_icos = {}
|
||||
win_icos = {
|
||||
'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,
|
||||
) }
|
||||
make_ico = executable('makeico', sources: 'MakeIco.cpp', native: true)
|
||||
generated_win_icos = {}
|
||||
win_icos = {
|
||||
'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
|
||||
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_cps'],
|
||||
]
|
||||
icon_exe_ico_path = join_paths(meson.current_build_dir(), 'icon_exe.ico')
|
||||
icon_cps_ico_path = join_paths(meson.current_build_dir(), 'icon_cps.ico')
|
||||
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,
|
||||
],
|
||||
depend_files: [
|
||||
'resource.h',
|
||||
],
|
||||
)
|
||||
elif host_platform == 'darwin'
|
||||
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
|
||||
#include "VcsTag.h"
|
||||
#include "common/Version.h"
|
||||
|
||||
constexpr bool SET_WINDOW_ICON = @SET_WINDOW_ICON@;
|
||||
constexpr bool DEBUG = @DEBUG@;
|
||||
@ -13,27 +11,11 @@ constexpr bool LUACONSOLE = @LUACONSOLE@;
|
||||
constexpr bool ALLOW_FAKE_NEWER_VERSION = @ALLOW_FAKE_NEWER_VERSION@;
|
||||
constexpr bool USE_UPDATESERVER = @USE_UPDATESERVER@;
|
||||
constexpr bool CAN_INSTALL = @CAN_INSTALL@;
|
||||
constexpr bool USE_BLUESCREEN = @USE_BLUESCREEN@;
|
||||
constexpr bool INSTALL_CHECK = @INSTALL_CHECK@;
|
||||
constexpr bool IGNORE_UPDATES = @IGNORE_UPDATES@;
|
||||
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@';
|
||||
|
||||
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 STATICSERVER[] = "@STATICSERVER@";
|
||||
constexpr char UPDATESERVER[] = "@UPDATESERVER@";
|
||||
@ -46,17 +28,13 @@ constexpr char APPID[] = "@APPID@";
|
||||
constexpr char APPDATA[] = "@APPDATA@";
|
||||
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@;
|
||||
|
||||
struct DisplayVersionWithBuild
|
||||
{
|
||||
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 int FUTURE_SAVE_VERSION = 97;
|
||||
constexpr int FUTURE_MINOR_VERSION = 0;
|
||||
|
||||
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 STAMPS_DIR[] = "stamps";
|
||||
constexpr char BRUSH_DIR[] = "Brushes";
|
||||
|
||||
constexpr int httpMaxConcurrentStreams = 50;
|
||||
constexpr int httpConnectTimeoutS = 15;
|
||||
|
@ -10,19 +10,12 @@
|
||||
#include "Format.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;
|
||||
char buffer[128];
|
||||
|
||||
if (local)
|
||||
{
|
||||
timeData = localtime(&unixtime);
|
||||
}
|
||||
else
|
||||
{
|
||||
timeData = gmtime(&unixtime);
|
||||
}
|
||||
timeData = localtime(&unixtime);
|
||||
|
||||
strftime(buffer, 128, dateFormat.c_str(), timeData);
|
||||
return ByteString(buffer);
|
||||
|
@ -11,7 +11,7 @@ namespace format
|
||||
{
|
||||
ByteString URLEncode(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);
|
||||
String CleanString(String dirtyString, bool ascii, bool color, bool newlines, bool numeric = false);
|
||||
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 "common/tpt-minmax.h"
|
||||
#include "common/String.h"
|
||||
#include <cstring>
|
||||
#include <sys/types.h>
|
||||
#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
|
||||
{
|
||||
@ -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)
|
||||
{
|
||||
return str.size() == size && !memcmp(str.data(), data, size);
|
||||
|
63
src/Misc.h
63
src/Misc.h
@ -5,25 +5,6 @@
|
||||
#include <cstddef>
|
||||
#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
|
||||
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;
|
||||
}
|
||||
|
||||
inline int iabs(int i)
|
||||
{
|
||||
return i * isign(i);
|
||||
}
|
||||
|
||||
inline unsigned clamp_flt(float f, float min, float max)
|
||||
{
|
||||
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 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;
|
||||
|
||||
bool byteStringEqualsString(const ByteString &str, const char *data, size_t size);
|
||||
|
@ -7,22 +7,17 @@
|
||||
#include "client/SaveFile.h"
|
||||
#include "client/SaveInfo.h"
|
||||
#include "client/http/requestmanager/RequestManager.h"
|
||||
#include "client/http/GetSaveRequest.h"
|
||||
#include "client/http/GetSaveDataRequest.h"
|
||||
#include "common/platform/Platform.h"
|
||||
#include "graphics/Graphics.h"
|
||||
#include "simulation/SaveRenderer.h"
|
||||
#include "simulation/SimulationData.h"
|
||||
#include "common/tpt-rand.h"
|
||||
#include "gui/game/Favorite.h"
|
||||
#include "gui/Style.h"
|
||||
#include "gui/game/GameController.h"
|
||||
#include "gui/game/GameView.h"
|
||||
#include "gui/game/IntroText.h"
|
||||
#include "gui/dialogues/ConfirmPrompt.h"
|
||||
#include "gui/dialogues/ErrorMessage.h"
|
||||
#include "gui/interface/Engine.h"
|
||||
#include "gui/interface/TextWrapper.h"
|
||||
#include "Config.h"
|
||||
#include "SimulationConfig.h"
|
||||
#include <optional>
|
||||
@ -30,8 +25,6 @@
|
||||
#include <iostream>
|
||||
#include <csignal>
|
||||
#include <SDL.h>
|
||||
#include <exception>
|
||||
#include <cstdlib>
|
||||
|
||||
void LoadWindowPosition()
|
||||
{
|
||||
@ -85,14 +78,14 @@ void SaveWindowPosition()
|
||||
void LargeScreenDialog()
|
||||
{
|
||||
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 << 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.";
|
||||
new ConfirmPrompt("Large screen detected", message.Build(), { nullptr, []() {
|
||||
if (!ConfirmPrompt::Blocking("Large screen detected", message.Build()))
|
||||
{
|
||||
GlobalPrefs::Ref().Set("Scale", 1);
|
||||
ui::Engine::Ref().windowFrameOps.scale = 1;
|
||||
} });
|
||||
ui::Engine::Ref().SetScale(1);
|
||||
}
|
||||
}
|
||||
|
||||
void TickClient()
|
||||
@ -100,115 +93,50 @@ void TickClient()
|
||||
Client::Ref().Tick();
|
||||
}
|
||||
|
||||
static void BlueScreen(String detailMessage, std::optional<std::vector<String>> stackTrace)
|
||||
void BlueScreen(String detailMessage)
|
||||
{
|
||||
auto &engine = ui::Engine::Ref();
|
||||
engine.g->BlendFilledRect(engine.g->Size().OriginRect(), 0x1172A9_rgb .WithAlpha(0xD2));
|
||||
|
||||
auto crashPrevLogPath = ByteString("crash.prev.log");
|
||||
auto crashLogPath = ByteString("crash.log");
|
||||
Platform::RenameFile(crashLogPath, crashPrevLogPath, true);
|
||||
String errorTitle = "ERROR";
|
||||
String errorDetails = "Details: " + detailMessage;
|
||||
String errorHelp = String("An unrecoverable fault has occurred, please report the error by visiting the website below\n") + SCHEME + SERVER;
|
||||
|
||||
StringBuilder crashInfo;
|
||||
crashInfo << "ERROR - Details: " << detailMessage << "\n";
|
||||
crashInfo << "An unrecoverable fault has occurred, please report it by visiting the website below\n\n " << SCHEME << SERVER << "\n\n";
|
||||
crashInfo << "An attempt will be made to save all of this information to " << crashLogPath.FromUtf8() << " in your data folder.\n";
|
||||
crashInfo << "Please attach this file to your report.\n\n";
|
||||
crashInfo << "Version: " << VersionInfo().FromUtf8() << "\n";
|
||||
crashInfo << "Tag: " << VCS_TAG << "\n";
|
||||
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);
|
||||
// We use the width of errorHelp to center, but heights of the individual texts for vertical spacing
|
||||
auto pos = engine.g->Size() / 2 - Vec2(Graphics::TextSize(errorHelp).X / 2, 100);
|
||||
engine.g->BlendText(pos, errorTitle, 0xFFFFFF_rgb .WithAlpha(0xFF));
|
||||
pos.Y += 4 + Graphics::TextSize(errorTitle).Y;
|
||||
engine.g->BlendText(pos, errorDetails, 0xFFFFFF_rgb .WithAlpha(0xFF));
|
||||
pos.Y += 4 + Graphics::TextSize(errorDetails).Y;
|
||||
engine.g->BlendText(pos, errorHelp, 0xFFFFFF_rgb .WithAlpha(0xFF));
|
||||
|
||||
//Death loop
|
||||
SDL_Event event;
|
||||
auto running = true;
|
||||
while (running)
|
||||
while(true)
|
||||
{
|
||||
while (SDL_PollEvent(&event))
|
||||
{
|
||||
if (event.type == SDL_QUIT)
|
||||
{
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
if(event.type == SDL_QUIT)
|
||||
exit(-1); // Don't use Platform::Exit, we're practically zombies at this point anyway.
|
||||
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;
|
||||
const char *message;
|
||||
} signalMessages[] = {
|
||||
{ SIGSEGV, "Memory read/write error" },
|
||||
{ SIGFPE, "Floating point exception" },
|
||||
{ SIGILL, "Program execution exception" },
|
||||
{ SIGABRT, "Unexpected program abort" },
|
||||
{ 0, nullptr },
|
||||
};
|
||||
|
||||
static void SigHandler(int signal)
|
||||
{
|
||||
const char *message = "Unknown signal";
|
||||
for (auto *msg = signalMessages; msg->message; ++msg)
|
||||
{
|
||||
if (msg->sig == signal)
|
||||
{
|
||||
message = msg->message;
|
||||
break;
|
||||
}
|
||||
switch(signal){
|
||||
case SIGSEGV:
|
||||
BlueScreen("Memory read/write error");
|
||||
break;
|
||||
case SIGFPE:
|
||||
BlueScreen("Floating point exception");
|
||||
break;
|
||||
case SIGILL:
|
||||
BlueScreen("Program execution exception");
|
||||
break;
|
||||
case SIGABRT:
|
||||
BlueScreen("Unexpected program abort");
|
||||
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;
|
||||
@ -236,28 +164,17 @@ struct ExplicitSingletons
|
||||
http::RequestManagerPtr requestManager;
|
||||
std::unique_ptr<Client> client;
|
||||
std::unique_ptr<SaveRenderer> saveRenderer;
|
||||
std::unique_ptr<RNG> rng;
|
||||
std::unique_ptr<Favorite> favorite;
|
||||
std::unique_ptr<ui::Engine> engine;
|
||||
std::unique_ptr<SimulationData> simulationData;
|
||||
std::unique_ptr<GameController> gameController;
|
||||
};
|
||||
static std::unique_ptr<ExplicitSingletons> explicitSingletons;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
Platform::SetupCrt();
|
||||
return Platform::InvokeMain(argc, argv);
|
||||
}
|
||||
|
||||
int Main(int argc, char *argv[])
|
||||
{
|
||||
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();
|
||||
explicitSingletons.reset();
|
||||
});
|
||||
@ -323,38 +240,35 @@ int Main(int argc, char *argv[])
|
||||
}
|
||||
else
|
||||
{
|
||||
auto ddir = Platform::DefaultDdir();
|
||||
char *ddir = SDL_GetPrefPath(NULL, APPDATA);
|
||||
if (!Platform::FileExists("powder.pref"))
|
||||
{
|
||||
if (ddir.size())
|
||||
if (ddir)
|
||||
{
|
||||
if (!Platform::ChangeDir(ddir))
|
||||
{
|
||||
perror("failed to chdir to default ddir");
|
||||
ddir = {};
|
||||
SDL_free(ddir);
|
||||
ddir = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ddir.size())
|
||||
if (ddir)
|
||||
{
|
||||
Platform::sharedCwd = ddir;
|
||||
SDL_free(ddir);
|
||||
}
|
||||
}
|
||||
// We're now in the correct directory, time to get prefs.
|
||||
explicitSingletons->globalPrefs = std::make_unique<GlobalPrefs>();
|
||||
|
||||
auto &prefs = GlobalPrefs::Ref();
|
||||
|
||||
WindowFrameOps windowFrameOps{
|
||||
prefs.Get("Scale", 1),
|
||||
prefs.Get("Resizable", false),
|
||||
prefs.Get("Fullscreen", false),
|
||||
prefs.Get("AltFullscreen", false),
|
||||
prefs.Get("ForceIntegerScaling", true),
|
||||
prefs.Get("BlurryScaling", false),
|
||||
};
|
||||
auto graveExitsConsole = prefs.Get("GraveExitsConsole", true);
|
||||
scale = prefs.Get("Scale", 1);
|
||||
resizable = prefs.Get("Resizable", false);
|
||||
fullscreen = prefs.Get("Fullscreen", false);
|
||||
altFullscreen = prefs.Get("AltFullscreen", false);
|
||||
forceIntegerScaling = prefs.Get("ForceIntegerScaling", true);
|
||||
momentumScroll = prefs.Get("MomentumScroll", true);
|
||||
showAvatars = prefs.Get("ShowAvatars", true);
|
||||
|
||||
@ -375,8 +289,8 @@ int Main(int argc, char *argv[])
|
||||
auto kioskArg = arguments["kiosk"];
|
||||
if (kioskArg.has_value())
|
||||
{
|
||||
windowFrameOps.fullscreen = true_string(kioskArg.value());
|
||||
prefs.Set("Fullscreen", windowFrameOps.fullscreen);
|
||||
fullscreen = true_string(kioskArg.value());
|
||||
prefs.Set("Fullscreen", fullscreen);
|
||||
}
|
||||
|
||||
if (true_arg(arguments["redirect"]))
|
||||
@ -394,8 +308,8 @@ int Main(int argc, char *argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
windowFrameOps.scale = scaleArg.value().ToNumber<int>();
|
||||
prefs.Set("Scale", windowFrameOps.scale);
|
||||
scale = scaleArg.value().ToNumber<int>();
|
||||
prefs.Set("Scale", scale);
|
||||
}
|
||||
catch (const std::runtime_error &e)
|
||||
{
|
||||
@ -430,45 +344,49 @@ int Main(int argc, char *argv[])
|
||||
Client::Ref().Initialize();
|
||||
|
||||
explicitSingletons->saveRenderer = std::make_unique<SaveRenderer>();
|
||||
explicitSingletons->rng = std::make_unique<RNG>();
|
||||
explicitSingletons->favorite = std::make_unique<Favorite>();
|
||||
explicitSingletons->engine = std::make_unique<ui::Engine>();
|
||||
|
||||
// TODO: maybe bind the maximum allowed scale to screen size somehow
|
||||
if(windowFrameOps.scale < 1 || windowFrameOps.scale > SCALE_MAXIMUM)
|
||||
windowFrameOps.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;
|
||||
if(scale < 1 || scale > SCALE_MAXIMUM)
|
||||
scale = 1;
|
||||
|
||||
SDLOpen();
|
||||
|
||||
if (Client::Ref().IsFirstRun() && FORCE_WINDOW_FRAME_OPS == forceWindowFrameOpsNone)
|
||||
if (Client::Ref().IsFirstRun())
|
||||
{
|
||||
auto guessed = GuessBestScale();
|
||||
if (engine.windowFrameOps.scale != guessed)
|
||||
scale = GuessBestScale();
|
||||
if (scale > 1)
|
||||
{
|
||||
engine.windowFrameOps.scale = guessed;
|
||||
prefs.Set("Scale", windowFrameOps.scale);
|
||||
prefs.Set("Scale", scale);
|
||||
SDL_SetWindowSize(sdl_window, WINDOWW * scale, WINDOWH * scale);
|
||||
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)
|
||||
{
|
||||
//Get ready to catch any dodgy errors
|
||||
for (auto *msg = signalMessages; msg->message; ++msg)
|
||||
{
|
||||
signal(msg->sig, SigHandler);
|
||||
}
|
||||
std::set_terminate(TerminateHandler);
|
||||
signal(SIGSEGV, SigHandler);
|
||||
signal(SIGFPE, SigHandler);
|
||||
signal(SIGILL, SigHandler);
|
||||
signal(SIGABRT, SigHandler);
|
||||
}
|
||||
|
||||
if constexpr (X86)
|
||||
@ -476,97 +394,114 @@ int Main(int argc, char *argv[])
|
||||
X86KillDenormals();
|
||||
}
|
||||
|
||||
explicitSingletons->simulationData = std::make_unique<SimulationData>();
|
||||
explicitSingletons->gameController = std::make_unique<GameController>();
|
||||
auto *gameController = explicitSingletons->gameController.get();
|
||||
engine.ShowWindow(gameController->GetView());
|
||||
auto wrapWithBluescreen = [&]() {
|
||||
explicitSingletons->gameController = std::make_unique<GameController>();
|
||||
auto *gameController = explicitSingletons->gameController.get();
|
||||
engine.ShowWindow(gameController->GetView());
|
||||
|
||||
auto openArg = arguments["open"];
|
||||
if (openArg.has_value())
|
||||
{
|
||||
if constexpr (DEBUG)
|
||||
auto openArg = arguments["open"];
|
||||
if (openArg.has_value())
|
||||
{
|
||||
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
|
||||
{
|
||||
std::vector<char> gameSaveData;
|
||||
if (!Platform::ReadFile(gameSaveData, openArg.value()))
|
||||
ByteString saveIdPart;
|
||||
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
|
||||
{
|
||||
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));
|
||||
}
|
||||
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;
|
||||
}
|
||||
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)
|
||||
{
|
||||
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"];
|
||||
if (ptsaveArg.has_value())
|
||||
EngineProcess();
|
||||
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
|
||||
{
|
||||
ByteString saveIdPart;
|
||||
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);
|
||||
wrapWithBluescreen();
|
||||
}
|
||||
catch (std::exception & e)
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
new ErrorMessage("Error", ByteString(e.what()).FromUtf8());
|
||||
Platform::MarkPresentable();
|
||||
BlueScreen(ByteString(e.what()).FromUtf8());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Platform::MarkPresentable();
|
||||
wrapWithBluescreen();
|
||||
}
|
||||
|
||||
MainLoop();
|
||||
|
||||
Platform::Exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ void TickClient()
|
||||
struct ExplicitSingletons
|
||||
{
|
||||
// These need to be listed in the order they are populated in main.
|
||||
std::unique_ptr<RNG> rng;
|
||||
std::unique_ptr<ui::Engine> engine;
|
||||
};
|
||||
static std::unique_ptr<ExplicitSingletons> explicitSingletons;
|
||||
@ -41,31 +42,40 @@ int main(int argc, char * argv[])
|
||||
});
|
||||
explicitSingletons = std::make_unique<ExplicitSingletons>();
|
||||
|
||||
WindowFrameOps windowFrameOps;
|
||||
scale = 1;
|
||||
if (argc >= 3)
|
||||
{
|
||||
std::istringstream ss(argv[2]);
|
||||
int 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
|
||||
if (windowFrameOps.scale < 1 || windowFrameOps.scale > 10)
|
||||
{
|
||||
windowFrameOps.scale = 1;
|
||||
}
|
||||
if(scale < 1 || scale > 10)
|
||||
scale = 1;
|
||||
|
||||
explicitSingletons->rng = std::make_unique<RNG>();
|
||||
explicitSingletons->engine = std::make_unique<ui::Engine>();
|
||||
|
||||
auto &engine = ui::Engine::Ref();
|
||||
engine.g = new Graphics();
|
||||
engine.windowFrameOps = windowFrameOps;
|
||||
|
||||
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.SetFastQuit(true);
|
||||
|
||||
@ -75,14 +85,11 @@ int main(int argc, char * argv[])
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "path to font.bz2 not supplied" << std::endl;
|
||||
std::cerr << "path to font.cpp not supplied" << std::endl;
|
||||
Platform::Exit(1);
|
||||
}
|
||||
|
||||
while (engine.Running())
|
||||
{
|
||||
EngineProcess();
|
||||
}
|
||||
EngineProcess();
|
||||
Platform::Exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
@ -6,7 +6,6 @@
|
||||
#include "gui/interface/Engine.h"
|
||||
#include "client/GameSave.h"
|
||||
#include "simulation/Simulation.h"
|
||||
#include "simulation/SimulationData.h"
|
||||
#include "common/platform/Platform.h"
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
@ -22,18 +21,16 @@ int main(int argc, char *argv[])
|
||||
auto inputFilename = ByteString(argv[1]);
|
||||
auto outputFilename = ByteString(argv[2]) + ".png";
|
||||
|
||||
auto simulationData = std::make_unique<SimulationData>();
|
||||
|
||||
std::vector<char> fileData;
|
||||
if (!Platform::ReadFile(fileData, inputFilename))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::unique_ptr<GameSave> gameSave;
|
||||
GameSave * gameSave = NULL;
|
||||
try
|
||||
{
|
||||
gameSave = std::make_unique<GameSave>(fileData, false);
|
||||
gameSave = new GameSave(fileData, false);
|
||||
}
|
||||
catch (ParseException &e)
|
||||
{
|
||||
@ -42,12 +39,13 @@ int main(int argc, char *argv[])
|
||||
throw e;
|
||||
}
|
||||
|
||||
auto rng = std::make_unique<RNG>();
|
||||
Simulation * sim = new Simulation();
|
||||
Renderer * ren = new Renderer(sim);
|
||||
|
||||
if (gameSave)
|
||||
{
|
||||
sim->Load(gameSave.get(), true, { 0, 0 });
|
||||
sim->Load(gameSave, true);
|
||||
|
||||
//Render save
|
||||
ren->decorations_enable = true;
|
||||
@ -64,10 +62,9 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
else
|
||||
{
|
||||
ren->clearScreen();
|
||||
int w = Graphics::TextSize("Save file invalid").X + 15, x = (XRES-w)/2, y = (YRES-24)/2;
|
||||
ren->DrawRect(RectSized(Vec2{ x, y }, Vec2{ w, 24 }), 0xC0C0C0_rgb);
|
||||
ren->BlendText({ x+8, y+8 }, "Save file invalid", 0xC0C0F0_rgb .WithAlpha(255));
|
||||
int w = Graphics::textwidth("Save file invalid")+16, x = (XRES-w)/2, y = (YRES-24)/2;
|
||||
ren->drawrect(x, y, w, 24, 192, 192, 192, 255);
|
||||
ren->drawtext(x+8, y+8, "Save file invalid", 192, 192, 240, 255);
|
||||
}
|
||||
|
||||
ren->RenderBegin();
|
||||
|
@ -5,7 +5,6 @@
|
||||
#include "gui/interface/Engine.h"
|
||||
#include "graphics/Graphics.h"
|
||||
#include "common/platform/Platform.h"
|
||||
#include "common/clipboard/Clipboard.h"
|
||||
#include <iostream>
|
||||
|
||||
int desktopWidth = 1280;
|
||||
@ -13,8 +12,11 @@ int desktopHeight = 1024;
|
||||
SDL_Window *sdl_window = NULL;
|
||||
SDL_Renderer *sdl_renderer = NULL;
|
||||
SDL_Texture *sdl_texture = NULL;
|
||||
bool vsyncHint = false;
|
||||
WindowFrameOps currentFrameOps;
|
||||
int scale = 1;
|
||||
bool fullscreen = false;
|
||||
bool altFullscreen = false;
|
||||
bool forceIntegerScaling = true;
|
||||
bool resizable = false;
|
||||
bool momentumScroll = true;
|
||||
bool showAvatars = true;
|
||||
uint64_t lastTick = 0;
|
||||
@ -26,10 +28,6 @@ int mouseButton = 0;
|
||||
bool mouseDown = false;
|
||||
bool calculatedInitialMouse = false;
|
||||
bool hasMouseMoved = false;
|
||||
double correctedFrameTimeAvg = 0;
|
||||
uint64_t drawingTimer = 0;
|
||||
uint64_t frameStart = 0;
|
||||
uint64_t oldFrameStart = 0;
|
||||
|
||||
void StartTextInput()
|
||||
{
|
||||
@ -43,24 +41,11 @@ void StopTextInput()
|
||||
|
||||
void SetTextInputRect(int x, int y, int w, int h)
|
||||
{
|
||||
// Why does SDL_SetTextInputRect not take logical coordinates???
|
||||
SDL_Rect rect;
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 18)
|
||||
int wx, wy, wwx, why;
|
||||
SDL_RenderLogicalToWindow(sdl_renderer, float(x), float(y), &wx, &wy);
|
||||
SDL_RenderLogicalToWindow(sdl_renderer, float(x + w), float(y + h), &wwx, &why);
|
||||
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
|
||||
rect.x = x;
|
||||
rect.y = y;
|
||||
rect.w = w;
|
||||
rect.h = h;
|
||||
SDL_SetTextInputRect(&rect);
|
||||
}
|
||||
|
||||
@ -84,7 +69,7 @@ unsigned int GetTicks()
|
||||
return SDL_GetTicks();
|
||||
}
|
||||
|
||||
static void CalculateMousePosition(int *x, int *y)
|
||||
void CalculateMousePosition(int *x, int *y)
|
||||
{
|
||||
int globalMx, globalMy;
|
||||
SDL_GetGlobalMouseState(&globalMx, &globalMy);
|
||||
@ -92,16 +77,16 @@ static void CalculateMousePosition(int *x, int *y)
|
||||
SDL_GetWindowPosition(sdl_window, &windowX, &windowY);
|
||||
|
||||
if (x)
|
||||
*x = (globalMx - windowX) / currentFrameOps.scale;
|
||||
*x = (globalMx - windowX) / scale;
|
||||
if (y)
|
||||
*y = (globalMy - windowY) / currentFrameOps.scale;
|
||||
*y = (globalMy - windowY) / scale;
|
||||
}
|
||||
|
||||
void blit(pixel *vid)
|
||||
{
|
||||
SDL_UpdateTexture(sdl_texture, NULL, vid, WINDOWW * sizeof (Uint32));
|
||||
// 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_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL);
|
||||
SDL_RenderPresent(sdl_renderer);
|
||||
@ -114,9 +99,12 @@ void SDLOpen()
|
||||
fprintf(stderr, "Initializing SDL (video subsystem): %s\n", SDL_GetError());
|
||||
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);
|
||||
if (displayIndex >= 0)
|
||||
@ -129,7 +117,10 @@ void SDLOpen()
|
||||
}
|
||||
}
|
||||
|
||||
StopTextInput();
|
||||
if constexpr (SET_WINDOW_ICON)
|
||||
{
|
||||
WindowIcon(sdl_window);
|
||||
}
|
||||
}
|
||||
|
||||
void SDLClose()
|
||||
@ -148,150 +139,105 @@ void SDLClose()
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void SDLSetScreen()
|
||||
void SDLSetScreen(int scale_, bool resizable_, bool fullscreen_, bool altFullscreen_, bool forceIntegerScaling_)
|
||||
{
|
||||
auto newFrameOps = ui::Engine::Ref().windowFrameOps;
|
||||
auto newVsyncHint = std::holds_alternative<FpsLimitVsync>(ui::Engine::Ref().GetFpsLimit());
|
||||
if (FORCE_WINDOW_FRAME_OPS == forceWindowFrameOpsEmbedded)
|
||||
{
|
||||
newFrameOps.resizable = false;
|
||||
newFrameOps.fullscreen = false;
|
||||
newFrameOps.changeResolution = false;
|
||||
newFrameOps.forceIntegerScaling = false;
|
||||
}
|
||||
if (FORCE_WINDOW_FRAME_OPS == forceWindowFrameOpsHandheld)
|
||||
{
|
||||
newFrameOps.resizable = false;
|
||||
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))
|
||||
// bool changingScale = scale != scale_;
|
||||
bool changingFullscreen = fullscreen_ != fullscreen || (altFullscreen_ != altFullscreen && fullscreen);
|
||||
bool changingResizable = resizable != resizable_;
|
||||
scale = scale_;
|
||||
fullscreen = fullscreen_;
|
||||
altFullscreen = altFullscreen_;
|
||||
resizable = resizable_;
|
||||
forceIntegerScaling = forceIntegerScaling_;
|
||||
// Recreate the window when toggling fullscreen, due to occasional issues
|
||||
// Also recreate it when enabling resizable windows, to fix bugs on windows,
|
||||
// see https://github.com/jacob1/The-Powder-Toy/issues/24
|
||||
if (changingFullscreen || altFullscreen || (changingResizable && resizable && !fullscreen))
|
||||
{
|
||||
RecreateWindow();
|
||||
return;
|
||||
}
|
||||
if (changingResizable)
|
||||
SDL_RestoreWindow(sdl_window);
|
||||
|
||||
auto size = WINDOW * newFrameOpsNorm.scale;
|
||||
if (sdl_window && newFrameOpsNorm.resizable)
|
||||
{
|
||||
SDL_GetWindowSize(sdl_window, &size.X, &size.Y);
|
||||
}
|
||||
|
||||
if (recreate)
|
||||
{
|
||||
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_SetWindowSize(sdl_window, WINDOWW * scale, WINDOWH * scale);
|
||||
SDL_RenderSetIntegerScale(sdl_renderer, forceIntegerScaling && fullscreen ? SDL_TRUE : SDL_FALSE);
|
||||
unsigned int flags = 0;
|
||||
if (fullscreen)
|
||||
flags = altFullscreen ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||
SDL_SetWindowFullscreen(sdl_window, flags);
|
||||
if (fullscreen)
|
||||
SDL_RaiseWindow(sdl_window);
|
||||
Clipboard::RecreateWindow();
|
||||
}
|
||||
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;
|
||||
SDL_SetWindowResizable(sdl_window, resizable ? SDL_TRUE : SDL_FALSE);
|
||||
}
|
||||
|
||||
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();
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_QUIT:
|
||||
if (ALLOW_QUIT && (engine.GetFastQuit() || engine.CloseWindow()))
|
||||
{
|
||||
if (engine.GetFastQuit() || engine.CloseWindow())
|
||||
engine.Exit();
|
||||
}
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
if (SDL_GetModState() & KMOD_GUI)
|
||||
{
|
||||
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();
|
||||
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);
|
||||
@ -349,7 +295,7 @@ static void EventProcess(const SDL_Event &event)
|
||||
mousey = event.button.y;
|
||||
}
|
||||
mouseButton = event.button.button;
|
||||
engine.onMouseDown(mousex, mousey, mouseButton);
|
||||
engine.onMouseClick(mousex, mousey, mouseButton);
|
||||
|
||||
mouseDown = true;
|
||||
if constexpr (!DEBUG)
|
||||
@ -365,7 +311,7 @@ static void EventProcess(const SDL_Event &event)
|
||||
mousey = event.button.y;
|
||||
}
|
||||
mouseButton = event.button.button;
|
||||
engine.onMouseUp(mousex, mousey, mouseButton);
|
||||
engine.onMouseUnclick(mousex, mousey, mouseButton);
|
||||
|
||||
mouseDown = false;
|
||||
if constexpr (!DEBUG)
|
||||
@ -395,52 +341,75 @@ static void EventProcess(const SDL_Event &event)
|
||||
|
||||
void EngineProcess()
|
||||
{
|
||||
auto &engine = ui::Engine::Ref();
|
||||
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();
|
||||
}
|
||||
|
||||
double correctedFrameTimeAvg = 0;
|
||||
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();
|
||||
int drawcap = ui::Engine::Ref().GetDrawingFrequencyLimit();
|
||||
if (!drawcap || drawingTimer > 1e9f / drawcap)
|
||||
auto &engine = ui::Engine::Ref();
|
||||
while(engine.Running())
|
||||
{
|
||||
engine.Draw();
|
||||
drawingTimer = 0;
|
||||
SDLSetScreen();
|
||||
blit(engine.g->Data());
|
||||
if(engine.Broken()) { engine.UnBreak(); break; }
|
||||
event.type = 0;
|
||||
while (SDL_PollEvent(&event))
|
||||
{
|
||||
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);
|
||||
oldFrameStart = frameStart;
|
||||
frameStart = now;
|
||||
if (auto *fpsLimitExplicit = std::get_if<FpsLimitExplicit>(&fpsLimit))
|
||||
if constexpr (DEBUG)
|
||||
{
|
||||
auto timeBlockDuration = uint64_t(UINT64_C(1'000'000'000) / fpsLimitExplicit->value);
|
||||
auto oldFrameStartTimeBlock = oldFrameStart / timeBlockDuration;
|
||||
auto frameStartTimeBlock = oldFrameStartTimeBlock + 1U;
|
||||
frameStart = std::max(frameStart, frameStartTimeBlock * timeBlockDuration);
|
||||
SDL_Delay((frameStart - now) / UINT64_C(1'000'000));
|
||||
std::cout << "Breaking out of EngineProcess" << std::endl;
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,19 @@
|
||||
#pragma once
|
||||
#include "common/String.h"
|
||||
#include "graphics/Pixel.h"
|
||||
#include "gui/WindowFrameOps.h"
|
||||
#include "FpsLimit.h"
|
||||
#include <cstdint>
|
||||
#include <SDL.h>
|
||||
#include <variant>
|
||||
|
||||
extern int desktopWidth;
|
||||
extern int desktopHeight;
|
||||
extern SDL_Window *sdl_window;
|
||||
extern SDL_Renderer *sdl_renderer;
|
||||
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 showAvatars;
|
||||
extern uint64_t lastTick;
|
||||
@ -24,7 +26,6 @@ extern bool mouseDown;
|
||||
extern bool calculatedInitialMouse;
|
||||
extern bool hasMouseMoved;
|
||||
|
||||
void MainLoop();
|
||||
void EngineProcess();
|
||||
void StartTextInput();
|
||||
void StopTextInput();
|
||||
@ -33,13 +34,14 @@ void ClipboardPush(ByteString text);
|
||||
ByteString ClipboardPull();
|
||||
int GetModifiers();
|
||||
unsigned int GetTicks();
|
||||
void CalculateMousePosition(int *x, int *y);
|
||||
void blit(pixel *vid);
|
||||
void SDLOpen();
|
||||
void SDLClose();
|
||||
void SDLSetScreen();
|
||||
void SetFpsLimit(FpsLimit newFpsLimit);
|
||||
void SDLSetScreen(int scale_, bool resizable_, bool fullscreen_, bool altFullscreen_, bool forceIntegerScaling_);
|
||||
bool RecreateWindow();
|
||||
void LoadWindowPosition();
|
||||
void SaveWindowPosition();
|
||||
void LargeScreenDialog();
|
||||
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 float CFDS = 4.0f / CELL;
|
||||
constexpr float MAX_VELOCITY = 1e4f;
|
||||
constexpr float SIM_MAXVELOCITY = 1e4f;
|
||||
|
||||
//Air constants
|
||||
constexpr float AIR_TSTEPP = 0.3f;
|
||||
@ -42,13 +42,10 @@ constexpr float AIR_PLOSS = 0.9999f;
|
||||
|
||||
constexpr int NGOL = 24;
|
||||
|
||||
enum DefaultBrushes
|
||||
{
|
||||
BRUSH_CIRCLE,
|
||||
BRUSH_SQUARE,
|
||||
BRUSH_TRIANGLE,
|
||||
NUM_DEFAULTBRUSHES,
|
||||
};
|
||||
constexpr int CIRCLE_BRUSH = 0;
|
||||
constexpr int SQUARE_BRUSH = 1;
|
||||
constexpr int TRI_BRUSH = 2;
|
||||
constexpr int BRUSH_NUM = 3;
|
||||
|
||||
//Photon constants
|
||||
constexpr int SURF_RANGE = 10;
|
||||
@ -64,5 +61,3 @@ constexpr float GLASS_IOR = 1.9f;
|
||||
constexpr float GLASS_DISP = 0.07f;
|
||||
|
||||
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
|
||||
#include "common/tpt-inline.h"
|
||||
#include <ctime>
|
||||
#include <cstdlib>
|
||||
#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) )
|
||||
//#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;
|
||||
char *out = ( char * )outp;
|
||||
|
||||
@ -1196,7 +1197,7 @@ static inline void bson_swap_endian64( void *outp, const void *inp ) {
|
||||
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;
|
||||
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 "prefs/GlobalPrefs.h"
|
||||
#include "client/http/StartupRequest.h"
|
||||
#include "client/http/Request.h"
|
||||
#include "ClientListener.h"
|
||||
#include "Format.h"
|
||||
#include "MD5.h"
|
||||
#include "client/GameSave.h"
|
||||
#include "client/SaveFile.h"
|
||||
#include "client/SaveInfo.h"
|
||||
@ -10,9 +11,9 @@
|
||||
#include "common/platform/Platform.h"
|
||||
#include "common/String.h"
|
||||
#include "graphics/Graphics.h"
|
||||
#include "gui/dialogues/ErrorMessage.h"
|
||||
#include "prefs/Prefs.h"
|
||||
#include "lua/CommandInterface.h"
|
||||
#include "gui/preview/Comment.h"
|
||||
#include "Config.h"
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
@ -28,12 +29,27 @@
|
||||
|
||||
Client::Client():
|
||||
messageOfTheDay("Fetching the message of the day..."),
|
||||
versionCheckRequest(nullptr),
|
||||
alternateVersionCheckRequest(nullptr),
|
||||
usingAltUpdateServer(false),
|
||||
updateAvailable(false),
|
||||
authUser(0, "")
|
||||
{
|
||||
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(""));
|
||||
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();
|
||||
}
|
||||
|
||||
@ -72,14 +88,24 @@ void Client::Initialize()
|
||||
}
|
||||
|
||||
//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();
|
||||
|
||||
if constexpr (USE_UPDATESERVER)
|
||||
{
|
||||
// use an alternate update server
|
||||
alternateVersionCheckRequest = std::make_unique<http::StartupRequest>(true);
|
||||
alternateVersionCheckRequest->Start();
|
||||
alternateVersionCheckRequest = std::make_unique<http::Request>(ByteString::Build(SCHEME, UPDATESERVER, "/Startup.json"));
|
||||
usingAltUpdateServer = true;
|
||||
if (authUser.UserID)
|
||||
{
|
||||
alternateVersionCheckRequest->AuthHeaders(authUser.Username, "");
|
||||
}
|
||||
alternateVersionCheckRequest->Start();
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,82 +125,203 @@ String Client::GetMessageOfTheDay()
|
||||
return messageOfTheDay;
|
||||
}
|
||||
|
||||
void Client::AddServerNotification(ServerNotification notification)
|
||||
void Client::AddServerNotification(std::pair<String, ByteString> notification)
|
||||
{
|
||||
serverNotifications.push_back(notification);
|
||||
notifyNewNotification(notification);
|
||||
}
|
||||
|
||||
std::vector<ServerNotification> Client::GetServerNotifications()
|
||||
std::vector<std::pair<String, ByteString> > Client::GetServerNotifications()
|
||||
{
|
||||
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()
|
||||
{
|
||||
auto applyUpdateInfo = false;
|
||||
if (versionCheckRequest && versionCheckRequest->CheckDone())
|
||||
CheckUpdate(versionCheckRequest, true);
|
||||
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();
|
||||
if (!info.sessionGood)
|
||||
//free(data);
|
||||
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;
|
||||
applyUpdateInfo = true;
|
||||
SetMessageOfTheDay(info.messageOfTheDay);
|
||||
}
|
||||
for (auto ¬ification : info.notifications)
|
||||
{
|
||||
AddServerNotification(notification);
|
||||
//Do nothing
|
||||
}
|
||||
}
|
||||
catch (const http::RequestError &ex)
|
||||
{
|
||||
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();
|
||||
}
|
||||
updateRequest.reset();
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<UpdateInfo> Client::GetUpdateInfo()
|
||||
UpdateInfo Client::GetUpdateInfo()
|
||||
{
|
||||
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)
|
||||
{
|
||||
@ -235,7 +382,31 @@ Client::~Client()
|
||||
void Client::SetAuthUser(User 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();
|
||||
}
|
||||
|
||||
@ -244,6 +415,65 @@ User Client::GetAuthUser()
|
||||
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)
|
||||
{
|
||||
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"));
|
||||
auto saveFile = LoadSaveFile(stampFile);
|
||||
SaveFile *saveFile = LoadSaveFile(stampFile);
|
||||
if (!saveFile)
|
||||
saveFile = LoadSaveFile(stampID);
|
||||
else
|
||||
@ -287,28 +517,7 @@ void Client::DeleteStamp(ByteString stampID)
|
||||
}
|
||||
}
|
||||
|
||||
void Client::RenameStamp(ByteString stampID, ByteString newName)
|
||||
{
|
||||
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)
|
||||
ByteString Client::AddStamp(GameSave * saveData)
|
||||
{
|
||||
auto now = (uint64_t)time(NULL);
|
||||
if (lastStampTime != now)
|
||||
@ -346,8 +555,8 @@ ByteString Client::AddStamp(std::unique_ptr<GameSave> saveData)
|
||||
}
|
||||
saveData->authors = stampInfo;
|
||||
|
||||
std::vector<char> gameData;
|
||||
std::tie(std::ignore, gameData) = saveData->Serialise();
|
||||
auto [ fromNewerVersion, gameData ] = saveData->Serialise();
|
||||
(void)fromNewerVersion;
|
||||
if (!gameData.size())
|
||||
return "";
|
||||
|
||||
@ -377,6 +586,7 @@ void Client::RescanStamps()
|
||||
newStampIDs.push_back(stampID);
|
||||
}
|
||||
}
|
||||
auto oldCount = newStampIDs.size();
|
||||
auto stampIDsSet = std::set<ByteString>(stampIDs.begin(), stampIDs.end());
|
||||
for (auto &stampID : stampFilesSet)
|
||||
{
|
||||
@ -388,6 +598,8 @@ void Client::RescanStamps()
|
||||
}
|
||||
if (changed)
|
||||
{
|
||||
// Move newly discovered stamps to front.
|
||||
std::rotate(newStampIDs.begin(), newStampIDs.begin() + oldCount, newStampIDs.end());
|
||||
stampIDs = newStampIDs;
|
||||
WriteStamps();
|
||||
}
|
||||
@ -406,19 +618,325 @@ const std::vector<ByteString> &Client::GetStamps() const
|
||||
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;
|
||||
std::unique_ptr<SaveFile> file;
|
||||
SaveFile *file = nullptr;
|
||||
if (Platform::FileExists(filename))
|
||||
{
|
||||
file = std::make_unique<SaveFile>(filename);
|
||||
file = new SaveFile(filename);
|
||||
try
|
||||
{
|
||||
std::vector<char> data;
|
||||
if (Platform::ReadFile(data, filename))
|
||||
{
|
||||
file->SetGameSave(std::make_unique<GameSave>(std::move(data)));
|
||||
file->SetGameSave(new GameSave(std::move(data)));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -441,10 +959,89 @@ std::unique_ptr<SaveFile> Client::LoadSaveFile(ByteString filename)
|
||||
{
|
||||
file->SetLoadingError(err.FromUtf8());
|
||||
}
|
||||
commandInterface->SetLastError(err.FromUtf8());
|
||||
}
|
||||
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
|
||||
// also used for clipboard and lua stamps
|
||||
void Client::MergeStampAuthorInfo(Json::Value stampAuthors)
|
||||
|
@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
#include "common/String.h"
|
||||
#include "common/ExplicitSingleton.h"
|
||||
#include "StartupInfo.h"
|
||||
#include "User.h"
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
@ -11,27 +10,53 @@
|
||||
|
||||
class SaveInfo;
|
||||
class SaveFile;
|
||||
class SaveComment;
|
||||
class GameSave;
|
||||
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 RequestListener;
|
||||
class ClientListener;
|
||||
namespace http
|
||||
{
|
||||
class StartupRequest;
|
||||
class Request;
|
||||
}
|
||||
class Client: public ExplicitSingleton<Client> {
|
||||
private:
|
||||
String messageOfTheDay;
|
||||
std::vector<ServerNotification> serverNotifications;
|
||||
std::vector<std::pair<String, ByteString> > serverNotifications;
|
||||
|
||||
std::unique_ptr<http::StartupRequest> versionCheckRequest;
|
||||
std::unique_ptr<http::StartupRequest> alternateVersionCheckRequest;
|
||||
std::unique_ptr<http::Request> versionCheckRequest;
|
||||
std::unique_ptr<http::Request> alternateVersionCheckRequest;
|
||||
bool usingAltUpdateServer;
|
||||
bool updateAvailable;
|
||||
std::optional<UpdateInfo> updateInfo;
|
||||
UpdateInfo updateInfo;
|
||||
|
||||
String lastError;
|
||||
bool firstRun;
|
||||
|
||||
std::vector<ByteString> stampIDs;
|
||||
@ -44,7 +69,7 @@ private:
|
||||
void notifyUpdateAvailable();
|
||||
void notifyAuthUserChanged();
|
||||
void notifyMessageOfTheDay();
|
||||
void notifyNewNotification(ServerNotification notification);
|
||||
void notifyNewNotification(std::pair<String, ByteString> notification);
|
||||
|
||||
// Save stealing info
|
||||
Json::Value authors;
|
||||
@ -53,9 +78,6 @@ private:
|
||||
void MigrateStampsDef();
|
||||
void WriteStamps();
|
||||
|
||||
void LoadAuthUser();
|
||||
void SaveAuthUser();
|
||||
|
||||
public:
|
||||
|
||||
std::vector<ClientListener*> listeners;
|
||||
@ -69,7 +91,7 @@ public:
|
||||
void ClearAuthorInfo() { authors.clear(); }
|
||||
bool IsAuthorsEmpty() { return authors.size() == 0; }
|
||||
|
||||
std::optional<UpdateInfo> GetUpdateInfo();
|
||||
UpdateInfo GetUpdateInfo();
|
||||
|
||||
Client();
|
||||
~Client();
|
||||
@ -77,8 +99,8 @@ public:
|
||||
ByteString FileOpenDialogue();
|
||||
//std::string FileSaveDialogue();
|
||||
|
||||
void AddServerNotification(ServerNotification notification);
|
||||
std::vector<ServerNotification> GetServerNotifications();
|
||||
void AddServerNotification(std::pair<String, ByteString> notification);
|
||||
std::vector<std::pair<String, ByteString> > GetServerNotifications();
|
||||
|
||||
void SetMessageOfTheDay(String message);
|
||||
String GetMessageOfTheDay();
|
||||
@ -89,19 +111,40 @@ public:
|
||||
void AddListener(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 RenameStamp(ByteString stampID, ByteString newName);
|
||||
ByteString AddStamp(std::unique_ptr<GameSave> saveData);
|
||||
ByteString AddStamp(GameSave * saveData);
|
||||
void RescanStamps();
|
||||
const std::vector<ByteString> &GetStamps() const;
|
||||
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);
|
||||
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 CheckUpdate(std::unique_ptr<http::Request> &updateRequest, bool checkSession);
|
||||
|
||||
String DoMigration(ByteString fromDir, ByteString toDir);
|
||||
};
|
||||
|
@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
#include "common/String.h"
|
||||
#include "client/ServerNotification.h"
|
||||
|
||||
class Client;
|
||||
class ClientListener
|
||||
@ -12,6 +11,6 @@ public:
|
||||
virtual void NotifyUpdateAvailable(Client * sender) {}
|
||||
virtual void NotifyAuthUserChanged(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
|
||||
#include "common/Plane.h"
|
||||
#include "common/String.h"
|
||||
#include "common/tpt-rand.h"
|
||||
#include "common/Version.h"
|
||||
#include "simulation/Sign.h"
|
||||
#include "simulation/Particle.h"
|
||||
#include "simulation/MissingElements.h"
|
||||
#include "Misc.h"
|
||||
#include "SimulationConfig.h"
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <json/json.h>
|
||||
|
||||
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
|
||||
{
|
||||
// number of pixels translated. When translating CELL pixels, shift all CELL grids
|
||||
vector2d translated = { 0, 0 };
|
||||
void readOPS(const std::vector<char> &data);
|
||||
void readPSv(const std::vector<char> &data);
|
||||
std::pair<bool, std::vector<char>> serialiseOPS() const;
|
||||
|
||||
void MapPalette();
|
||||
|
||||
public:
|
||||
Vec2<int> blockSize = { 0, 0 };
|
||||
int blockWidth = 0;
|
||||
int blockHeight = 0;
|
||||
bool fromNewerVersion = false;
|
||||
Version<2> version{};
|
||||
int majorVersion = 0;
|
||||
int minorVersion = 0;
|
||||
bool hasPressure = 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
|
||||
int particlesCount = 0;
|
||||
std::vector<Particle> particles;
|
||||
PlaneAdapter<std::vector<unsigned char>> blockMap;
|
||||
PlaneAdapter<std::vector<float>> fanVelX;
|
||||
PlaneAdapter<std::vector<float>> fanVelY;
|
||||
PlaneAdapter<std::vector<float>> pressure;
|
||||
PlaneAdapter<std::vector<float>> velocityX;
|
||||
PlaneAdapter<std::vector<float>> velocityY;
|
||||
PlaneAdapter<std::vector<float>> ambientHeat;
|
||||
PlaneAdapter<std::vector<unsigned char>> blockAir;
|
||||
PlaneAdapter<std::vector<unsigned char>> blockAirh;
|
||||
Plane<unsigned char> blockMap;
|
||||
Plane<float> fanVelX;
|
||||
Plane<float> fanVelY;
|
||||
Plane<float> pressure;
|
||||
Plane<float> velocityX;
|
||||
Plane<float> velocityY;
|
||||
Plane<float> ambientHeat;
|
||||
|
||||
//Simulation Options
|
||||
bool waterEEnabled = false;
|
||||
@ -104,8 +119,6 @@ public:
|
||||
int edgeMode = 0;
|
||||
bool wantAuthors = true;
|
||||
|
||||
MissingElements missingElements;
|
||||
|
||||
//Signs
|
||||
std::vector<sign> signs;
|
||||
StkmData stkm;
|
||||
@ -119,12 +132,14 @@ public:
|
||||
|
||||
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);
|
||||
void setSize(Vec2<int> newBlockSize);
|
||||
void setSize(int width, int height);
|
||||
// return value is [ fakeFromNewerVersion, gameData ]
|
||||
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);
|
||||
|
||||
|
@ -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 "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):
|
||||
gameSave(NULL),
|
||||
filename(filename),
|
||||
displayName(filename.FromUtf8()),
|
||||
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)
|
||||
{
|
||||
@ -20,7 +32,7 @@ const GameSave *SaveFile::LazyGetGameSave() // non-owning
|
||||
std::vector<char> data;
|
||||
if (Platform::ReadFile(data, filename))
|
||||
{
|
||||
gameSave = std::make_unique<GameSave>(std::move(data));
|
||||
gameSave = new GameSave(std::move(data));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -32,33 +44,24 @@ const GameSave *SaveFile::LazyGetGameSave() // non-owning
|
||||
loadingError = ByteString(e.what()).FromUtf8();
|
||||
}
|
||||
}
|
||||
return gameSave.get();
|
||||
}
|
||||
|
||||
const GameSave *SaveFile::GetGameSave() const
|
||||
{
|
||||
return gameSave.get();
|
||||
}
|
||||
|
||||
std::unique_ptr<GameSave> SaveFile::TakeGameSave()
|
||||
{
|
||||
return std::move(gameSave);
|
||||
return gameSave;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@ -68,7 +71,7 @@ void SaveFile::SetFileName(ByteString fileName)
|
||||
this->filename = fileName;
|
||||
}
|
||||
|
||||
const String &SaveFile::GetDisplayName() const
|
||||
String SaveFile::GetDisplayName()
|
||||
{
|
||||
return displayName;
|
||||
}
|
||||
@ -78,7 +81,7 @@ void SaveFile::SetDisplayName(String displayName)
|
||||
this->displayName = displayName;
|
||||
}
|
||||
|
||||
const String &SaveFile::GetError() const
|
||||
String SaveFile::GetError()
|
||||
{
|
||||
return loadingError;
|
||||
}
|
||||
@ -87,3 +90,10 @@ void SaveFile::SetLoadingError(String error)
|
||||
{
|
||||
loadingError = error;
|
||||
}
|
||||
|
||||
SaveFile::~SaveFile() {
|
||||
if (gameSave)
|
||||
{
|
||||
delete gameSave;
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,27 @@
|
||||
#pragma once
|
||||
#include "common/String.h"
|
||||
#include <memory>
|
||||
|
||||
class GameSave;
|
||||
|
||||
class SaveFile {
|
||||
public:
|
||||
SaveFile(SaveFile & save);
|
||||
SaveFile(ByteString filename, bool newLazyLoad = false);
|
||||
|
||||
const GameSave *LazyGetGameSave();
|
||||
const GameSave *GetGameSave() const;
|
||||
std::unique_ptr<GameSave> TakeGameSave();
|
||||
void SetGameSave(std::unique_ptr<GameSave> newSameSave);
|
||||
const String &GetDisplayName() const;
|
||||
GameSave * GetGameSave();
|
||||
void SetGameSave(GameSave * save);
|
||||
String GetDisplayName();
|
||||
void SetDisplayName(String displayName);
|
||||
const ByteString &GetName() const;
|
||||
ByteString GetName();
|
||||
void SetFileName(ByteString fileName);
|
||||
const String &GetError() const;
|
||||
String GetError();
|
||||
void SetLoadingError(String error);
|
||||
|
||||
void LazyUnload();
|
||||
|
||||
virtual ~SaveFile();
|
||||
private:
|
||||
std::unique_ptr<GameSave> gameSave;
|
||||
GameSave * gameSave;
|
||||
ByteString filename;
|
||||
String displayName;
|
||||
String loadingError;
|
||||
|
@ -1,7 +1,31 @@
|
||||
#include "SaveInfo.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),
|
||||
createdDate(_createdDate),
|
||||
updatedDate(_updatedDate),
|
||||
@ -15,12 +39,14 @@ SaveInfo::SaveInfo(int _id, time_t _createdDate, time_t _updatedDate, int _votes
|
||||
userName(_userName),
|
||||
name(_name),
|
||||
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),
|
||||
createdDate(_createdDate),
|
||||
updatedDate(_updatedDate),
|
||||
@ -34,18 +60,28 @@ SaveInfo::SaveInfo(int _id, time_t _createdDate, time_t _updatedDate, int _votes
|
||||
userName(_userName),
|
||||
name(_name),
|
||||
Description(description_),
|
||||
Published(published_)
|
||||
Published(published_),
|
||||
tags(),
|
||||
gameSave(NULL)
|
||||
{
|
||||
std::list<ByteString> tagsSorted = tags_;
|
||||
tagsSorted.sort();
|
||||
tags=tagsSorted;
|
||||
}
|
||||
|
||||
SaveInfo::~SaveInfo()
|
||||
{
|
||||
if(gameSave)
|
||||
{
|
||||
delete gameSave;
|
||||
}
|
||||
}
|
||||
|
||||
void SaveInfo::SetName(String name)
|
||||
{
|
||||
this->name = name;
|
||||
}
|
||||
const String &SaveInfo::GetName() const
|
||||
String SaveInfo::GetName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
@ -54,7 +90,7 @@ void SaveInfo::SetDescription(String description)
|
||||
{
|
||||
Description = description;
|
||||
}
|
||||
const String &SaveInfo::GetDescription() const
|
||||
String SaveInfo::GetDescription()
|
||||
{
|
||||
return Description;
|
||||
}
|
||||
@ -63,7 +99,7 @@ void SaveInfo::SetPublished(bool published)
|
||||
{
|
||||
Published = published;
|
||||
}
|
||||
bool SaveInfo::GetPublished() const
|
||||
bool SaveInfo::GetPublished()
|
||||
{
|
||||
return Published;
|
||||
}
|
||||
@ -72,7 +108,7 @@ void SaveInfo::SetVote(int vote)
|
||||
{
|
||||
this->vote = vote;
|
||||
}
|
||||
int SaveInfo::GetVote() const
|
||||
int SaveInfo::GetVote()
|
||||
{
|
||||
return vote;
|
||||
}
|
||||
@ -82,7 +118,7 @@ void SaveInfo::SetUserName(ByteString userName)
|
||||
this->userName = userName;
|
||||
}
|
||||
|
||||
const ByteString &SaveInfo::GetUserName() const
|
||||
ByteString SaveInfo::GetUserName()
|
||||
{
|
||||
return userName;
|
||||
}
|
||||
@ -91,7 +127,7 @@ void SaveInfo::SetID(int id)
|
||||
{
|
||||
this->id = id;
|
||||
}
|
||||
int SaveInfo::GetID() const
|
||||
int SaveInfo::GetID()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
@ -100,7 +136,7 @@ void SaveInfo::SetVotesUp(int votesUp)
|
||||
{
|
||||
this->votesUp = votesUp;
|
||||
}
|
||||
int SaveInfo::GetVotesUp() const
|
||||
int SaveInfo::GetVotesUp()
|
||||
{
|
||||
return votesUp;
|
||||
}
|
||||
@ -109,7 +145,7 @@ void SaveInfo::SetVotesDown(int votesDown)
|
||||
{
|
||||
this->votesDown = votesDown;
|
||||
}
|
||||
int SaveInfo::GetVotesDown() const
|
||||
int SaveInfo::GetVotesDown()
|
||||
{
|
||||
return votesDown;
|
||||
}
|
||||
@ -118,7 +154,7 @@ void SaveInfo::SetVersion(int version)
|
||||
{
|
||||
this->Version = version;
|
||||
}
|
||||
int SaveInfo::GetVersion() const
|
||||
int SaveInfo::GetVersion()
|
||||
{
|
||||
return Version;
|
||||
}
|
||||
@ -130,33 +166,18 @@ void SaveInfo::SetTags(std::list<ByteString> tags)
|
||||
this->tags=tagsSorted;
|
||||
}
|
||||
|
||||
std::list<ByteString> SaveInfo::GetTags() const
|
||||
std::list<ByteString> SaveInfo::GetTags()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
delete gameSave;
|
||||
gameSave = saveGame;
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
#pragma once
|
||||
#include "common/String.h"
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <ctime>
|
||||
|
||||
#ifdef GetUserName
|
||||
# undef GetUserName // dammit windows
|
||||
#endif
|
||||
|
||||
class GameSave;
|
||||
|
||||
@ -11,8 +13,8 @@ class SaveInfo
|
||||
private:
|
||||
public:
|
||||
int id;
|
||||
time_t createdDate;
|
||||
time_t updatedDate;
|
||||
int createdDate;
|
||||
int updatedDate;
|
||||
int votesUp, votesDown;
|
||||
int vote;
|
||||
bool Favourite;
|
||||
@ -27,45 +29,46 @@ public:
|
||||
bool Published;
|
||||
|
||||
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);
|
||||
const String &GetName() const;
|
||||
String GetName();
|
||||
|
||||
void SetDescription(String description);
|
||||
const String &GetDescription() const;
|
||||
String GetDescription();
|
||||
|
||||
void SetPublished(bool published);
|
||||
bool GetPublished() const;
|
||||
bool GetPublished();
|
||||
|
||||
void SetUserName(ByteString userName);
|
||||
const ByteString &GetUserName() const;
|
||||
ByteString GetUserName();
|
||||
|
||||
void SetID(int id);
|
||||
int GetID() const;
|
||||
int GetID();
|
||||
|
||||
void SetVote(int vote);
|
||||
int GetVote() const;
|
||||
int GetVote();
|
||||
|
||||
void SetVotesUp(int votesUp);
|
||||
int GetVotesUp() const;
|
||||
int GetVotesUp();
|
||||
|
||||
void SetVotesDown(int votesDown);
|
||||
int GetVotesDown() const;
|
||||
int GetVotesDown();
|
||||
|
||||
void SetVersion(int version);
|
||||
int GetVersion() const;
|
||||
int GetVersion();
|
||||
|
||||
void SetTags(std::list<ByteString> tags);
|
||||
std::list<ByteString> GetTags() const;
|
||||
std::list<ByteString> GetTags();
|
||||
|
||||
const GameSave *GetGameSave() const;
|
||||
std::unique_ptr<GameSave> TakeGameSave();
|
||||
void SetGameSave(std::unique_ptr<GameSave> newGameSave);
|
||||
|
||||
std::unique_ptr<SaveInfo> CloneInfo() const;
|
||||
GameSave * GetGameSave();
|
||||
void SetGameSave(GameSave * gameSave);
|
||||
};
|
||||
|
@ -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()
|
||||
{
|
||||
thumbnail = SaveRenderer::Ref().Render(save.get(), decorations, fire);
|
||||
thumbnail = std::unique_ptr<VideoBuffer>(SaveRenderer::Ref().Render(save.get(), decorations, fire));
|
||||
if (thumbnail)
|
||||
{
|
||||
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:
|
||||
enum Elevation
|
||||
{
|
||||
ElevationNone,
|
||||
ElevationHalfMod,
|
||||
ElevationMod,
|
||||
ElevationAdmin,
|
||||
ElevationAdmin, ElevationModerator, ElevationNone
|
||||
};
|
||||
static Elevation ElevationFromString(ByteString str);
|
||||
static ByteString ElevationToString(Elevation elevation);
|
||||
|
||||
int UserID;
|
||||
ByteString Username;
|
||||
ByteString SessionID;
|
||||
|
@ -1,36 +1,39 @@
|
||||
#include "APIRequest.h"
|
||||
|
||||
#include "client/Client.h"
|
||||
|
||||
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();
|
||||
if (authMode == authRequire && !user.UserID)
|
||||
{
|
||||
FailEarly("Not authenticated");
|
||||
return;
|
||||
}
|
||||
if (authMode != authOmit && user.UserID)
|
||||
{
|
||||
AuthHeaders(ByteString::Build(user.UserID), user.SessionID);
|
||||
}
|
||||
User user = Client::Ref().GetAuthUser();
|
||||
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
|
||||
{
|
||||
std::istringstream ss(data);
|
||||
ss >> document;
|
||||
ByteString data;
|
||||
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 "common/String.h"
|
||||
#include <json/json.h>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
|
||||
namespace http
|
||||
{
|
||||
class APIRequest : public Request
|
||||
{
|
||||
bool checkStatus;
|
||||
|
||||
public:
|
||||
enum AuthMode
|
||||
struct Result
|
||||
{
|
||||
authRequire,
|
||||
authUse,
|
||||
authOmit,
|
||||
int status;
|
||||
std::unique_ptr<Json::Value> document;
|
||||
};
|
||||
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
|
||||
{
|
||||
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();
|
||||
UserInfo userInfo;
|
||||
try
|
||||
if (result.document)
|
||||
{
|
||||
auto &user = result["User"];
|
||||
userInfo = UserInfo(
|
||||
auto &user = (*result.document)["User"];
|
||||
user_info = std::unique_ptr<UserInfo>(new UserInfo(
|
||||
user["ID"].asInt(),
|
||||
user["Age"].asInt(),
|
||||
user["Username"].asString(),
|
||||
@ -29,13 +33,9 @@ namespace http
|
||||
user["Forum"]["Topics"].asInt(),
|
||||
user["Forum"]["Replies"].asInt(),
|
||||
user["Forum"]["Reputation"].asInt()
|
||||
);
|
||||
));
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
||||
}
|
||||
return userInfo;
|
||||
return user_info;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "APIRequest.h"
|
||||
#include "client/UserInfo.h"
|
||||
|
||||
class UserInfo;
|
||||
|
||||
namespace http
|
||||
{
|
||||
@ -8,7 +9,8 @@ namespace http
|
||||
{
|
||||
public:
|
||||
GetUserInfoRequest(ByteString username);
|
||||
virtual ~GetUserInfoRequest();
|
||||
|
||||
UserInfo Finish();
|
||||
std::unique_ptr<UserInfo> Finish();
|
||||
};
|
||||
}
|
||||
|
@ -1,27 +1,32 @@
|
||||
#include "ImageRequest.h"
|
||||
#include "graphics/Graphics.h"
|
||||
#include "client/Client.h"
|
||||
#include <iostream>
|
||||
|
||||
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()
|
||||
{
|
||||
auto [ status, data ] = Request::Finish();
|
||||
ParseResponse(data, status, responseData);
|
||||
auto vb = VideoBuffer::FromPNG(std::vector<char>(data.begin(), data.end()));
|
||||
if (vb)
|
||||
(void)status; // We don't use this for anything, not ideal >_>
|
||||
std::unique_ptr<VideoBuffer> vb;
|
||||
if (data.size())
|
||||
{
|
||||
vb->Resize(requestedSize, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
vb = std::make_unique<VideoBuffer>(Vec2(15, 16));
|
||||
vb->BlendChar(Vec2(2, 4), 0xE06E, 0xFFFFFF_rgb .WithAlpha(0xFF));
|
||||
vb = VideoBuffer::FromPNG(std::vector<char>(data.begin(), data.end()));
|
||||
if (vb)
|
||||
vb->Resize(size, true);
|
||||
else
|
||||
{
|
||||
vb = std::make_unique<VideoBuffer>(Vec2(15, 16));
|
||||
vb->BlendChar(Vec2(2, 4), 0xE06E, 0xFFFFFF_rgb .WithAlpha(0xFF));
|
||||
}
|
||||
}
|
||||
return vb;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "common/String.h"
|
||||
#include "common/Vec2.h"
|
||||
#include "Request.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class VideoBuffer;
|
||||
@ -10,10 +11,11 @@ namespace http
|
||||
{
|
||||
class ImageRequest : public Request
|
||||
{
|
||||
Vec2<int> requestedSize;
|
||||
Vec2<int> size;
|
||||
|
||||
public:
|
||||
ImageRequest(ByteString url, Vec2<int> newRequestedSize);
|
||||
ImageRequest(ByteString url, Vec2<int> size);
|
||||
virtual ~ImageRequest();
|
||||
|
||||
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 "requestmanager/RequestManager.h"
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <json/json.h>
|
||||
|
||||
namespace http
|
||||
{
|
||||
@ -15,61 +12,45 @@ namespace http
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void Request::FailEarly(ByteString error)
|
||||
{
|
||||
assert(handle->state == RequestHandle::ready);
|
||||
handle->failEarly = error;
|
||||
}
|
||||
|
||||
void Request::Verb(ByteString newVerb)
|
||||
{
|
||||
assert(handle->state == RequestHandle::ready);
|
||||
handle->verb = newVerb;
|
||||
}
|
||||
|
||||
void Request::AddHeader(Header header)
|
||||
void Request::AddHeader(ByteString header)
|
||||
{
|
||||
assert(handle->state == RequestHandle::ready);
|
||||
handle->headers.push_back(header);
|
||||
}
|
||||
|
||||
void Request::AddPostData(PostData data)
|
||||
void Request::AddPostData(std::map<ByteString, ByteString> data)
|
||||
{
|
||||
assert(handle->state == RequestHandle::ready);
|
||||
// Even if the map is empty, calling this function signifies you want to do a POST request
|
||||
handle->isPost = true;
|
||||
handle->postData = data;
|
||||
handle->postData.insert(data.begin(), data.end());
|
||||
}
|
||||
|
||||
void Request::AuthHeaders(ByteString ID, ByteString session)
|
||||
{
|
||||
assert(handle->state == RequestHandle::ready);
|
||||
if (ID.size() && ID != "-1") // -1 is an emscripten hack, see AuthUserEmscripten.cpp
|
||||
if (ID.size())
|
||||
{
|
||||
if (session.size())
|
||||
{
|
||||
AddHeader({ "X-Auth-User-Id", ID });
|
||||
AddHeader({ "X-Auth-Session-Key", session });
|
||||
AddHeader("X-Auth-User-Id: " + ID);
|
||||
AddHeader("X-Auth-Session-Key: " + session);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddHeader({ "X-Auth-User", ID });
|
||||
AddHeader("X-Auth-User: " + ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -88,71 +69,52 @@ namespace http
|
||||
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);
|
||||
assert(handle->state == RequestHandle::running || handle->state == RequestHandle::done);
|
||||
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);
|
||||
assert(handle->state == RequestHandle::done);
|
||||
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::unique_lock lk(handle->stateMx);
|
||||
if (handle->state == RequestHandle::running)
|
||||
{
|
||||
std::unique_lock lk(handle->stateMx);
|
||||
assert(handle->state == RequestHandle::done);
|
||||
handle->stateCv.wait(lk, [this]() {
|
||||
return handle->state == RequestHandle::done;
|
||||
});
|
||||
}
|
||||
assert(handle->state == RequestHandle::done);
|
||||
handle->state = RequestHandle::dead;
|
||||
if (handle->error)
|
||||
{
|
||||
throw RequestError(*handle->error);
|
||||
}
|
||||
return std::pair{ handle->statusCode, std::move(handle->responseData) };
|
||||
return { handle->statusCode, std::move(handle->responseData) };
|
||||
}
|
||||
|
||||
void RequestHandle::MarkDone()
|
||||
std::pair<int, ByteString> Request::Simple(ByteString uri, std::map<ByteString, ByteString> 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;
|
||||
}
|
||||
return SimpleAuth(uri, "", "", post_data);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@ -234,69 +196,7 @@ namespace http
|
||||
case 619: return "SSL: Failed to Load CRL File";
|
||||
case 620: return "SSL: Issuer Check Failed";
|
||||
case 621: return "SSL: Pinned Public Key Mismatch";
|
||||
}
|
||||
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;
|
||||
default: return "Unknown Status Code";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user