Merge branch 'master' into phot-revamp
This commit is contained in:
commit
b690a8ad75
64
.github/build.sh
vendored
64
.github/build.sh
vendored
@ -43,6 +43,28 @@ aarch64-android-bionic-static) ;;
|
||||
*) >&2 echo "configuration $BSH_HOST_ARCH-$BSH_HOST_PLATFORM-$BSH_HOST_LIBC-$BSH_STATIC_DYNAMIC is not supported" && exit 1;;
|
||||
esac
|
||||
|
||||
if [[ -z ${BSH_NO_PACKAGES-} ]]; then
|
||||
case $BSH_BUILD_PLATFORM in
|
||||
linux)
|
||||
sudo apt update
|
||||
if [[ $BSH_STATIC_DYNAMIC == static ]]; then
|
||||
sudo apt install libc6-dev libc6-dev-i386
|
||||
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
|
||||
if [[ $BSH_STATIC_DYNAMIC != static ]]; then
|
||||
brew install luajit curl fftw zlib sdl2 bzip2 jsoncpp
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
function inplace_sed() {
|
||||
local subst=$1
|
||||
local path=$2
|
||||
@ -163,7 +185,6 @@ meson_configure+=$'\t'-Dapp_data=$APP_DATA
|
||||
meson_configure+=$'\t'-Dapp_vendor=$APP_VENDOR
|
||||
meson_configure+=$'\t'-Db_strip=false
|
||||
meson_configure+=$'\t'-Db_staticpic=false
|
||||
meson_configure+=$'\t'-Dinstall_check=true
|
||||
meson_configure+=$'\t'-Dmod_id=$MOD_ID
|
||||
case $BSH_HOST_ARCH-$BSH_HOST_PLATFORM-$BSH_HOST_LIBC-$BSH_DEBUG_RELEASE in
|
||||
x86_64-linux-gnu-debug) ;&
|
||||
@ -174,6 +195,12 @@ x86_64-darwin-macos-debug)
|
||||
meson_configure+=$'\t'-Dbuild_font=true
|
||||
;;
|
||||
esac
|
||||
if [[ $PACKAGE_MODE == nohttp ]]; then
|
||||
meson_configure+=$'\t'-Dhttp=false
|
||||
fi
|
||||
if [[ $PACKAGE_MODE == nolua ]]; then
|
||||
meson_configure+=$'\t'-Dlua=none
|
||||
fi
|
||||
if [[ $BSH_STATIC_DYNAMIC == static ]]; then
|
||||
meson_configure+=$'\t'-Dstatic=prebuilt
|
||||
if [[ $BSH_HOST_PLATFORM == windows ]]; then
|
||||
@ -212,9 +239,15 @@ fi
|
||||
if [[ $RELEASE_TYPE == stable ]]; then
|
||||
stable_or_beta=yes
|
||||
fi
|
||||
save_version=$(grep -w src/Config.template.h -e "#define SAVE_VERSION" | cut -d ' ' -f 3)
|
||||
minor_version=$(grep -w src/Config.template.h -e "#define MINOR_VERSION" | cut -d ' ' -f 3)
|
||||
build_num=$(grep -w src/Config.template.h -e "#define BUILD_NUM" | cut -d ' ' -f 3)
|
||||
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)
|
||||
@ -252,20 +285,28 @@ if [[ $BSH_HOST_PLATFORM-$BSH_HOST_ARCH == darwin-aarch64 ]]; then
|
||||
meson_configure+=$'\t'--cross-file=.github/macaa64-ghactions.ini
|
||||
fi
|
||||
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"
|
||||
exit 1
|
||||
fi
|
||||
tptlibsremote=https://github.com/$GITHUB_REPOSITORY_OWNER/tpt-libs
|
||||
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
|
||||
fi
|
||||
if [[ ! -d build-tpt-libs/tpt-libs ]]; then
|
||||
mkdir -p build-tpt-libs
|
||||
cd build-tpt-libs
|
||||
git clone https://github.com/$GITHUB_REPOSITORY_OWNER/tpt-libs --branch $tptlibsbranch --depth 1
|
||||
git clone $tptlibsremote --branch $tptlibsbranch --depth 1
|
||||
cd ..
|
||||
fi
|
||||
tpt_libs_vtag=v00000000000000
|
||||
@ -331,7 +372,7 @@ if [[ $BSH_HOST_PLATFORM == android ]]; then
|
||||
fi
|
||||
if [[ $PACKAGE_MODE == appimage ]]; then
|
||||
# so far this can only happen with $BSH_HOST_PLATFORM-$BSH_HOST_LIBC == linux-gnu, but this may change later
|
||||
meson configure -Dinstall_check=false -Dignore_updates=true -Dbuild_render=false -Dbuild_font=false
|
||||
meson configure -Dcan_install=no -Dignore_updates=true -Dbuild_render=false -Dbuild_font=false
|
||||
strip_target=$APP_EXE
|
||||
fi
|
||||
if [[ $BSH_BUILD_PLATFORM == windows ]]; then
|
||||
@ -341,6 +382,14 @@ if [[ $BSH_BUILD_PLATFORM == windows ]]; then
|
||||
set -e
|
||||
cat $APP_EXE.exe.rsp
|
||||
[[ $ninja_code == 0 ]];
|
||||
echo # rsps don't usually have a newline at the end
|
||||
if [[ "$BSH_HOST_PLATFORM-$BSH_STATIC_DYNAMIC $BSH_BUILD_PLATFORM" == "windows-dynamic windows" ]]; then
|
||||
# on windows we provide the dynamic dependencies also; makes sense to check for their presence
|
||||
# msys ldd works fine but only on windows build machines
|
||||
if ldd $APP_EXE | grep "not found"; then
|
||||
exit 1 # ldd | grep will have printed missing deps
|
||||
fi
|
||||
fi
|
||||
else
|
||||
ninja -v
|
||||
fi
|
||||
@ -383,8 +432,7 @@ if [[ $PACKAGE_MODE == dmg ]]; then
|
||||
mv $appdir dmgroot/$appdir
|
||||
cp ../LICENSE dmgroot/LICENSE
|
||||
cp ../README.md dmgroot/README.md
|
||||
hdiutil create uncompressed.dmg -ov -volname $APP_NAME -fs HFS+ -srcfolder dmgroot
|
||||
hdiutil convert uncompressed.dmg -format UDZO -o $ASSET_PATH
|
||||
hdiutil create -format UDZO -volname $APP_NAME -fs HFS+ -srcfolder dmgroot -o $ASSET_PATH
|
||||
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
|
||||
|
28
.github/prepare.py
vendored
28
.github/prepare.py
vendored
@ -78,11 +78,13 @@ 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 in [
|
||||
( 'x86_64', 'linux', 'gnu', 'static', 'linux', 'ubuntu-18.04', '', False, False, None, None, None, 'debug' ),
|
||||
( 'x86_64', 'linux', 'gnu', 'static', 'linux', 'ubuntu-18.04', '', True, True, '.dbg', None, 'x86_64-lin-gcc-static', 'release' ),
|
||||
( 'x86_64', 'linux', 'gnu', 'static', 'linux', 'ubuntu-18.04', '', False, True, '.dbg', 'appimage', None, 'release' ),
|
||||
( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-18.04', '', False, False, None, None, None, 'debug' ),
|
||||
( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-18.04', '', False, False, None, None, None, 'release' ),
|
||||
( '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' ),
|
||||
@ -107,14 +109,14 @@ for arch, platform, libc, statdyn, bplatform, runson, suff
|
||||
( '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-18.04', '.apk', False, False, None, None, None, 'debug' ),
|
||||
( 'x86', 'android', 'bionic', 'static', 'linux', 'ubuntu-18.04', '.apk', True, True, '.dbg', None, 'i686-and-gcc-static', 'release' ),
|
||||
( 'x86_64', 'android', 'bionic', 'static', 'linux', 'ubuntu-18.04', '.apk', False, False, None, None, None, 'debug' ),
|
||||
( 'x86_64', 'android', 'bionic', 'static', 'linux', 'ubuntu-18.04', '.apk', True, True, '.dbg', None, 'x86_64-and-gcc-static', 'release' ),
|
||||
( 'arm', 'android', 'bionic', 'static', 'linux', 'ubuntu-18.04', '.apk', False, False, None, None, None, 'debug' ),
|
||||
( 'arm', 'android', 'bionic', 'static', 'linux', 'ubuntu-18.04', '.apk', True, True, '.dbg', None, 'arm-and-gcc-static', 'release' ),
|
||||
( 'aarch64', 'android', 'bionic', 'static', 'linux', 'ubuntu-18.04', '.apk', False, False, None, None, None, 'debug' ),
|
||||
( 'aarch64', 'android', 'bionic', 'static', 'linux', 'ubuntu-18.04', '.apk', True, True, '.dbg', None, 'arm64-and-gcc-static', 'release' ),
|
||||
( '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 not mode:
|
||||
mode = 'default'
|
||||
|
14
.github/workflows/build.yaml
vendored
14
.github/workflows/build.yaml
vendored
@ -31,6 +31,7 @@ jobs:
|
||||
app_vendor: ${{ steps.prepare.outputs.app_vendor }}
|
||||
do_publish: ${{ steps.prepare.outputs.do_publish }}
|
||||
steps:
|
||||
- run: git config --global core.autocrlf false
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
@ -58,21 +59,14 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJSON(needs.prepare.outputs.build_matrix) }}
|
||||
steps:
|
||||
- run: git config --global core.autocrlf false
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- run: python -m pip install meson ninja
|
||||
- if: matrix.bsh_build_platform == 'darwin'
|
||||
run: brew install pkg-config coreutils binutils bash
|
||||
- if: matrix.bsh_build_platform == 'darwin' && matrix.bsh_static_dynamic != 'static'
|
||||
run: brew install luajit curl fftw zlib sdl2 bzip2 jsoncpp
|
||||
- if: matrix.bsh_build_platform == 'linux' && matrix.bsh_host_libc == 'mingw'
|
||||
run: sudo apt update && sudo apt install g++-mingw-w64-x86-64
|
||||
- if: matrix.bsh_build_platform == 'linux' && matrix.bsh_static_dynamic != 'static'
|
||||
run: sudo apt update && sudo apt install libluajit-5.1-dev libcurl4-openssl-dev libfftw3-dev zlib1g-dev libsdl2-dev libbz2-dev libjsoncpp-dev
|
||||
- if: matrix.bsh_build_platform == 'linux' && matrix.bsh_static_dynamic == 'static'
|
||||
run: sudo apt update && sudo apt install libc6-dev libc6-dev-i386
|
||||
run: brew install bash coreutils
|
||||
- run: bash -c './.github/build.sh'
|
||||
env:
|
||||
BSH_HOST_ARCH: ${{ matrix.bsh_host_arch }}
|
||||
@ -130,6 +124,7 @@ jobs:
|
||||
matrix: ${{ fromJSON(needs.prepare.outputs.publish_matrix) }}
|
||||
if: needs.prepare.outputs.do_publish == 'yes'
|
||||
steps:
|
||||
- run: git config --global core.autocrlf false
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
@ -147,6 +142,7 @@ jobs:
|
||||
needs: [build, publish, prepare]
|
||||
if: needs.prepare.outputs.do_publish == 'yes'
|
||||
steps:
|
||||
- run: git config --global core.autocrlf false
|
||||
- uses: actions/checkout@v3
|
||||
- run: ./.github/starcatcher-release.sh
|
||||
env:
|
||||
|
@ -1,4 +1,4 @@
|
||||
The Powder Toy - December 2022
|
||||
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
|
||||
* Bryan Hoyle
|
||||
* Victoria Hoyle
|
||||
* Nathan Cousins
|
||||
* jacksonmj
|
||||
* Felix Wallin
|
||||
@ -112,7 +112,7 @@ Command Line
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
| Command | Description | Example |
|
||||
| --------------------- | ------------------------------------------------ | --------------------------------- |
|
||||
| --------------------- | ------------------------------------------------ | --------------------------------------------|
|
||||
| `scale:SIZE` | Change window scale factor | `scale:2` |
|
||||
| `kiosk` | Fullscreen mode | |
|
||||
| `proxy:SERVER[:PORT]` | Proxy server to use | `proxy:wwwcache.lancs.ac.uk:8080` |
|
||||
@ -120,6 +120,7 @@ Command Line
|
||||
| `ddir DIRECTORY` | Directory used for saving stamps and preferences | |
|
||||
| `ptsave:SAVEID` | Open online save, used by ptsave: URLs | `ptsave:2198` |
|
||||
| `disable-network` | Disables internet connections | |
|
||||
| `disable-bluescreen` | Disable bluescreen handler | |
|
||||
| `redirect` | Redirects output to stdout.txt / stderr.txt | |
|
||||
| `cafile:CAFILE` | Set certificate bundle path | `cafile:/etc/ssl/certs/ca-certificates.crt` |
|
||||
| `capath:CAPATH` | Set certificate directory path | `capath:/etc/ssl/certs` |
|
||||
|
78
meson.build
78
meson.build
@ -37,6 +37,9 @@ host_arch = host_machine.cpu_family()
|
||||
host_platform = host_machine.system()
|
||||
# educated guesses follow, PRs welcome
|
||||
if c_compiler.get_id() in [ 'msvc' ]
|
||||
if host_platform != 'windows'
|
||||
error('this seems fishy')
|
||||
endif
|
||||
host_libc = 'msvc'
|
||||
elif c_compiler.get_id() in [ 'gcc' ] and host_platform == 'windows'
|
||||
host_libc = 'mingw'
|
||||
@ -49,6 +52,11 @@ elif host_platform in [ 'android' ]
|
||||
host_platform = 'android'
|
||||
host_libc = 'bionic'
|
||||
else
|
||||
if host_platform != 'linux'
|
||||
# TODO: maybe use 'default' in place of 'linux', or use something other than host_platform where details such as desktop integration are concerned
|
||||
warning('host platform is not linux but we will pretend that it is')
|
||||
host_platform = 'linux'
|
||||
endif
|
||||
host_libc = 'gnu'
|
||||
endif
|
||||
|
||||
@ -64,11 +72,7 @@ endif
|
||||
|
||||
is_static = static_variant != 'none'
|
||||
is_debug = get_option('optimization') in [ '0', 'g' ]
|
||||
enforce_https = get_option('enforce_https')
|
||||
|
||||
if not is_debug and not enforce_https
|
||||
error('refusing to build a release binary with enforce_https=false')
|
||||
endif
|
||||
app_exe = get_option('app_exe')
|
||||
|
||||
tpt_libs_static = 'none'
|
||||
if static_variant == 'prebuilt'
|
||||
@ -81,7 +85,7 @@ 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 = 'v20221223184203'
|
||||
tpt_libs_vtag = 'v20230205154205'
|
||||
endif
|
||||
if tpt_libs_static != 'none'
|
||||
if tpt_libs_variant not in [
|
||||
@ -122,10 +126,6 @@ else
|
||||
endif
|
||||
endif
|
||||
|
||||
conf_data = configuration_data()
|
||||
conf_data.set('CURL_STATICLIB', false)
|
||||
conf_data.set('ZLIB_WINAPI', false)
|
||||
|
||||
x86_sse_level_str = get_option('x86_sse')
|
||||
if x86_sse_level_str == 'auto'
|
||||
x86_sse_level = 20
|
||||
@ -179,9 +179,7 @@ if host_platform == 'android'
|
||||
endif
|
||||
curl_dep = enable_http ? dependency('libcurl', static: is_static) : []
|
||||
|
||||
enable_gravfft = get_option('gravfft')
|
||||
fftw_dep = enable_gravfft ? dependency('fftw3f', 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)
|
||||
@ -278,13 +276,11 @@ endif
|
||||
|
||||
if host_platform == 'windows'
|
||||
args_ccomp_win = [ '-D_WIN32_WINNT=0x0501' ]
|
||||
project_c_args += args_ccomp_win
|
||||
project_cpp_args += args_ccomp_win
|
||||
windows_mod = import('windows')
|
||||
if is_static
|
||||
conf_data.set('CURL_STATICLIB', true)
|
||||
args_ccomp_win += [ '-DCURL_STATICLIB' ]
|
||||
if host_arch == 'x86_64'
|
||||
conf_data.set('ZLIB_WINAPI', true)
|
||||
args_ccomp_win += [ '-DZLIB_WINAPI' ]
|
||||
endif
|
||||
else
|
||||
foreach input_output_condition : tpt_libs.get_variable('config_dlls')
|
||||
@ -302,6 +298,8 @@ if host_platform == 'windows'
|
||||
endif
|
||||
endforeach
|
||||
endif
|
||||
project_c_args += args_ccomp_win
|
||||
project_cpp_args += args_ccomp_win
|
||||
endif
|
||||
|
||||
project_inc = include_directories([ 'src', 'resources' ])
|
||||
@ -316,43 +314,6 @@ else
|
||||
ident_platform = 'UNKNOWN'
|
||||
endif
|
||||
|
||||
app_exe = get_option('app_exe')
|
||||
app_id = get_option('app_id')
|
||||
|
||||
conf_data.set('LIN', host_platform == 'linux')
|
||||
conf_data.set('AND', host_platform == 'android')
|
||||
conf_data.set('WIN', host_platform == 'windows')
|
||||
conf_data.set('MACOSX', host_platform == 'darwin')
|
||||
conf_data.set('X86', is_x86)
|
||||
conf_data.set('X86_SSE3', x86_sse_level >= 30)
|
||||
conf_data.set('X86_SSE2', x86_sse_level >= 20)
|
||||
conf_data.set('X86_SSE', x86_sse_level >= 10)
|
||||
conf_data.set('_64BIT', is_64bit)
|
||||
conf_data.set('BETA', get_option('beta'))
|
||||
conf_data.set('NO_INSTALL_CHECK', not get_option('install_check'))
|
||||
conf_data.set('IGNORE_UPDATES', get_option('ignore_updates'))
|
||||
conf_data.set('MOD_ID', get_option('mod_id'))
|
||||
conf_data.set('DEBUG', is_debug)
|
||||
conf_data.set('SNAPSHOT', get_option('snapshot'))
|
||||
conf_data.set('SNAPSHOT_ID', get_option('snapshot_id'))
|
||||
conf_data.set('SERVER', '"@0@"'.format(get_option('server')))
|
||||
conf_data.set('STATICSERVER', '"@0@"'.format(get_option('static_server')))
|
||||
conf_data.set('IDENT_PLATFORM', '"@0@"'.format(ident_platform))
|
||||
conf_data.set('IDENT', '"@0@-@1@-@2@"'.format(host_arch, host_platform, host_libc).to_upper())
|
||||
conf_data.set('ENFORCE_HTTPS', enforce_https)
|
||||
conf_data.set('APPNAME', get_option('app_name'))
|
||||
conf_data.set('APPCOMMENT', get_option('app_comment'))
|
||||
conf_data.set('APPEXE', app_exe)
|
||||
conf_data.set('APPID', app_id)
|
||||
conf_data.set('APPDATA', get_option('app_data'))
|
||||
conf_data.set('APPVENDOR', get_option('app_vendor'))
|
||||
|
||||
if get_option('update_server') != ''
|
||||
conf_data.set('UPDATESERVER', '"@0@"'.format(get_option('update_server')))
|
||||
else
|
||||
conf_data.set('UPDATESERVER', false)
|
||||
endif
|
||||
|
||||
data_files = []
|
||||
|
||||
subdir('src')
|
||||
@ -378,7 +339,7 @@ if get_option('build_powder')
|
||||
powder_sha = shared_library(
|
||||
app_exe,
|
||||
sources: powder_files,
|
||||
include_directories: [ project_inc, powder_inc ],
|
||||
include_directories: project_inc,
|
||||
c_args: project_c_args,
|
||||
cpp_args: project_cpp_args,
|
||||
link_args: project_link_args,
|
||||
@ -389,12 +350,13 @@ if get_option('build_powder')
|
||||
executable(
|
||||
app_exe,
|
||||
sources: powder_files,
|
||||
include_directories: [ project_inc, powder_inc ],
|
||||
include_directories: project_inc,
|
||||
c_args: project_c_args,
|
||||
cpp_args: project_cpp_args,
|
||||
win_subsystem: is_debug ? 'console' : 'windows',
|
||||
link_args: project_link_args,
|
||||
dependencies: powder_deps,
|
||||
install: true,
|
||||
)
|
||||
endif
|
||||
endif
|
||||
@ -414,7 +376,7 @@ if get_option('build_render')
|
||||
executable(
|
||||
'render',
|
||||
sources: render_files,
|
||||
include_directories: [ project_inc, render_inc ],
|
||||
include_directories: project_inc,
|
||||
c_args: project_c_args,
|
||||
cpp_args: project_cpp_args,
|
||||
link_args: render_link_args,
|
||||
@ -434,7 +396,7 @@ if get_option('build_font')
|
||||
executable(
|
||||
'font',
|
||||
sources: font_files,
|
||||
include_directories: [ project_inc, font_inc ],
|
||||
include_directories: project_inc,
|
||||
c_args: project_c_args,
|
||||
cpp_args: project_cpp_args,
|
||||
link_args: project_link_args,
|
||||
|
@ -18,10 +18,11 @@ option(
|
||||
description: 'Don\'t show notifications about available updates'
|
||||
)
|
||||
option(
|
||||
'install_check',
|
||||
type: 'boolean',
|
||||
value: false,
|
||||
description: 'Do install check on startup'
|
||||
'can_install',
|
||||
type: 'combo',
|
||||
choices: [ 'no', 'yes', 'yes_check', 'auto' ],
|
||||
value: 'auto',
|
||||
description: 'Disable (\'no\') or enable (\'yes\') setting up file and URL associations, or even offer to do it at startup (\'yes_check\')'
|
||||
)
|
||||
option(
|
||||
'http',
|
||||
@ -29,12 +30,6 @@ option(
|
||||
value: true,
|
||||
description: 'Enable HTTP via libcurl'
|
||||
)
|
||||
option(
|
||||
'gravfft',
|
||||
type: 'boolean',
|
||||
value: true,
|
||||
description: 'Enable FFT gravity via libfftw3'
|
||||
)
|
||||
option(
|
||||
'snapshot',
|
||||
type: 'boolean',
|
||||
@ -207,6 +202,12 @@ 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',
|
||||
|
@ -18,7 +18,7 @@
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>icon_exe.icns</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2008-2011 Stanislaw K Skowrenek, Copyright © 2011-2022 Simon Robertshaw, Copyright © 2016-2022 jacob1</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>
|
||||
|
Binary file not shown.
@ -60,6 +60,7 @@ if host_platform == 'windows'
|
||||
],
|
||||
depend_files: [
|
||||
'resource.h',
|
||||
'winutf8.xml',
|
||||
],
|
||||
)
|
||||
elif host_platform == 'darwin'
|
||||
|
@ -1,4 +1,6 @@
|
||||
#include "resource.h"
|
||||
#include <winuser.h>
|
||||
|
||||
IDI_ICON ICON DISCARDABLE "icon_exe.ico"
|
||||
IDI_DOC_ICON ICON DISCARDABLE "icon_cps.ico"
|
||||
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "winutf8.xml"
|
||||
|
@ -1,5 +1,3 @@
|
||||
#define LUASCRIPT 256
|
||||
|
||||
#define IDI_ICON 101
|
||||
#define IDI_DOC_ICON 102
|
||||
|
||||
|
8
resources/winutf8.xml
Normal file
8
resources/winutf8.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<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 "Config.h"
|
||||
|
||||
#include "gui/interface/Window.h"
|
||||
|
||||
class Activity
|
||||
|
@ -1,158 +1,46 @@
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
#pragma once
|
||||
|
||||
#mesondefine CURL_STATICLIB
|
||||
#mesondefine ZLIB_WINAPI
|
||||
constexpr bool SET_WINDOW_ICON = @SET_WINDOW_ICON@;
|
||||
constexpr bool DEBUG = @DEBUG@;
|
||||
constexpr bool X86 = @X86@;
|
||||
constexpr bool BETA = @BETA@;
|
||||
constexpr bool SNAPSHOT = @SNAPSHOT@;
|
||||
constexpr bool MOD = @MOD@;
|
||||
constexpr bool NOHTTP = @NOHTTP@;
|
||||
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 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 char PATH_SEP_CHAR = '@PATH_SEP_CHAR@';
|
||||
|
||||
#mesondefine LUACONSOLE
|
||||
#mesondefine NOHTTP
|
||||
#mesondefine GRAVFFT
|
||||
#mesondefine RENDERER
|
||||
#mesondefine FONTEDITOR
|
||||
constexpr char SERVER[] = "@SERVER@";
|
||||
constexpr char STATICSERVER[] = "@STATICSERVER@";
|
||||
constexpr char UPDATESERVER[] = "@UPDATESERVER@";
|
||||
constexpr char IDENT_PLATFORM[] = "@IDENT_PLATFORM@";
|
||||
constexpr char IDENT[] = "@IDENT@";
|
||||
constexpr char APPNAME[] = "@APPNAME@";
|
||||
constexpr char APPCOMMENT[] = "@APPCOMMENT@";
|
||||
constexpr char APPEXE[] = "@APPEXE@";
|
||||
constexpr char APPID[] = "@APPID@";
|
||||
constexpr char APPDATA[] = "@APPDATA@";
|
||||
constexpr char APPVENDOR[] = "@APPVENDOR@";
|
||||
|
||||
#mesondefine BETA
|
||||
#mesondefine DEBUG
|
||||
#mesondefine IGNORE_UPDATES
|
||||
#mesondefine LIN
|
||||
#mesondefine AND
|
||||
#mesondefine NO_INSTALL_CHECK
|
||||
#mesondefine SNAPSHOT
|
||||
#mesondefine WIN
|
||||
#mesondefine MACOSX
|
||||
#mesondefine X86
|
||||
#mesondefine X86_SSE
|
||||
#mesondefine X86_SSE2
|
||||
#mesondefine X86_SSE3
|
||||
#mesondefine _64BIT
|
||||
#mesondefine SERVER
|
||||
#mesondefine STATICSERVER
|
||||
#mesondefine UPDATESERVER
|
||||
#mesondefine IDENT_PLATFORM
|
||||
#mesondefine IDENT
|
||||
#mesondefine ENFORCE_HTTPS
|
||||
#define APPNAME "@APPNAME@"
|
||||
#define APPCOMMENT "@APPCOMMENT@"
|
||||
#define APPEXE "@APPEXE@"
|
||||
#define APPID "@APPID@"
|
||||
#define APPDATA "@APPDATA@"
|
||||
#define APPVENDOR "@APPVENDOR@"
|
||||
constexpr int SAVE_VERSION = 97;
|
||||
constexpr int MINOR_VERSION = 0;
|
||||
constexpr int BUILD_NUM = 352;
|
||||
constexpr int SNAPSHOT_ID = @SNAPSHOT_ID@;
|
||||
constexpr int MOD_ID = @MOD_ID@;
|
||||
constexpr int FUTURE_SAVE_VERSION = 98;
|
||||
constexpr int FUTURE_MINOR_VERSION = 0;
|
||||
|
||||
#ifdef WIN
|
||||
# define PATH_SEP "\\"
|
||||
# define PATH_SEP_CHAR '\\'
|
||||
#else
|
||||
# define PATH_SEP "/"
|
||||
# define PATH_SEP_CHAR '/'
|
||||
#endif
|
||||
constexpr char IDENT_RELTYPE = SNAPSHOT ? 'S' : (BETA ? 'B' : 'R');
|
||||
|
||||
//VersionInfoStart
|
||||
#define SAVE_VERSION 97
|
||||
#define MINOR_VERSION 0
|
||||
#define BUILD_NUM 351
|
||||
#mesondefine SNAPSHOT_ID
|
||||
#mesondefine MOD_ID
|
||||
#define FUTURE_SAVE_VERSION 97
|
||||
#define FUTURE_MINOR_VERSION 0
|
||||
|
||||
#if !(defined(SNAPSHOT) || defined(BETA) || defined(DEBUG) || MOD_ID > 0)
|
||||
#undef FUTURE_SAVE_VERSION
|
||||
#undef FUTURE_MINOR_VERSION
|
||||
#endif
|
||||
//VersionInfoEnd
|
||||
|
||||
#if !(defined(MACOSX) && defined(DEBUG))
|
||||
#define HIGH_QUALITY_RESAMPLE //High quality image resampling, slower but much higher quality than my terribad linear interpolation
|
||||
#endif
|
||||
|
||||
#if defined(SNAPSHOT)
|
||||
#define IDENT_RELTYPE "S"
|
||||
#elif defined(BETA)
|
||||
#define IDENT_RELTYPE "B"
|
||||
#else
|
||||
#define IDENT_RELTYPE "R"
|
||||
#endif
|
||||
|
||||
#if defined(X86_SSE3)
|
||||
#define IDENT_BUILD "SSE3"
|
||||
#elif defined(X86_SSE2)
|
||||
#define IDENT_BUILD "SSE2"
|
||||
#elif defined(X86_SSE)
|
||||
#define IDENT_BUILD "SSE"
|
||||
#else
|
||||
#define IDENT_BUILD "NO"
|
||||
#endif
|
||||
|
||||
#define MTOS_EXPAND(str) #str
|
||||
#define MTOS(str) MTOS_EXPAND(str)
|
||||
|
||||
#define SCHEME "https://"
|
||||
#define STATICSCHEME "https://"
|
||||
|
||||
#define LOCAL_SAVE_DIR "Saves"
|
||||
|
||||
#define STAMPS_DIR "stamps"
|
||||
|
||||
#define BRUSH_DIR "Brushes"
|
||||
|
||||
#ifndef M_GRAV
|
||||
#define M_GRAV 6.67300e-1
|
||||
#endif
|
||||
|
||||
#ifdef RENDERER
|
||||
#define MENUSIZE 0
|
||||
#define BARSIZE 0
|
||||
#else
|
||||
#define MENUSIZE 40
|
||||
#define BARSIZE 17
|
||||
#endif
|
||||
#define XRES 612
|
||||
#define YRES 384
|
||||
#define NPART XRES*YRES
|
||||
|
||||
#define XCNTR XRES/2
|
||||
#define YCNTR YRES/2
|
||||
|
||||
#define WINDOWW (XRES+BARSIZE)
|
||||
#define WINDOWH (YRES+MENUSIZE)
|
||||
|
||||
#define GRAV_DIFF
|
||||
|
||||
#define MAXSIGNS 16
|
||||
|
||||
//CELL, the size of the pressure, gravity, and wall maps. Larger than 1 to prevent extreme lag
|
||||
#define CELL 4
|
||||
#define ISTP (CELL/2)
|
||||
#define CFDS (4.0f/CELL)
|
||||
#define SIM_MAXVELOCITY 1e4f
|
||||
|
||||
//Air constants
|
||||
#define AIR_TSTEPP 0.3f
|
||||
#define AIR_TSTEPV 0.4f
|
||||
#define AIR_VADV 0.3f
|
||||
#define AIR_VLOSS 0.999f
|
||||
#define AIR_PLOSS 0.9999f
|
||||
|
||||
#define NGOL 24
|
||||
|
||||
#define CIRCLE_BRUSH 0
|
||||
#define SQUARE_BRUSH 1
|
||||
#define TRI_BRUSH 2
|
||||
#define BRUSH_NUM 3
|
||||
|
||||
//Photon constants
|
||||
#define SURF_RANGE 10
|
||||
#define NORMAL_MIN_EST 3
|
||||
#define NORMAL_INTERP 20
|
||||
#define NORMAL_FRAC 16
|
||||
|
||||
#define REFRACT 0x80000000
|
||||
|
||||
/* heavy flint glass, for awesome refraction/dispersion
|
||||
this way you can make roof prisms easily */
|
||||
#define GLASS_IOR 1.9
|
||||
#define GLASS_DISP 0.07
|
||||
|
||||
#define SDEUT
|
||||
#define R_TEMP 22
|
||||
|
||||
#endif /* CONFIG_H */
|
||||
constexpr char SCHEME[] = "https://";
|
||||
constexpr char STATICSCHEME[] = "https://";
|
||||
constexpr char LOCAL_SAVE_DIR[] = "Saves";
|
||||
constexpr char STAMPS_DIR[] = "stamps";
|
||||
constexpr char BRUSH_DIR[] = "Brushes";
|
||||
|
@ -1,6 +1,4 @@
|
||||
#ifndef CONTROLLER_H_
|
||||
#define CONTROLLER_H_
|
||||
|
||||
#pragma once
|
||||
class Controller
|
||||
{
|
||||
private:
|
||||
@ -9,5 +7,3 @@ private:
|
||||
virtual void Hide();
|
||||
virtual ~Controller() = default;
|
||||
};
|
||||
|
||||
#endif /* CONTROLLER_H_ */
|
||||
|
201
src/Format.cpp
201
src/Format.cpp
@ -1,19 +1,15 @@
|
||||
#include "Format.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <cstring>
|
||||
#include <zlib.h>
|
||||
#include <cstdio>
|
||||
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <png.h>
|
||||
#include "Format.h"
|
||||
#include "graphics/Graphics.h"
|
||||
|
||||
#ifndef RENDERER
|
||||
# include "SDLCompat.h"
|
||||
#endif
|
||||
|
||||
ByteString format::UnixtimeToDate(time_t unixtime, ByteString dateFormat)
|
||||
{
|
||||
struct tm * timeData;
|
||||
@ -99,30 +95,187 @@ String format::CleanString(String dirtyString, bool ascii, bool color, bool newl
|
||||
return dirtyString;
|
||||
}
|
||||
|
||||
std::vector<char> format::VideoBufferToPPM(const VideoBuffer & vidBuf)
|
||||
std::vector<char> format::PixelsToPPM(PlaneAdapter<std::vector<pixel>> const &input)
|
||||
{
|
||||
std::vector<char> data;
|
||||
char buffer[256];
|
||||
sprintf(buffer, "P6\n%d %d\n255\n", vidBuf.Width, vidBuf.Height);
|
||||
sprintf(buffer, "P6\n%d %d\n255\n", input.Size().X, input.Size().Y);
|
||||
data.insert(data.end(), buffer, buffer + strlen(buffer));
|
||||
|
||||
unsigned char * currentRow = new unsigned char[vidBuf.Width*3];
|
||||
for(int y = 0; y < vidBuf.Height; y++)
|
||||
data.reserve(data.size() + input.Size().X * input.Size().Y * 3);
|
||||
|
||||
for (int i = 0; i < input.Size().X * input.Size().Y; i++)
|
||||
{
|
||||
int rowPos = 0;
|
||||
for(int x = 0; x < vidBuf.Width; x++)
|
||||
{
|
||||
currentRow[rowPos++] = PIXR(vidBuf.Buffer[(y*vidBuf.Width)+x]);
|
||||
currentRow[rowPos++] = PIXG(vidBuf.Buffer[(y*vidBuf.Width)+x]);
|
||||
currentRow[rowPos++] = PIXB(vidBuf.Buffer[(y*vidBuf.Width)+x]);
|
||||
auto colour = RGB<uint8_t>::Unpack(input.data()[i]);
|
||||
data.push_back(colour.Red);
|
||||
data.push_back(colour.Green);
|
||||
data.push_back(colour.Blue);
|
||||
}
|
||||
data.insert(data.end(), currentRow, currentRow+(vidBuf.Width*3));
|
||||
}
|
||||
delete [] currentRow;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static std::unique_ptr<PlaneAdapter<std::vector<uint32_t>>> readPNG(
|
||||
std::vector<char> const &data,
|
||||
// If omitted,
|
||||
// RGB data is returned with A=0xFF
|
||||
// RGBA data is returned as itself
|
||||
// If specified
|
||||
// RGB data is returned with A=0x00
|
||||
// RGBA data is blended against the background and returned with A=0x00
|
||||
std::optional<RGB<uint8_t>> background
|
||||
)
|
||||
{
|
||||
png_infop info = nullptr;
|
||||
auto deleter = [&info](png_struct *png) {
|
||||
png_destroy_read_struct(&png, &info, NULL);
|
||||
};
|
||||
auto png = std::unique_ptr<png_struct, decltype(deleter)>(
|
||||
png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
|
||||
[](png_structp png, png_const_charp msg) {
|
||||
fprintf(stderr, "PNG error: %s\n", msg);
|
||||
},
|
||||
[](png_structp png, png_const_charp msg) {
|
||||
fprintf(stderr, "PNG warning: %s\n", msg);
|
||||
}
|
||||
), deleter
|
||||
);
|
||||
if (!png)
|
||||
return nullptr;
|
||||
|
||||
// libpng might longjmp() here in case of error
|
||||
// Every time we create an object with a non-trivial destructor we must call setjmp again
|
||||
if (setjmp(png_jmpbuf(png.get())))
|
||||
return nullptr;
|
||||
|
||||
info = png_create_info_struct(png.get());
|
||||
if (!info)
|
||||
return nullptr;
|
||||
|
||||
auto it = data.begin();
|
||||
auto const end = data.end();
|
||||
auto readFn = [&it, end](png_structp png, png_bytep data, size_t length) {
|
||||
if (size_t(end - it) < length)
|
||||
png_error(png, "Tried to read beyond the buffer");
|
||||
std::copy_n(it, length, data);
|
||||
it += length;
|
||||
};
|
||||
|
||||
// See above
|
||||
if (setjmp(png_jmpbuf(png.get())))
|
||||
return nullptr;
|
||||
|
||||
png_set_read_fn(png.get(), static_cast<void *>(&readFn), [](png_structp png, png_bytep data, size_t length) {
|
||||
(*static_cast<decltype(readFn) *>(png_get_io_ptr(png)))(png, data, length);
|
||||
});
|
||||
png_set_user_limits(png.get(), RES.X, RES.Y); // Refuse to parse larger images
|
||||
png_read_info(png.get(), info);
|
||||
|
||||
auto output = std::make_unique<PlaneAdapter<std::vector<uint32_t>>>(
|
||||
Vec2<int>(png_get_image_width(png.get(), info), png_get_image_height(png.get(), info))
|
||||
);
|
||||
|
||||
std::vector<png_bytep> rowPointers(output->Size().Y);
|
||||
for (int y = 0; y < output->Size().Y; y++)
|
||||
rowPointers[y] = reinterpret_cast<png_bytep>(&*output->RowIterator(Vec2(0, y)));
|
||||
|
||||
// See above
|
||||
if (setjmp(png_jmpbuf(png.get())))
|
||||
return nullptr;
|
||||
|
||||
png_set_filler(png.get(), background ? 0x00 : 0xFF, PNG_FILLER_AFTER);
|
||||
png_set_bgr(png.get());
|
||||
|
||||
auto bitDepth = png_get_bit_depth(png.get(), info);
|
||||
auto colorType = png_get_color_type(png.get(), info);
|
||||
if (colorType == PNG_COLOR_TYPE_PALETTE)
|
||||
png_set_palette_to_rgb(png.get());
|
||||
if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8)
|
||||
png_set_expand_gray_1_2_4_to_8(png.get());
|
||||
if (bitDepth == 16)
|
||||
png_set_scale_16(png.get());
|
||||
if (png_get_valid(png.get(), info, PNG_INFO_tRNS))
|
||||
png_set_tRNS_to_alpha(png.get());
|
||||
if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||
png_set_gray_to_rgb(png.get());
|
||||
if (background)
|
||||
{
|
||||
png_color_16 colour;
|
||||
colour.red = background->Red;
|
||||
colour.green = background->Green;
|
||||
colour.blue = background->Blue;
|
||||
png_set_background(png.get(), &colour, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
|
||||
}
|
||||
|
||||
png_read_image(png.get(), rowPointers.data());
|
||||
return output;
|
||||
}
|
||||
|
||||
std::unique_ptr<PlaneAdapter<std::vector<pixel_rgba>>> format::PixelsFromPNG(std::vector<char> const &data)
|
||||
{
|
||||
return readPNG(data, std::nullopt);
|
||||
}
|
||||
|
||||
std::unique_ptr<PlaneAdapter<std::vector<pixel>>> format::PixelsFromPNG(std::vector<char> const &data, RGB<uint8_t> background)
|
||||
{
|
||||
return readPNG(data, background);
|
||||
}
|
||||
|
||||
std::unique_ptr<std::vector<char>> format::PixelsToPNG(PlaneAdapter<std::vector<pixel>> const &input)
|
||||
{
|
||||
png_infop info = nullptr;
|
||||
auto deleter = [&info](png_struct *png) {
|
||||
png_destroy_write_struct(&png, &info);
|
||||
};
|
||||
auto png = std::unique_ptr<png_struct, decltype(deleter)>(
|
||||
png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
|
||||
[](png_structp png, png_const_charp msg) {
|
||||
fprintf(stderr, "PNG error: %s\n", msg);
|
||||
},
|
||||
[](png_structp png, png_const_charp msg) {
|
||||
fprintf(stderr, "PNG warning: %s\n", msg);
|
||||
}
|
||||
), deleter
|
||||
);
|
||||
if (!png)
|
||||
return nullptr;
|
||||
|
||||
// libpng might longjmp() here in case of error
|
||||
// Every time we create an object with a non-trivial destructor we must call setjmp again
|
||||
if (setjmp(png_jmpbuf(png.get())))
|
||||
return nullptr;
|
||||
|
||||
|
||||
info = png_create_info_struct(png.get());
|
||||
if (!info)
|
||||
return nullptr;
|
||||
|
||||
std::vector<char> output;
|
||||
auto writeFn = [&output](png_structp png, png_bytep data, size_t length) {
|
||||
output.insert(output.end(), data, data + length);
|
||||
};
|
||||
|
||||
std::vector<png_const_bytep> rowPointers(input.Size().Y);
|
||||
for (int y = 0; y < input.Size().Y; y++)
|
||||
rowPointers[y] = reinterpret_cast<png_const_bytep>(&*input.RowIterator(Vec2(0, y)));
|
||||
|
||||
// See above
|
||||
if (setjmp(png_jmpbuf(png.get())))
|
||||
return nullptr;
|
||||
|
||||
png_set_write_fn(png.get(), static_cast<void *>(&writeFn), [](png_structp png, png_bytep data, size_t length) {
|
||||
(*static_cast<decltype(writeFn) *>(png_get_io_ptr(png)))(png, data, length);
|
||||
}, NULL);
|
||||
png_set_IHDR(png.get(), info, input.Size().X, input.Size().Y, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||
png_write_info(png.get(), info);
|
||||
png_set_filler(png.get(), 0x00, PNG_FILLER_AFTER);
|
||||
png_set_bgr(png.get());
|
||||
png_write_image(png.get(), const_cast<png_bytepp>(rowPointers.data()));
|
||||
png_write_end(png.get(), NULL);
|
||||
|
||||
return std::make_unique<std::vector<char>>(std::move(output));
|
||||
}
|
||||
|
||||
const static char hex[] = "0123456789ABCDEF";
|
||||
|
||||
ByteString format::URLEncode(ByteString source)
|
||||
|
12
src/Format.h
12
src/Format.h
@ -1,8 +1,9 @@
|
||||
#pragma once
|
||||
#include "Config.h"
|
||||
|
||||
#include "common/String.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "common/String.h"
|
||||
#include "common/Plane.h"
|
||||
#include "graphics/Pixel.h"
|
||||
|
||||
class VideoBuffer;
|
||||
|
||||
@ -13,7 +14,10 @@ namespace format
|
||||
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> VideoBufferToPPM(const VideoBuffer & vidBuf);
|
||||
std::vector<char> PixelsToPPM(PlaneAdapter<std::vector<pixel>> const &);
|
||||
std::unique_ptr<std::vector<char>> PixelsToPNG(PlaneAdapter<std::vector<pixel>> const &);
|
||||
std::unique_ptr<PlaneAdapter<std::vector<pixel_rgba>>> PixelsFromPNG(std::vector<char> const &);
|
||||
std::unique_ptr<PlaneAdapter<std::vector<pixel>>> PixelsFromPNG(std::vector<char> const &, RGB<uint8_t> background);
|
||||
void RenderTemperature(StringBuilder &sb, float temp, int scale);
|
||||
float StringToTemperature(String str, int defaultScale);
|
||||
}
|
||||
|
137
src/Misc.cpp
137
src/Misc.cpp
@ -1,140 +1,10 @@
|
||||
#include "Misc.h"
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
#include "common/tpt-minmax.h"
|
||||
#include "common/String.h"
|
||||
#include <cstring>
|
||||
#include <sys/types.h>
|
||||
#include <cmath>
|
||||
|
||||
#include "common/tpt-minmax.h"
|
||||
#include "common/String.h"
|
||||
|
||||
const static char hex[] = "0123456789ABCDEF";
|
||||
void strcaturl(char *dst, char *src)
|
||||
{
|
||||
char *d;
|
||||
unsigned char *s;
|
||||
|
||||
for (d=dst; *d; d++) ;
|
||||
|
||||
for (s=(unsigned char *)src; *s; s++)
|
||||
{
|
||||
if ((*s>='0' && *s<='9') ||
|
||||
(*s>='a' && *s<='z') ||
|
||||
(*s>='A' && *s<='Z'))
|
||||
*(d++) = *s;
|
||||
else
|
||||
{
|
||||
*(d++) = '%';
|
||||
*(d++) = hex[*s>>4];
|
||||
*(d++) = hex[*s&15];
|
||||
}
|
||||
}
|
||||
*d = 0;
|
||||
}
|
||||
|
||||
void strappend(char *dst, const char *src)
|
||||
{
|
||||
char *d;
|
||||
unsigned char *s;
|
||||
|
||||
for (d=dst; *d; d++) ;
|
||||
|
||||
for (s=(unsigned char *)src; *s; s++)
|
||||
{
|
||||
*(d++) = *s;
|
||||
}
|
||||
*d = 0;
|
||||
}
|
||||
|
||||
void *file_load(char *fn, int *size)
|
||||
{
|
||||
FILE *f = fopen(fn, "rb");
|
||||
void *s;
|
||||
|
||||
if (!f)
|
||||
return NULL;
|
||||
fseek(f, 0, SEEK_END);
|
||||
*size = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
s = malloc(*size);
|
||||
if (!s)
|
||||
{
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
int readsize = fread(s, *size, 1, f);
|
||||
fclose(f);
|
||||
if (readsize != 1)
|
||||
{
|
||||
free(s);
|
||||
return NULL;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
float hh, ss, vv, c, x;
|
||||
@ -216,9 +86,6 @@ 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);
|
||||
|
67
src/Misc.h
67
src/Misc.h
@ -1,12 +1,29 @@
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
#include "Config.h"
|
||||
#pragma once
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#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)
|
||||
{
|
||||
@ -55,52 +72,10 @@ inline float restrict_flt(float f, float min, float max)
|
||||
return f;
|
||||
}
|
||||
|
||||
void save_presets(int do_update);
|
||||
|
||||
void load_presets(void);
|
||||
|
||||
void strcaturl(char *dst, const char *src);
|
||||
|
||||
void strappend(char *dst, const char *src);
|
||||
|
||||
void *file_load(char *fn, int *size);
|
||||
|
||||
extern char *clipboard_text;
|
||||
|
||||
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 {
|
||||
float a,b,c,d;
|
||||
};
|
||||
typedef struct matrix2d matrix2d;
|
||||
|
||||
// column vector
|
||||
struct vector2d {
|
||||
float x,y;
|
||||
};
|
||||
typedef struct vector2d vector2d;
|
||||
|
||||
matrix2d m2d_multiply_m2d(matrix2d m1, matrix2d m2);
|
||||
vector2d m2d_multiply_v2d(matrix2d m, vector2d v);
|
||||
matrix2d m2d_multiply_float(matrix2d m, float s);
|
||||
vector2d v2d_multiply_float(vector2d v, float s);
|
||||
|
||||
vector2d v2d_add(vector2d v1, vector2d v2);
|
||||
vector2d v2d_sub(vector2d v1, vector2d v2);
|
||||
|
||||
matrix2d m2d_new(float me0, float me1, float me2, float me3);
|
||||
vector2d v2d_new(float x, float y);
|
||||
|
||||
extern vector2d v2d_zero;
|
||||
extern matrix2d m2d_identity;
|
||||
|
||||
class ByteString;
|
||||
|
||||
bool byteStringEqualsString(const ByteString &str, const char *data, size_t size);
|
||||
@ -110,5 +85,3 @@ bool byteStringEqualsLiteral(const ByteString &str, const char (&lit)[N])
|
||||
{
|
||||
return byteStringEqualsString(str, lit, N - 1U);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
534
src/PowderToy.cpp
Normal file
534
src/PowderToy.cpp
Normal file
@ -0,0 +1,534 @@
|
||||
#include "PowderToySDL.h"
|
||||
#include "Format.h"
|
||||
#include "X86KillDenormals.h"
|
||||
#include "prefs/GlobalPrefs.h"
|
||||
#include "client/Client.h"
|
||||
#include "client/GameSave.h"
|
||||
#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 "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/dialogues/ConfirmPrompt.h"
|
||||
#include "gui/dialogues/ErrorMessage.h"
|
||||
#include "gui/interface/Engine.h"
|
||||
#include "Config.h"
|
||||
#include "SimulationConfig.h"
|
||||
#include <optional>
|
||||
#include <climits>
|
||||
#include <iostream>
|
||||
#include <csignal>
|
||||
#include <SDL.h>
|
||||
|
||||
void LoadWindowPosition()
|
||||
{
|
||||
if (Client::Ref().IsFirstRun())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto &prefs = GlobalPrefs::Ref();
|
||||
int savedWindowX = prefs.Get("WindowX", INT_MAX);
|
||||
int savedWindowY = prefs.Get("WindowY", INT_MAX);
|
||||
|
||||
int borderTop, borderLeft;
|
||||
SDL_GetWindowBordersSize(sdl_window, &borderTop, &borderLeft, nullptr, nullptr);
|
||||
// Sometimes (Windows), the border size may not be reported for 200+ frames
|
||||
// So just have a default of 5 to ensure the window doesn't get stuck where it can't be moved
|
||||
if (borderTop == 0)
|
||||
borderTop = 5;
|
||||
|
||||
int numDisplays = SDL_GetNumVideoDisplays();
|
||||
SDL_Rect displayBounds;
|
||||
bool ok = false;
|
||||
for (int i = 0; i < numDisplays; i++)
|
||||
{
|
||||
SDL_GetDisplayBounds(i, &displayBounds);
|
||||
if (savedWindowX + borderTop > displayBounds.x && savedWindowY + borderLeft > displayBounds.y &&
|
||||
savedWindowX + borderTop < displayBounds.x + displayBounds.w &&
|
||||
savedWindowY + borderLeft < displayBounds.y + displayBounds.h)
|
||||
{
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ok)
|
||||
SDL_SetWindowPosition(sdl_window, savedWindowX + borderLeft, savedWindowY + borderTop);
|
||||
}
|
||||
|
||||
void SaveWindowPosition()
|
||||
{
|
||||
int x, y;
|
||||
SDL_GetWindowPosition(sdl_window, &x, &y);
|
||||
|
||||
int borderTop, borderLeft;
|
||||
SDL_GetWindowBordersSize(sdl_window, &borderTop, &borderLeft, nullptr, nullptr);
|
||||
|
||||
auto &prefs = GlobalPrefs::Ref();
|
||||
prefs.Set("WindowX", x - borderLeft);
|
||||
prefs.Set("WindowY", y - borderTop);
|
||||
}
|
||||
|
||||
void LargeScreenDialog()
|
||||
{
|
||||
StringBuilder message;
|
||||
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 << "\nTo undo this, hit Cancel. You can change this in settings at any time.";
|
||||
if (!ConfirmPrompt::Blocking("Large screen detected", message.Build()))
|
||||
{
|
||||
GlobalPrefs::Ref().Set("Scale", 1);
|
||||
ui::Engine::Ref().SetScale(1);
|
||||
}
|
||||
}
|
||||
|
||||
void TickClient()
|
||||
{
|
||||
Client::Ref().Tick();
|
||||
}
|
||||
|
||||
void BlueScreen(String detailMessage)
|
||||
{
|
||||
auto &engine = ui::Engine::Ref();
|
||||
engine.g->BlendFilledRect(engine.g->Size().OriginRect(), 0x1172A9_rgb .WithAlpha(0xD2));
|
||||
|
||||
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;
|
||||
|
||||
// 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;
|
||||
while(true)
|
||||
{
|
||||
while (SDL_PollEvent(&event))
|
||||
if(event.type == SDL_QUIT)
|
||||
exit(-1); // Don't use Platform::Exit, we're practically zombies at this point anyway.
|
||||
blit(engine.g->Data());
|
||||
}
|
||||
}
|
||||
|
||||
struct
|
||||
{
|
||||
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 },
|
||||
};
|
||||
|
||||
void SigHandler(int signal)
|
||||
{
|
||||
const char *message = "Unknown signal";
|
||||
for (auto *msg = signalMessages; msg->message; ++msg)
|
||||
{
|
||||
if (msg->sig == signal)
|
||||
{
|
||||
message = msg->message;
|
||||
break;
|
||||
}
|
||||
}
|
||||
BlueScreen(ByteString(message).FromUtf8());
|
||||
}
|
||||
|
||||
constexpr int SCALE_MAXIMUM = 10;
|
||||
constexpr int SCALE_MARGIN = 30;
|
||||
|
||||
int GuessBestScale()
|
||||
{
|
||||
const int widthNoMargin = desktopWidth - SCALE_MARGIN;
|
||||
const int widthGuess = widthNoMargin / WINDOWW;
|
||||
|
||||
const int heightNoMargin = desktopHeight - SCALE_MARGIN;
|
||||
const int heightGuess = heightNoMargin / WINDOWH;
|
||||
|
||||
int guess = std::min(widthGuess, heightGuess);
|
||||
if(guess < 1 || guess > SCALE_MAXIMUM)
|
||||
guess = 1;
|
||||
|
||||
return guess;
|
||||
}
|
||||
|
||||
struct ExplicitSingletons
|
||||
{
|
||||
// These need to be listed in the order they are populated in main.
|
||||
std::unique_ptr<GlobalPrefs> globalPrefs;
|
||||
http::RequestManagerPtr requestManager;
|
||||
std::unique_ptr<Client> client;
|
||||
std::unique_ptr<SaveRenderer> saveRenderer;
|
||||
std::unique_ptr<Favorite> favorite;
|
||||
std::unique_ptr<ui::Engine> engine;
|
||||
std::unique_ptr<GameController> gameController;
|
||||
};
|
||||
static std::unique_ptr<ExplicitSingletons> explicitSingletons;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
Platform::SetupCrt();
|
||||
Platform::Atexit([]() {
|
||||
// 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();
|
||||
});
|
||||
explicitSingletons = std::make_unique<ExplicitSingletons>();
|
||||
|
||||
|
||||
// https://bugzilla.libsdl.org/show_bug.cgi?id=3796
|
||||
if (SDL_Init(0) < 0)
|
||||
{
|
||||
fprintf(stderr, "Initializing SDL: %s\n", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
Platform::originalCwd = Platform::GetCwd();
|
||||
|
||||
using Argument = std::optional<ByteString>;
|
||||
std::map<ByteString, Argument> arguments;
|
||||
|
||||
for (auto i = 1; i < argc; ++i)
|
||||
{
|
||||
auto str = ByteString(argv[i]);
|
||||
if (str.BeginsWith("file://"))
|
||||
{
|
||||
arguments.insert({ "open", format::URLDecode(str.substr(7 /* length of the "file://" prefix */)) });
|
||||
}
|
||||
else if (str.BeginsWith("ptsave:"))
|
||||
{
|
||||
arguments.insert({ "ptsave", str });
|
||||
}
|
||||
else if (auto split = str.SplitBy(':'))
|
||||
{
|
||||
arguments.insert({ split.Before(), split.After() });
|
||||
}
|
||||
else if (auto split = str.SplitBy('='))
|
||||
{
|
||||
arguments.insert({ split.Before(), split.After() });
|
||||
}
|
||||
else if (str == "open" || str == "ptsave" || str == "ddir")
|
||||
{
|
||||
if (i + 1 < argc)
|
||||
{
|
||||
arguments.insert({ str, argv[i + 1] });
|
||||
i += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "no value provided for command line parameter " << str << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
arguments.insert({ str, "" }); // so .has_value() is true
|
||||
}
|
||||
}
|
||||
|
||||
auto ddirArg = arguments["ddir"];
|
||||
if (ddirArg.has_value())
|
||||
{
|
||||
if (Platform::ChangeDir(ddirArg.value()))
|
||||
Platform::sharedCwd = Platform::GetCwd();
|
||||
else
|
||||
perror("failed to chdir to requested ddir");
|
||||
}
|
||||
else
|
||||
{
|
||||
auto ddir = std::unique_ptr<char, decltype(&SDL_free)>(SDL_GetPrefPath(NULL, APPDATA), SDL_free);
|
||||
if (!Platform::FileExists("powder.pref"))
|
||||
{
|
||||
if (ddir)
|
||||
{
|
||||
if (!Platform::ChangeDir(ddir.get()))
|
||||
{
|
||||
perror("failed to chdir to default ddir");
|
||||
ddir.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ddir)
|
||||
{
|
||||
Platform::sharedCwd = ddir.get();
|
||||
}
|
||||
}
|
||||
// We're now in the correct directory, time to get prefs.
|
||||
explicitSingletons->globalPrefs = std::make_unique<GlobalPrefs>();
|
||||
|
||||
auto &prefs = GlobalPrefs::Ref();
|
||||
scale = prefs.Get("Scale", 1);
|
||||
auto graveExitsConsole = prefs.Get("GraveExitsConsole", true);
|
||||
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);
|
||||
|
||||
auto true_string = [](ByteString str) {
|
||||
str = str.ToLower();
|
||||
return str == "true" ||
|
||||
str == "t" ||
|
||||
str == "on" ||
|
||||
str == "yes" ||
|
||||
str == "y" ||
|
||||
str == "1" ||
|
||||
str == ""; // standalone "redirect" or "disable-bluescreen" or similar arguments
|
||||
};
|
||||
auto true_arg = [&true_string](Argument arg) {
|
||||
return arg.has_value() && true_string(arg.value());
|
||||
};
|
||||
|
||||
auto kioskArg = arguments["kiosk"];
|
||||
if (kioskArg.has_value())
|
||||
{
|
||||
fullscreen = true_string(kioskArg.value());
|
||||
prefs.Set("Fullscreen", fullscreen);
|
||||
}
|
||||
|
||||
if (true_arg(arguments["redirect"]))
|
||||
{
|
||||
FILE *new_stdout = freopen("stdout.log", "w", stdout);
|
||||
FILE *new_stderr = freopen("stderr.log", "w", stderr);
|
||||
if (!new_stdout || !new_stderr)
|
||||
{
|
||||
Platform::Exit(42);
|
||||
}
|
||||
}
|
||||
|
||||
auto scaleArg = arguments["scale"];
|
||||
if (scaleArg.has_value())
|
||||
{
|
||||
try
|
||||
{
|
||||
scale = scaleArg.value().ToNumber<int>();
|
||||
prefs.Set("Scale", scale);
|
||||
}
|
||||
catch (const std::runtime_error &e)
|
||||
{
|
||||
std::cerr << "failed to set scale: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
auto clientConfig = [&prefs](Argument arg, ByteString name, ByteString defaultValue) {
|
||||
ByteString value;
|
||||
if (arg.has_value())
|
||||
{
|
||||
value = arg.value();
|
||||
if (value == "")
|
||||
{
|
||||
value = defaultValue;
|
||||
}
|
||||
prefs.Set(name, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = prefs.Get(name, defaultValue);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
ByteString proxyString = clientConfig(arguments["proxy"], "Proxy", "");
|
||||
ByteString cafileString = clientConfig(arguments["cafile"], "CAFile", "");
|
||||
ByteString capathString = clientConfig(arguments["capath"], "CAPath", "");
|
||||
bool disableNetwork = true_arg(arguments["disable-network"]);
|
||||
explicitSingletons->requestManager = http::RequestManager::Create(proxyString, cafileString, capathString, disableNetwork);
|
||||
|
||||
explicitSingletons->client = std::make_unique<Client>();
|
||||
Client::Ref().Initialize();
|
||||
|
||||
explicitSingletons->saveRenderer = std::make_unique<SaveRenderer>();
|
||||
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(scale < 1 || scale > SCALE_MAXIMUM)
|
||||
scale = 1;
|
||||
|
||||
SDLOpen();
|
||||
|
||||
if (Client::Ref().IsFirstRun())
|
||||
{
|
||||
scale = GuessBestScale();
|
||||
if (scale > 1)
|
||||
{
|
||||
prefs.Set("Scale", scale);
|
||||
SDL_SetWindowSize(sdl_window, WINDOWW * scale, WINDOWH * scale);
|
||||
showLargeScreenDialog = true;
|
||||
}
|
||||
}
|
||||
|
||||
StopTextInput();
|
||||
|
||||
auto &engine = ui::Engine::Ref();
|
||||
engine.g = new Graphics();
|
||||
engine.Scale = scale;
|
||||
engine.GraveExitsConsole = graveExitsConsole;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (X86)
|
||||
{
|
||||
X86KillDenormals();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
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::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;
|
||||
}
|
||||
int saveId = saveIdPart.ToNumber<int>();
|
||||
|
||||
auto getSave = std::make_unique<http::GetSaveRequest>(saveId, 0);
|
||||
getSave->Start();
|
||||
getSave->Wait();
|
||||
std::unique_ptr<SaveInfo> newSave;
|
||||
try
|
||||
{
|
||||
newSave = getSave->Finish();
|
||||
}
|
||||
catch (const http::RequestError &ex)
|
||||
{
|
||||
throw std::runtime_error("Could not load save info\n" + ByteString(ex.what()));
|
||||
}
|
||||
auto getSaveData = std::make_unique<http::GetSaveDataRequest>(saveId, 0);
|
||||
getSaveData->Start();
|
||||
getSaveData->Wait();
|
||||
std::unique_ptr<GameSave> saveData;
|
||||
try
|
||||
{
|
||||
saveData = std::make_unique<GameSave>(getSaveData->Finish());
|
||||
}
|
||||
catch (const http::RequestError &ex)
|
||||
{
|
||||
throw std::runtime_error("Could not load save\n" + ByteString(ex.what()));
|
||||
}
|
||||
newSave->SetGameSave(std::move(saveData));
|
||||
gameController->LoadSave(std::move(newSave));
|
||||
}
|
||||
catch (std::exception & e)
|
||||
{
|
||||
new ErrorMessage("Error", ByteString(e.what()).FromUtf8());
|
||||
}
|
||||
}
|
||||
|
||||
EngineProcess();
|
||||
SaveWindowPosition();
|
||||
};
|
||||
|
||||
if (enableBluescreen)
|
||||
{
|
||||
try
|
||||
{
|
||||
wrapWithBluescreen();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
BlueScreen(ByteString(e.what()).FromUtf8());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wrapWithBluescreen();
|
||||
}
|
||||
Platform::Exit(0);
|
||||
return 0;
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/String.h"
|
||||
|
||||
void EngineProcess();
|
||||
void ClipboardPush(ByteString text);
|
||||
ByteString ClipboardPull();
|
||||
int GetModifiers();
|
||||
unsigned int GetTicks();
|
||||
void StartTextInput();
|
||||
void StopTextInput();
|
||||
void SetTextInputRect(int x, int y, int w, int h);
|
@ -1,442 +1,45 @@
|
||||
#include "Config.h"
|
||||
#include <ctime>
|
||||
#include <climits>
|
||||
#ifdef WIN
|
||||
#include <direct.h>
|
||||
#endif
|
||||
#include "SDLCompat.h"
|
||||
|
||||
#ifdef X86_SSE
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
#ifdef X86_SSE3
|
||||
#include <pmmintrin.h>
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#if defined(LIN)
|
||||
# include "icon_exe.png.h"
|
||||
#endif
|
||||
#include <stdexcept>
|
||||
|
||||
#ifndef WIN
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef MACOSX
|
||||
# include "common/macosx.h"
|
||||
#endif
|
||||
|
||||
#include "Format.h"
|
||||
#include "Misc.h"
|
||||
|
||||
#include "PowderToySDL.h"
|
||||
#include "graphics/Graphics.h"
|
||||
|
||||
#include "client/SaveInfo.h"
|
||||
#include "client/GameSave.h"
|
||||
#include "client/SaveFile.h"
|
||||
|
||||
#include "gui/game/GameController.h"
|
||||
#include "gui/game/GameView.h"
|
||||
#include "common/platform/Platform.h"
|
||||
#include "common/tpt-rand.h"
|
||||
#include "gui/font/FontEditor.h"
|
||||
#include "gui/dialogues/ErrorMessage.h"
|
||||
#include "gui/dialogues/ConfirmPrompt.h"
|
||||
#include "gui/interface/Keys.h"
|
||||
#include "gui/Style.h"
|
||||
#include "gui/interface/Engine.h"
|
||||
#include "Config.h"
|
||||
#include "SimulationConfig.h"
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include "SDLCompat.h"
|
||||
|
||||
int desktopWidth = 1280, desktopHeight = 1024;
|
||||
|
||||
SDL_Window * sdl_window;
|
||||
SDL_Renderer * sdl_renderer;
|
||||
SDL_Texture * sdl_texture;
|
||||
int scale = 1;
|
||||
bool fullscreen = false;
|
||||
bool altFullscreen = false;
|
||||
bool forceIntegerScaling = true;
|
||||
bool resizable = false;
|
||||
|
||||
|
||||
void StartTextInput()
|
||||
void LoadWindowPosition()
|
||||
{
|
||||
SDL_StartTextInput();
|
||||
}
|
||||
|
||||
void StopTextInput()
|
||||
void SaveWindowPosition()
|
||||
{
|
||||
SDL_StopTextInput();
|
||||
}
|
||||
|
||||
void SetTextInputRect(int x, int y, int w, int h)
|
||||
void LargeScreenDialog()
|
||||
{
|
||||
SDL_Rect rect;
|
||||
rect.x = x;
|
||||
rect.y = y;
|
||||
rect.w = w;
|
||||
rect.h = h;
|
||||
SDL_SetTextInputRect(&rect);
|
||||
}
|
||||
|
||||
void ClipboardPush(ByteString text)
|
||||
void TickClient()
|
||||
{
|
||||
SDL_SetClipboardText(text.c_str());
|
||||
}
|
||||
|
||||
ByteString ClipboardPull()
|
||||
struct ExplicitSingletons
|
||||
{
|
||||
return ByteString(SDL_GetClipboardText());
|
||||
}
|
||||
|
||||
int GetModifiers()
|
||||
{
|
||||
return SDL_GetModState();
|
||||
}
|
||||
|
||||
void CalculateMousePosition(int *x, int *y)
|
||||
{
|
||||
int globalMx, globalMy;
|
||||
SDL_GetGlobalMouseState(&globalMx, &globalMy);
|
||||
int windowX, windowY;
|
||||
SDL_GetWindowPosition(sdl_window, &windowX, &windowY);
|
||||
|
||||
if (x)
|
||||
*x = (globalMx - windowX) / scale;
|
||||
if (y)
|
||||
*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 (fullscreen || resizable)
|
||||
SDL_RenderClear(sdl_renderer);
|
||||
SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL);
|
||||
SDL_RenderPresent(sdl_renderer);
|
||||
}
|
||||
|
||||
void RecreateWindow();
|
||||
int SDLOpen()
|
||||
{
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0)
|
||||
{
|
||||
fprintf(stderr, "Initializing SDL: %s\n", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
RecreateWindow();
|
||||
|
||||
int displayIndex = SDL_GetWindowDisplayIndex(sdl_window);
|
||||
if (displayIndex >= 0)
|
||||
{
|
||||
SDL_Rect rect;
|
||||
if (!SDL_GetDisplayUsableBounds(displayIndex, &rect))
|
||||
{
|
||||
desktopWidth = rect.w;
|
||||
desktopHeight = rect.h;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LIN
|
||||
std::vector<pixel> imageData;
|
||||
int imgw, imgh;
|
||||
if (PngDataToPixels(imageData, imgw, imgh, reinterpret_cast<const char *>(icon_exe_png), icon_exe_png_size, false))
|
||||
{
|
||||
SDL_Surface *icon = SDL_CreateRGBSurfaceFrom(&imageData[0], imgw, imgh, 32, imgw * sizeof(pixel), 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
|
||||
SDL_SetWindowIcon(sdl_window, icon);
|
||||
SDL_FreeSurface(icon);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SDLSetScreen(int scale_, bool resizable_, bool fullscreen_, bool altFullscreen_, bool 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 || (changingResizable && resizable && !fullscreen))
|
||||
{
|
||||
RecreateWindow();
|
||||
return;
|
||||
}
|
||||
if (changingResizable)
|
||||
SDL_RestoreWindow(sdl_window);
|
||||
|
||||
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);
|
||||
SDL_SetWindowResizable(sdl_window, resizable ? SDL_TRUE : SDL_FALSE);
|
||||
}
|
||||
|
||||
void 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)
|
||||
{
|
||||
SDL_DestroyWindow(sdl_window);
|
||||
}
|
||||
|
||||
sdl_window = SDL_CreateWindow(APPNAME, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOWW * scale, WINDOWH * scale,
|
||||
flags);
|
||||
sdl_renderer = SDL_CreateRenderer(sdl_window, -1, 0);
|
||||
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);
|
||||
}
|
||||
|
||||
unsigned int GetTicks()
|
||||
{
|
||||
return SDL_GetTicks();
|
||||
}
|
||||
|
||||
int elapsedTime = 0, currentTime = 0, lastTime = 0, currentFrame = 0;
|
||||
unsigned int lastTick = 0;
|
||||
unsigned int lastFpsUpdate = 0;
|
||||
float fps = 0;
|
||||
ui::Engine * engine = NULL;
|
||||
bool showDoubleScreenDialog = false;
|
||||
float currentWidth, currentHeight;
|
||||
|
||||
int mousex = 0, mousey = 0;
|
||||
int mouseButton = 0;
|
||||
bool mouseDown = false;
|
||||
|
||||
bool calculatedInitialMouse = false, delay = false;
|
||||
bool hasMouseMoved = false;
|
||||
|
||||
void EventProcess(SDL_Event event)
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_QUIT:
|
||||
if (engine->GetFastQuit() || engine->CloseWindow())
|
||||
engine->Exit();
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
if (SDL_GetModState() & KMOD_GUI)
|
||||
{
|
||||
break;
|
||||
}
|
||||
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);
|
||||
break;
|
||||
case SDL_KEYUP:
|
||||
if (SDL_GetModState() & KMOD_GUI)
|
||||
{
|
||||
break;
|
||||
}
|
||||
engine->onKeyRelease(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);
|
||||
break;
|
||||
case SDL_TEXTINPUT:
|
||||
if (SDL_GetModState() & KMOD_GUI)
|
||||
{
|
||||
break;
|
||||
}
|
||||
engine->onTextInput(ByteString(event.text.text).FromUtf8());
|
||||
break;
|
||||
case SDL_TEXTEDITING:
|
||||
if (SDL_GetModState() & KMOD_GUI)
|
||||
{
|
||||
break;
|
||||
}
|
||||
engine->onTextEditing(ByteString(event.edit.text).FromUtf8(), event.edit.start);
|
||||
break;
|
||||
case SDL_MOUSEWHEEL:
|
||||
{
|
||||
// int x = event.wheel.x;
|
||||
int y = event.wheel.y;
|
||||
if (event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED)
|
||||
{
|
||||
// x *= -1;
|
||||
y *= -1;
|
||||
}
|
||||
|
||||
engine->onMouseWheel(mousex, mousey, y); // TODO: pass x?
|
||||
break;
|
||||
}
|
||||
case SDL_MOUSEMOTION:
|
||||
mousex = event.motion.x;
|
||||
mousey = event.motion.y;
|
||||
engine->onMouseMove(mousex, mousey);
|
||||
|
||||
hasMouseMoved = true;
|
||||
break;
|
||||
case SDL_DROPFILE:
|
||||
engine->onFileDrop(event.drop.file);
|
||||
SDL_free(event.drop.file);
|
||||
break;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
// if mouse hasn't moved yet, sdl will send 0,0. We don't want that
|
||||
if (hasMouseMoved)
|
||||
{
|
||||
mousex = event.motion.x;
|
||||
mousey = event.motion.y;
|
||||
}
|
||||
mouseButton = event.button.button;
|
||||
engine->onMouseClick(event.motion.x, event.motion.y, mouseButton);
|
||||
|
||||
mouseDown = true;
|
||||
#if !defined(NDEBUG) && !defined(DEBUG)
|
||||
SDL_CaptureMouse(SDL_TRUE);
|
||||
#endif
|
||||
break;
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
// if mouse hasn't moved yet, sdl will send 0,0. We don't want that
|
||||
if (hasMouseMoved)
|
||||
{
|
||||
mousex = event.motion.x;
|
||||
mousey = event.motion.y;
|
||||
}
|
||||
mouseButton = event.button.button;
|
||||
engine->onMouseUnclick(mousex, mousey, mouseButton);
|
||||
|
||||
mouseDown = false;
|
||||
#if !defined(NDEBUG) && !defined(DEBUG)
|
||||
SDL_CaptureMouse(SDL_FALSE);
|
||||
#endif
|
||||
break;
|
||||
case SDL_WINDOWEVENT:
|
||||
{
|
||||
switch (event.window.event)
|
||||
{
|
||||
case SDL_WINDOWEVENT_SHOWN:
|
||||
if (!calculatedInitialMouse)
|
||||
{
|
||||
//initial mouse coords, sdl won't tell us this if mouse hasn't moved
|
||||
CalculateMousePosition(&mousex, &mousey);
|
||||
engine->onMouseMove(mousex, mousey);
|
||||
calculatedInitialMouse = true;
|
||||
}
|
||||
break;
|
||||
// This event would be needed in certain glitchy cases of window resizing
|
||||
// But for all currently tested cases, it isn't needed
|
||||
/*case SDL_WINDOWEVENT_RESIZED:
|
||||
{
|
||||
float width = event.window.data1;
|
||||
float height = event.window.data2;
|
||||
|
||||
currentWidth = width;
|
||||
currentHeight = height;
|
||||
// this "* scale" thing doesn't really work properly
|
||||
// currently there is a bug where input doesn't scale properly after resizing, only when double scale mode is active
|
||||
inputScaleH = (float)WINDOWW * scale / currentWidth;
|
||||
inputScaleV = (float)WINDOWH * scale / currentHeight;
|
||||
std::cout << "Changing input scale to " << inputScaleH << "x" << inputScaleV << std::endl;
|
||||
break;
|
||||
}*/
|
||||
// This would send a mouse up event when focus is lost
|
||||
// Not even sdl itself will know when the mouse was released if it happens in another window
|
||||
// So it will ignore the next mouse down (after tpt is re-focused) and not send any events at all
|
||||
// This is more unintuitive than pretending the mouse is still down when it's not, so this code is commented out
|
||||
/*case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||
if (mouseDown)
|
||||
{
|
||||
mouseDown = false;
|
||||
engine->onMouseUnclick(mousex, mousey, mouseButton);
|
||||
}
|
||||
break;*/
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EngineProcess()
|
||||
{
|
||||
double frameTimeAvg = 0.0f, correctedFrameTimeAvg = 0.0f;
|
||||
SDL_Event event;
|
||||
while(engine->Running())
|
||||
{
|
||||
int frameStart = SDL_GetTicks();
|
||||
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();
|
||||
engine->Draw();
|
||||
|
||||
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->vid);
|
||||
|
||||
int frameTime = SDL_GetTicks() - frameStart;
|
||||
frameTimeAvg = frameTimeAvg * 0.8 + frameTime * 0.2;
|
||||
float fpsLimit = ui::Engine::Ref().FpsLimit;
|
||||
if(fpsLimit > 2)
|
||||
{
|
||||
double offset = 1000.0 / fpsLimit - frameTimeAvg;
|
||||
if(offset > 0)
|
||||
SDL_Delay(Uint32(offset + 0.5));
|
||||
}
|
||||
int correctedFrameTime = SDL_GetTicks() - frameStart;
|
||||
correctedFrameTimeAvg = correctedFrameTimeAvg * 0.95 + correctedFrameTime * 0.05;
|
||||
if (frameStart - lastFpsUpdate > 200)
|
||||
{
|
||||
engine->SetFps(1000.0 / correctedFrameTimeAvg);
|
||||
lastFpsUpdate = frameStart;
|
||||
}
|
||||
if (frameStart - lastTick > 100)
|
||||
{
|
||||
lastTick = frameStart;
|
||||
}
|
||||
if (showDoubleScreenDialog)
|
||||
{
|
||||
showDoubleScreenDialog = false;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
std::cout << "Breaking out of EngineProcess" << std::endl;
|
||||
#endif
|
||||
}
|
||||
// These need to be listed in the order they are populated in main.
|
||||
std::unique_ptr<ui::Engine> engine;
|
||||
};
|
||||
static std::unique_ptr<ExplicitSingletons> explicitSingletons;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
currentWidth = WINDOWW;
|
||||
currentHeight = WINDOWH;
|
||||
Platform::SetupCrt();
|
||||
Platform::Atexit([]() {
|
||||
SDLClose();
|
||||
explicitSingletons.reset();
|
||||
});
|
||||
explicitSingletons = std::make_unique<ExplicitSingletons>();
|
||||
|
||||
scale = 1;
|
||||
if (argc >= 3)
|
||||
@ -457,6 +60,8 @@ int main(int argc, char * argv[])
|
||||
if(scale < 1 || scale > 10)
|
||||
scale = 1;
|
||||
|
||||
explicitSingletons->engine = std::make_unique<ui::Engine>();
|
||||
|
||||
SDLOpen();
|
||||
|
||||
StopTextInput();
|
||||
@ -468,38 +73,21 @@ int main(int argc, char * argv[])
|
||||
ui::Engine::Ref().SetAltFullscreen(altFullscreen);
|
||||
ui::Engine::Ref().SetForceIntegerScaling(forceIntegerScaling);
|
||||
|
||||
engine = &ui::Engine::Ref();
|
||||
engine->SetMaxSize(desktopWidth, desktopHeight);
|
||||
engine->Begin(WINDOWW, WINDOWH);
|
||||
engine->SetFastQuit(true);
|
||||
|
||||
GameController * gameController = NULL;
|
||||
auto &engine = ui::Engine::Ref();
|
||||
engine.Begin();
|
||||
engine.SetFastQuit(true);
|
||||
|
||||
if (argc >= 2)
|
||||
{
|
||||
engine->ShowWindow(new FontEditor(argv[1]));
|
||||
engine.ShowWindow(new FontEditor(argv[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "path to font.cpp not supplied" << std::endl;
|
||||
exit(1);
|
||||
Platform::Exit(1);
|
||||
}
|
||||
|
||||
EngineProcess();
|
||||
ui::Engine::Ref().CloseWindow();
|
||||
delete gameController;
|
||||
delete ui::Engine::Ref().g;
|
||||
if (SDL_GetWindowFlags(sdl_window) & SDL_WINDOW_OPENGL)
|
||||
{
|
||||
// * nvidia-460 egl registers callbacks with x11 that end up being called
|
||||
// after egl is unloaded unless we grab it here and release it after
|
||||
// sdl closes the display. this is an nvidia driver weirdness but
|
||||
// technically an sdl bug. glfw has this fixed:
|
||||
// https://github.com/glfw/glfw/commit/9e6c0c747be838d1f3dc38c2924a47a42416c081
|
||||
SDL_GL_LoadLibrary(NULL);
|
||||
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
||||
SDL_GL_UnloadLibrary();
|
||||
}
|
||||
SDL_Quit();
|
||||
Platform::Exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,42 +1,17 @@
|
||||
#include "Config.h"
|
||||
#include "graphics/Graphics.h"
|
||||
#include "graphics/Renderer.h"
|
||||
|
||||
#include "common/String.h"
|
||||
#include "common/tpt-rand.h"
|
||||
#include "Format.h"
|
||||
#include "gui/interface/Engine.h"
|
||||
#include "client/GameSave.h"
|
||||
#include "simulation/Simulation.h"
|
||||
#include "common/platform/Platform.h"
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
#include "common/String.h"
|
||||
#include "Format.h"
|
||||
#include "gui/interface/Engine.h"
|
||||
|
||||
#include "client/GameSave.h"
|
||||
#include "simulation/Simulation.h"
|
||||
|
||||
|
||||
void EngineProcess() {}
|
||||
void ClipboardPush(ByteString) {}
|
||||
ByteString ClipboardPull() { return ""; }
|
||||
int GetModifiers() { return 0; }
|
||||
void SetCursorEnabled(int enabled) {}
|
||||
unsigned int GetTicks() { return 0; }
|
||||
|
||||
static bool ReadFile(std::vector<char> &fileData, ByteString filename)
|
||||
{
|
||||
std::ifstream f(filename, std::ios::binary);
|
||||
if (f) f.seekg(0, std::ios::end);
|
||||
if (f) fileData.resize(f.tellg());
|
||||
if (f) f.seekg(0);
|
||||
if (f) f.read(&fileData[0], fileData.size());
|
||||
if (!f)
|
||||
{
|
||||
std::cerr << "ReadFile: " << filename << ": " << strerror(errno) << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (!argv[1] || !argv[2]) {
|
||||
@ -47,15 +22,15 @@ int main(int argc, char *argv[])
|
||||
auto outputFilename = ByteString(argv[2]) + ".png";
|
||||
|
||||
std::vector<char> fileData;
|
||||
if (!ReadFile(fileData, inputFilename))
|
||||
if (!Platform::ReadFile(fileData, inputFilename))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
GameSave * gameSave = NULL;
|
||||
std::unique_ptr<GameSave> gameSave;
|
||||
try
|
||||
{
|
||||
gameSave = new GameSave(fileData);
|
||||
gameSave = std::make_unique<GameSave>(fileData, false);
|
||||
}
|
||||
catch (ParseException &e)
|
||||
{
|
||||
@ -65,11 +40,11 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
Simulation * sim = new Simulation();
|
||||
Renderer * ren = new Renderer(new Graphics(), sim);
|
||||
Renderer * ren = new Renderer(sim);
|
||||
|
||||
if (gameSave)
|
||||
{
|
||||
sim->Load(gameSave, true);
|
||||
sim->Load(gameSave.get(), true, { 0, 0 });
|
||||
|
||||
//Render save
|
||||
ren->decorations_enable = true;
|
||||
@ -81,19 +56,19 @@ int main(int argc, char *argv[])
|
||||
frame--;
|
||||
ren->render_parts();
|
||||
ren->render_fire();
|
||||
ren->clearScreen(1.0f);
|
||||
ren->clearScreen();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
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));
|
||||
}
|
||||
|
||||
ren->RenderBegin();
|
||||
ren->RenderEnd();
|
||||
|
||||
VideoBuffer screenBuffer = ren->DumpFrame();
|
||||
screenBuffer.WritePNG(outputFilename);
|
||||
if (auto data = ren->DumpFrame().ToPNG())
|
||||
Platform::WriteFile(*data, outputFilename);
|
||||
}
|
||||
|
@ -1,66 +1,17 @@
|
||||
#include "PowderToySDL.h"
|
||||
#include "SimulationConfig.h"
|
||||
#include "WindowIcon.h"
|
||||
#include "Config.h"
|
||||
#include "common/tpt-minmax.h"
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <ctime>
|
||||
#include <climits>
|
||||
#ifdef WIN
|
||||
#include <direct.h>
|
||||
#endif
|
||||
#include "SDLCompat.h"
|
||||
|
||||
#ifdef X86_SSE
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
#ifdef X86_SSE3
|
||||
#include <pmmintrin.h>
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#if defined(LIN)
|
||||
# include "icon_exe.png.h"
|
||||
#endif
|
||||
#include <csignal>
|
||||
#include <stdexcept>
|
||||
|
||||
#ifndef WIN
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#ifdef MACOSX
|
||||
# ifdef DEBUG
|
||||
# undef DEBUG
|
||||
# define DEBUG 1
|
||||
# endif
|
||||
# include <CoreServices/CoreServices.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "Format.h"
|
||||
#include "Misc.h"
|
||||
|
||||
#include "client/Client.h"
|
||||
#include "client/GameSave.h"
|
||||
#include "client/SaveFile.h"
|
||||
#include "client/SaveInfo.h"
|
||||
#include "common/Platform.h"
|
||||
#include "graphics/Graphics.h"
|
||||
#include "gui/Style.h"
|
||||
|
||||
#include "gui/game/GameController.h"
|
||||
#include "gui/game/GameView.h"
|
||||
#include "gui/dialogues/ConfirmPrompt.h"
|
||||
#include "gui/dialogues/ErrorMessage.h"
|
||||
#include "gui/interface/Engine.h"
|
||||
#include "gui/interface/Keys.h"
|
||||
#include "graphics/Graphics.h"
|
||||
#include "common/platform/Platform.h"
|
||||
#include <iostream>
|
||||
|
||||
#include "SDLCompat.h"
|
||||
|
||||
int desktopWidth = 1280, desktopHeight = 1024;
|
||||
|
||||
SDL_Window * sdl_window;
|
||||
SDL_Renderer * sdl_renderer;
|
||||
SDL_Texture * sdl_texture;
|
||||
int desktopWidth = 1280;
|
||||
int desktopHeight = 1024;
|
||||
SDL_Window *sdl_window = NULL;
|
||||
SDL_Renderer *sdl_renderer = NULL;
|
||||
SDL_Texture *sdl_texture = NULL;
|
||||
int scale = 1;
|
||||
bool fullscreen = false;
|
||||
bool altFullscreen = false;
|
||||
@ -68,6 +19,15 @@ bool forceIntegerScaling = true;
|
||||
bool resizable = false;
|
||||
bool momentumScroll = true;
|
||||
bool showAvatars = true;
|
||||
uint64_t lastTick = 0;
|
||||
uint64_t lastFpsUpdate = 0;
|
||||
bool showLargeScreenDialog = false;
|
||||
int mousex = 0;
|
||||
int mousey = 0;
|
||||
int mouseButton = 0;
|
||||
bool mouseDown = false;
|
||||
bool calculatedInitialMouse = false;
|
||||
bool hasMouseMoved = false;
|
||||
|
||||
void StartTextInput()
|
||||
{
|
||||
@ -104,46 +64,9 @@ int GetModifiers()
|
||||
return SDL_GetModState();
|
||||
}
|
||||
|
||||
void LoadWindowPosition()
|
||||
unsigned int GetTicks()
|
||||
{
|
||||
int savedWindowX = Client::Ref().GetPrefInteger("WindowX", INT_MAX);
|
||||
int savedWindowY = Client::Ref().GetPrefInteger("WindowY", INT_MAX);
|
||||
|
||||
int borderTop, borderLeft;
|
||||
SDL_GetWindowBordersSize(sdl_window, &borderTop, &borderLeft, nullptr, nullptr);
|
||||
// Sometimes (Windows), the border size may not be reported for 200+ frames
|
||||
// So just have a default of 5 to ensure the window doesn't get stuck where it can't be moved
|
||||
if (borderTop == 0)
|
||||
borderTop = 5;
|
||||
|
||||
int numDisplays = SDL_GetNumVideoDisplays();
|
||||
SDL_Rect displayBounds;
|
||||
bool ok = false;
|
||||
for (int i = 0; i < numDisplays; i++)
|
||||
{
|
||||
SDL_GetDisplayBounds(i, &displayBounds);
|
||||
if (savedWindowX + borderTop > displayBounds.x && savedWindowY + borderLeft > displayBounds.y &&
|
||||
savedWindowX + borderTop < displayBounds.x + displayBounds.w &&
|
||||
savedWindowY + borderLeft < displayBounds.y + displayBounds.h)
|
||||
{
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ok)
|
||||
SDL_SetWindowPosition(sdl_window, savedWindowX + borderLeft, savedWindowY + borderTop);
|
||||
}
|
||||
|
||||
void SaveWindowPosition()
|
||||
{
|
||||
int x, y;
|
||||
SDL_GetWindowPosition(sdl_window, &x, &y);
|
||||
|
||||
int borderTop, borderLeft;
|
||||
SDL_GetWindowBordersSize(sdl_window, &borderTop, &borderLeft, nullptr, nullptr);
|
||||
|
||||
Client::Ref().SetPref("WindowX", x - borderLeft);
|
||||
Client::Ref().SetPref("WindowY", y - borderTop);
|
||||
return SDL_GetTicks();
|
||||
}
|
||||
|
||||
void CalculateMousePosition(int *x, int *y)
|
||||
@ -169,19 +92,18 @@ void blit(pixel * vid)
|
||||
SDL_RenderPresent(sdl_renderer);
|
||||
}
|
||||
|
||||
bool RecreateWindow();
|
||||
void SDLOpen()
|
||||
{
|
||||
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
|
||||
{
|
||||
fprintf(stderr, "Initializing SDL (video subsystem): %s\n", SDL_GetError());
|
||||
exit(-1);
|
||||
Platform::Exit(-1);
|
||||
}
|
||||
|
||||
if (!RecreateWindow())
|
||||
{
|
||||
fprintf(stderr, "Creating SDL window: %s\n", SDL_GetError());
|
||||
exit(-1);
|
||||
Platform::Exit(-1);
|
||||
}
|
||||
|
||||
int displayIndex = SDL_GetWindowDisplayIndex(sdl_window);
|
||||
@ -195,16 +117,26 @@ void SDLOpen()
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LIN
|
||||
std::vector<pixel> imageData;
|
||||
int imgw, imgh;
|
||||
if (PngDataToPixels(imageData, imgw, imgh, reinterpret_cast<const char *>(icon_exe_png), icon_exe_png_size, false))
|
||||
if constexpr (SET_WINDOW_ICON)
|
||||
{
|
||||
SDL_Surface *icon = SDL_CreateRGBSurfaceFrom(&imageData[0], imgw, imgh, 32, imgw * sizeof(pixel), 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
|
||||
SDL_SetWindowIcon(sdl_window, icon);
|
||||
SDL_FreeSurface(icon);
|
||||
WindowIcon(sdl_window);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void SDLClose()
|
||||
{
|
||||
if (SDL_GetWindowFlags(sdl_window) & SDL_WINDOW_OPENGL)
|
||||
{
|
||||
// * nvidia-460 egl registers callbacks with x11 that end up being called
|
||||
// after egl is unloaded unless we grab it here and release it after
|
||||
// sdl closes the display. this is an nvidia driver weirdness but
|
||||
// technically an sdl bug. glfw has this fixed:
|
||||
// https://github.com/glfw/glfw/commit/9e6c0c747be838d1f3dc38c2924a47a42416c081
|
||||
SDL_GL_LoadLibrary(NULL);
|
||||
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
||||
SDL_GL_UnloadLibrary();
|
||||
}
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void SDLSetScreen(int scale_, bool resizable_, bool fullscreen_, bool altFullscreen_, bool forceIntegerScaling_)
|
||||
@ -286,39 +218,19 @@ bool RecreateWindow()
|
||||
//SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
|
||||
//SDL_SetWindowResizable(sdl_window, SDL_TRUE);
|
||||
|
||||
if (!Client::Ref().IsFirstRun())
|
||||
LoadWindowPosition();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int GetTicks()
|
||||
{
|
||||
return SDL_GetTicks();
|
||||
}
|
||||
|
||||
int elapsedTime = 0, currentTime = 0, lastTime = 0, currentFrame = 0;
|
||||
unsigned int lastTick = 0;
|
||||
unsigned int lastFpsUpdate = 0;
|
||||
float fps = 0;
|
||||
ui::Engine * engine = NULL;
|
||||
bool showLargeScreenDialog = false;
|
||||
float currentWidth, currentHeight;
|
||||
|
||||
int mousex = 0, mousey = 0;
|
||||
int mouseButton = 0;
|
||||
bool mouseDown = false;
|
||||
|
||||
bool calculatedInitialMouse = false, delay = false;
|
||||
bool hasMouseMoved = false;
|
||||
|
||||
void EventProcess(SDL_Event event)
|
||||
void EventProcess(const SDL_Event &event)
|
||||
{
|
||||
auto &engine = ui::Engine::Ref();
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_QUIT:
|
||||
if (engine->GetFastQuit() || engine->CloseWindow())
|
||||
engine->Exit();
|
||||
if (engine.GetFastQuit() || engine.CloseWindow())
|
||||
engine.Exit();
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
if (SDL_GetModState() & KMOD_GUI)
|
||||
@ -326,30 +238,30 @@ void EventProcess(SDL_Event event)
|
||||
break;
|
||||
}
|
||||
if (!event.key.repeat && event.key.keysym.sym == 'q' && (event.key.keysym.mod&KMOD_CTRL))
|
||||
engine->ConfirmExit();
|
||||
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);
|
||||
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);
|
||||
break;
|
||||
case SDL_KEYUP:
|
||||
if (SDL_GetModState() & KMOD_GUI)
|
||||
{
|
||||
break;
|
||||
}
|
||||
engine->onKeyRelease(event.key.keysym.sym, event.key.keysym.scancode, event.key.repeat, event.key.keysym.mod&KMOD_SHIFT, event.key.keysym.mod&KMOD_CTRL, event.key.keysym.mod&KMOD_ALT);
|
||||
engine.onKeyRelease(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);
|
||||
break;
|
||||
case SDL_TEXTINPUT:
|
||||
if (SDL_GetModState() & KMOD_GUI)
|
||||
{
|
||||
break;
|
||||
}
|
||||
engine->onTextInput(ByteString(event.text.text).FromUtf8());
|
||||
engine.onTextInput(ByteString(event.text.text).FromUtf8());
|
||||
break;
|
||||
case SDL_TEXTEDITING:
|
||||
if (SDL_GetModState() & KMOD_GUI)
|
||||
{
|
||||
break;
|
||||
}
|
||||
engine->onTextEditing(ByteString(event.edit.text).FromUtf8(), event.edit.start);
|
||||
engine.onTextEditing(ByteString(event.edit.text).FromUtf8(), event.edit.start);
|
||||
break;
|
||||
case SDL_MOUSEWHEEL:
|
||||
{
|
||||
@ -361,18 +273,18 @@ void EventProcess(SDL_Event event)
|
||||
y *= -1;
|
||||
}
|
||||
|
||||
engine->onMouseWheel(mousex, mousey, y); // TODO: pass x?
|
||||
engine.onMouseWheel(mousex, mousey, y); // TODO: pass x?
|
||||
break;
|
||||
}
|
||||
case SDL_MOUSEMOTION:
|
||||
mousex = event.motion.x;
|
||||
mousey = event.motion.y;
|
||||
engine->onMouseMove(mousex, mousey);
|
||||
engine.onMouseMove(mousex, mousey);
|
||||
|
||||
hasMouseMoved = true;
|
||||
break;
|
||||
case SDL_DROPFILE:
|
||||
engine->onFileDrop(event.drop.file);
|
||||
engine.onFileDrop(event.drop.file);
|
||||
SDL_free(event.drop.file);
|
||||
break;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
@ -383,12 +295,13 @@ void EventProcess(SDL_Event event)
|
||||
mousey = event.button.y;
|
||||
}
|
||||
mouseButton = event.button.button;
|
||||
engine->onMouseClick(mousex, mousey, mouseButton);
|
||||
engine.onMouseClick(mousex, mousey, mouseButton);
|
||||
|
||||
mouseDown = true;
|
||||
#if !defined(NDEBUG) && !defined(DEBUG)
|
||||
if constexpr (!DEBUG)
|
||||
{
|
||||
SDL_CaptureMouse(SDL_TRUE);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
// if mouse hasn't moved yet, sdl will send 0,0. We don't want that
|
||||
@ -398,12 +311,13 @@ void EventProcess(SDL_Event event)
|
||||
mousey = event.button.y;
|
||||
}
|
||||
mouseButton = event.button.button;
|
||||
engine->onMouseUnclick(mousex, mousey, mouseButton);
|
||||
engine.onMouseUnclick(mousex, mousey, mouseButton);
|
||||
|
||||
mouseDown = false;
|
||||
#if !defined(NDEBUG) && !defined(DEBUG)
|
||||
if constexpr (!DEBUG)
|
||||
{
|
||||
SDL_CaptureMouse(SDL_FALSE);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case SDL_WINDOWEVENT:
|
||||
{
|
||||
@ -414,119 +328,79 @@ void EventProcess(SDL_Event event)
|
||||
{
|
||||
//initial mouse coords, sdl won't tell us this if mouse hasn't moved
|
||||
CalculateMousePosition(&mousex, &mousey);
|
||||
engine->initialMouse(mousex, mousey);
|
||||
engine->onMouseMove(mousex, mousey);
|
||||
engine.initialMouse(mousex, mousey);
|
||||
engine.onMouseMove(mousex, mousey);
|
||||
calculatedInitialMouse = true;
|
||||
}
|
||||
break;
|
||||
// This event would be needed in certain glitchy cases of window resizing
|
||||
// But for all currently tested cases, it isn't needed
|
||||
/*case SDL_WINDOWEVENT_RESIZED:
|
||||
{
|
||||
float width = event.window.data1;
|
||||
float height = event.window.data2;
|
||||
|
||||
currentWidth = width;
|
||||
currentHeight = height;
|
||||
// this "* scale" thing doesn't really work properly
|
||||
// currently there is a bug where input doesn't scale properly after resizing, only when double scale mode is active
|
||||
inputScaleH = (float)WINDOWW * scale / currentWidth;
|
||||
inputScaleV = (float)WINDOWH * scale / currentHeight;
|
||||
std::cout << "Changing input scale to " << inputScaleH << "x" << inputScaleV << std::endl;
|
||||
break;
|
||||
}*/
|
||||
// This would send a mouse up event when focus is lost
|
||||
// Not even sdl itself will know when the mouse was released if it happens in another window
|
||||
// So it will ignore the next mouse down (after tpt is re-focused) and not send any events at all
|
||||
// This is more unintuitive than pretending the mouse is still down when it's not, so this code is commented out
|
||||
/*case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||
if (mouseDown)
|
||||
{
|
||||
mouseDown = false;
|
||||
engine->onMouseUnclick(mousex, mousey, mouseButton);
|
||||
}
|
||||
break;*/
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LargeScreenDialog()
|
||||
{
|
||||
StringBuilder message;
|
||||
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 << "\nTo undo this, hit Cancel. You can change this in settings at any time.";
|
||||
if (!ConfirmPrompt::Blocking("Large screen detected", message.Build()))
|
||||
{
|
||||
Client::Ref().SetPref("Scale", 1);
|
||||
engine->SetScale(1);
|
||||
}
|
||||
}
|
||||
|
||||
void EngineProcess()
|
||||
{
|
||||
double frameTimeAvg = 0.0f, correctedFrameTimeAvg = 0.0f;
|
||||
double correctedFrameTimeAvg = 0;
|
||||
SDL_Event event;
|
||||
|
||||
int drawingTimer = 0;
|
||||
int frameStart = 0;
|
||||
uint64_t drawingTimer = 0;
|
||||
auto frameStart = uint64_t(SDL_GetTicks()) * UINT64_C(1'000'000);
|
||||
|
||||
while(engine->Running())
|
||||
auto &engine = ui::Engine::Ref();
|
||||
while(engine.Running())
|
||||
{
|
||||
int oldFrameStart = frameStart;
|
||||
frameStart = SDL_GetTicks();
|
||||
drawingTimer += frameStart - oldFrameStart;
|
||||
|
||||
if(engine->Broken()) { engine->UnBreak(); break; }
|
||||
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; }
|
||||
if(engine.Broken()) { engine.UnBreak(); break; }
|
||||
|
||||
engine->Tick();
|
||||
engine.Tick();
|
||||
|
||||
int drawcap = ui::Engine::Ref().GetDrawingFrequencyLimit();
|
||||
if (!drawcap || drawingTimer > 1000.f/drawcap)
|
||||
if (!drawcap || drawingTimer > 1e9f / drawcap)
|
||||
{
|
||||
engine->Draw();
|
||||
engine.Draw();
|
||||
drawingTimer = 0;
|
||||
|
||||
if (scale != engine->Scale || fullscreen != engine->Fullscreen ||
|
||||
altFullscreen != engine->GetAltFullscreen() ||
|
||||
forceIntegerScaling != engine->GetForceIntegerScaling() || resizable != engine->GetResizable())
|
||||
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());
|
||||
SDLSetScreen(engine.Scale, engine.GetResizable(), engine.Fullscreen, engine.GetAltFullscreen(),
|
||||
engine.GetForceIntegerScaling());
|
||||
}
|
||||
|
||||
blit(engine->g->vid);
|
||||
blit(engine.g->Data());
|
||||
}
|
||||
|
||||
int frameTime = SDL_GetTicks() - frameStart;
|
||||
frameTimeAvg = frameTimeAvg * 0.8 + frameTime * 0.2;
|
||||
float fpsLimit = ui::Engine::Ref().FpsLimit;
|
||||
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)
|
||||
{
|
||||
double offset = 1000.0 / fpsLimit - frameTimeAvg;
|
||||
if(offset > 0)
|
||||
SDL_Delay(Uint32(offset + 0.5));
|
||||
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));
|
||||
}
|
||||
int correctedFrameTime = SDL_GetTicks() - frameStart;
|
||||
correctedFrameTimeAvg = correctedFrameTimeAvg * 0.95 + correctedFrameTime * 0.05;
|
||||
if (frameStart - lastFpsUpdate > 200)
|
||||
auto correctedFrameTime = frameStart - oldFrameStart;
|
||||
drawingTimer += correctedFrameTime;
|
||||
correctedFrameTimeAvg = correctedFrameTimeAvg + (correctedFrameTime - correctedFrameTimeAvg) * 0.05;
|
||||
if (frameStart - lastFpsUpdate > UINT64_C(200'000'000))
|
||||
{
|
||||
engine->SetFps(1000.0 / correctedFrameTimeAvg);
|
||||
engine.SetFps(1e9f / correctedFrameTimeAvg);
|
||||
lastFpsUpdate = frameStart;
|
||||
}
|
||||
if (frameStart - lastTick > 100)
|
||||
if (frameStart - lastTick > UINT64_C(100'000'000))
|
||||
{
|
||||
lastTick = frameStart;
|
||||
Client::Ref().Tick();
|
||||
TickClient();
|
||||
}
|
||||
if (showLargeScreenDialog)
|
||||
{
|
||||
@ -534,438 +408,8 @@ void EngineProcess()
|
||||
LargeScreenDialog();
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
if constexpr (DEBUG)
|
||||
{
|
||||
std::cout << "Breaking out of EngineProcess" << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void BlueScreen(String detailMessage)
|
||||
{
|
||||
ui::Engine * engine = &ui::Engine::Ref();
|
||||
engine->g->fillrect(0, 0, engine->GetWidth(), engine->GetHeight(), 17, 114, 169, 210);
|
||||
|
||||
String errorTitle = "ERROR";
|
||||
String errorDetails = "Details: " + detailMessage;
|
||||
String errorHelp = "An unrecoverable fault has occurred, please report the error by visiting the website below\n"
|
||||
SCHEME SERVER;
|
||||
int currentY = 0, width, height;
|
||||
int errorWidth = 0;
|
||||
Graphics::textsize(errorHelp, errorWidth, height);
|
||||
|
||||
engine->g->drawtext((engine->GetWidth()/2)-(errorWidth/2), ((engine->GetHeight()/2)-100) + currentY, errorTitle.c_str(), 255, 255, 255, 255);
|
||||
Graphics::textsize(errorTitle, width, height);
|
||||
currentY += height + 4;
|
||||
|
||||
engine->g->drawtext((engine->GetWidth()/2)-(errorWidth/2), ((engine->GetHeight()/2)-100) + currentY, errorDetails.c_str(), 255, 255, 255, 255);
|
||||
Graphics::textsize(errorTitle, width, height);
|
||||
currentY += height + 4;
|
||||
|
||||
engine->g->drawtext((engine->GetWidth()/2)-(errorWidth/2), ((engine->GetHeight()/2)-100) + currentY, errorHelp.c_str(), 255, 255, 255, 255);
|
||||
Graphics::textsize(errorTitle, width, height);
|
||||
currentY += height + 4;
|
||||
|
||||
//Death loop
|
||||
SDL_Event event;
|
||||
while(true)
|
||||
{
|
||||
while (SDL_PollEvent(&event))
|
||||
if(event.type == SDL_QUIT)
|
||||
exit(-1);
|
||||
blit(engine->g->vid);
|
||||
}
|
||||
}
|
||||
|
||||
void SigHandler(int signal)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr int SCALE_MAXIMUM = 10;
|
||||
constexpr int SCALE_MARGIN = 30;
|
||||
|
||||
int GuessBestScale()
|
||||
{
|
||||
const int widthNoMargin = desktopWidth - SCALE_MARGIN;
|
||||
const int widthGuess = widthNoMargin / WINDOWW;
|
||||
|
||||
const int heightNoMargin = desktopHeight - SCALE_MARGIN;
|
||||
const int heightGuess = heightNoMargin / WINDOWH;
|
||||
|
||||
int guess = std::min(widthGuess, heightGuess);
|
||||
if(guess < 1 || guess > SCALE_MAXIMUM)
|
||||
guess = 1;
|
||||
|
||||
return guess;
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
#if defined(DEBUG) && defined(_MSC_VER)
|
||||
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
|
||||
#endif
|
||||
currentWidth = WINDOWW;
|
||||
currentHeight = WINDOWH;
|
||||
|
||||
|
||||
// https://bugzilla.libsdl.org/show_bug.cgi?id=3796
|
||||
if (SDL_Init(0) < 0)
|
||||
{
|
||||
fprintf(stderr, "Initializing SDL: %s\n", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
Platform::originalCwd = Platform::GetCwd();
|
||||
|
||||
using Argument = std::optional<ByteString>;
|
||||
std::map<ByteString, Argument> arguments;
|
||||
|
||||
for (auto i = 1; i < argc; ++i)
|
||||
{
|
||||
auto str = ByteString(argv[i]);
|
||||
if (str.BeginsWith("file://"))
|
||||
{
|
||||
arguments.insert({ "open", format::URLDecode(str.substr(7 /* length of the "file://" prefix */)) });
|
||||
}
|
||||
else if (str.BeginsWith("ptsave:"))
|
||||
{
|
||||
arguments.insert({ "ptsave", str });
|
||||
}
|
||||
else if (auto split = str.SplitBy(':'))
|
||||
{
|
||||
arguments.insert({ split.Before(), split.After() });
|
||||
}
|
||||
else if (auto split = str.SplitBy('='))
|
||||
{
|
||||
arguments.insert({ split.Before(), split.After() });
|
||||
}
|
||||
else if (str == "open" || str == "ptsave" || str == "ddir")
|
||||
{
|
||||
if (i + 1 < argc)
|
||||
{
|
||||
arguments.insert({ str, argv[i + 1] });
|
||||
i += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "no value provided for command line parameter " << str << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
arguments.insert({ str, "" }); // so .has_value() is true
|
||||
}
|
||||
}
|
||||
|
||||
auto ddirArg = arguments["ddir"];
|
||||
if (ddirArg.has_value())
|
||||
{
|
||||
#ifdef WIN
|
||||
int failure = _chdir(ddirArg.value().c_str());
|
||||
#else
|
||||
int failure = chdir(ddirArg.value().c_str());
|
||||
#endif
|
||||
if (!failure)
|
||||
Platform::sharedCwd = Platform::GetCwd();
|
||||
else
|
||||
perror("failed to chdir to requested ddir");
|
||||
}
|
||||
else
|
||||
{
|
||||
char *ddir = SDL_GetPrefPath(NULL, APPDATA);
|
||||
#ifdef WIN
|
||||
struct _stat s;
|
||||
if (_stat("powder.pref", &s) != 0)
|
||||
#else
|
||||
struct stat s;
|
||||
if (stat("powder.pref", &s) != 0)
|
||||
#endif
|
||||
{
|
||||
if (ddir)
|
||||
{
|
||||
#ifdef WIN
|
||||
int failure = _chdir(ddir);
|
||||
#else
|
||||
int failure = chdir(ddir);
|
||||
#endif
|
||||
if (failure)
|
||||
{
|
||||
perror("failed to chdir to default ddir");
|
||||
SDL_free(ddir);
|
||||
ddir = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ddir)
|
||||
{
|
||||
Platform::sharedCwd = ddir;
|
||||
SDL_free(ddir);
|
||||
}
|
||||
}
|
||||
|
||||
scale = Client::Ref().GetPrefInteger("Scale", 1);
|
||||
resizable = Client::Ref().GetPrefBool("Resizable", false);
|
||||
fullscreen = Client::Ref().GetPrefBool("Fullscreen", false);
|
||||
altFullscreen = Client::Ref().GetPrefBool("AltFullscreen", false);
|
||||
forceIntegerScaling = Client::Ref().GetPrefBool("ForceIntegerScaling", true);
|
||||
momentumScroll = Client::Ref().GetPrefBool("MomentumScroll", true);
|
||||
showAvatars = Client::Ref().GetPrefBool("ShowAvatars", true);
|
||||
|
||||
auto true_string = [](ByteString str) {
|
||||
str = str.ToLower();
|
||||
return str == "true" ||
|
||||
str == "t" ||
|
||||
str == "on" ||
|
||||
str == "yes" ||
|
||||
str == "y" ||
|
||||
str == ""; // standalone "redirect" or "disable-bluescreen" or similar arguments
|
||||
};
|
||||
auto true_arg = [&true_string](Argument arg) {
|
||||
return arg.has_value() && true_string(arg.value());
|
||||
};
|
||||
|
||||
auto kioskArg = arguments["kiosk"];
|
||||
if (kioskArg.has_value())
|
||||
{
|
||||
fullscreen = true_string(kioskArg.value());
|
||||
Client::Ref().SetPref("Fullscreen", fullscreen);
|
||||
}
|
||||
|
||||
if (true_arg(arguments["redirect"]))
|
||||
{
|
||||
FILE *new_stdout = freopen("stdout.log", "w", stdout);
|
||||
FILE *new_stderr = freopen("stderr.log", "w", stderr);
|
||||
if (!new_stdout || !new_stderr)
|
||||
{
|
||||
exit(42);
|
||||
}
|
||||
}
|
||||
|
||||
auto scaleArg = arguments["scale"];
|
||||
if (scaleArg.has_value())
|
||||
{
|
||||
try
|
||||
{
|
||||
scale = scaleArg.value().ToNumber<int>();
|
||||
Client::Ref().SetPref("Scale", scale);
|
||||
}
|
||||
catch (const std::runtime_error &e)
|
||||
{
|
||||
std::cerr << "failed to set scale: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
auto clientConfig = [](Argument arg, ByteString name, ByteString defaultValue) {
|
||||
ByteString value;
|
||||
if (arg.has_value())
|
||||
{
|
||||
if (value == "")
|
||||
{
|
||||
value = defaultValue;
|
||||
}
|
||||
Client::Ref().SetPref(name, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = Client::Ref().GetPrefByteString(name, defaultValue);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
ByteString proxyString = clientConfig(arguments["proxy"], "Proxy", "");
|
||||
ByteString cafileString = clientConfig(arguments["cafile"], "CAFile", "");
|
||||
ByteString capathString = clientConfig(arguments["capath"], "CAPath", "");
|
||||
|
||||
bool disableNetwork = true_arg(arguments["disable-network"]);
|
||||
|
||||
Client::Ref().Initialise(proxyString, cafileString, capathString, disableNetwork);
|
||||
|
||||
// TODO: maybe bind the maximum allowed scale to screen size somehow
|
||||
if(scale < 1 || scale > SCALE_MAXIMUM)
|
||||
scale = 1;
|
||||
|
||||
SDLOpen();
|
||||
|
||||
if (Client::Ref().IsFirstRun())
|
||||
{
|
||||
scale = GuessBestScale();
|
||||
if (scale > 1)
|
||||
{
|
||||
Client::Ref().SetPref("Scale", scale);
|
||||
SDL_SetWindowSize(sdl_window, WINDOWW * scale, WINDOWH * scale);
|
||||
showLargeScreenDialog = true;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
ui::Engine::Ref().MomentumScroll = momentumScroll;
|
||||
ui::Engine::Ref().ShowAvatars = showAvatars;
|
||||
|
||||
engine = &ui::Engine::Ref();
|
||||
engine->SetMaxSize(desktopWidth, desktopHeight);
|
||||
engine->Begin(WINDOWW, WINDOWH);
|
||||
engine->SetFastQuit(Client::Ref().GetPrefBool("FastQuit", true));
|
||||
|
||||
#if !defined(DEBUG)
|
||||
bool enableBluescreen = !true_arg(arguments["disable-bluescreen"]);
|
||||
if (enableBluescreen)
|
||||
{
|
||||
//Get ready to catch any dodgy errors
|
||||
signal(SIGSEGV, SigHandler);
|
||||
signal(SIGFPE, SigHandler);
|
||||
signal(SIGILL, SigHandler);
|
||||
signal(SIGABRT, SigHandler);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef X86_SSE
|
||||
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
|
||||
#endif
|
||||
#ifdef X86_SSE3
|
||||
_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
|
||||
#endif
|
||||
|
||||
GameController * gameController = NULL;
|
||||
|
||||
auto wrapWithBluescreen = [&]() {
|
||||
gameController = new GameController();
|
||||
engine->ShowWindow(gameController->GetView());
|
||||
|
||||
auto openArg = arguments["open"];
|
||||
if (openArg.has_value())
|
||||
{
|
||||
#ifdef DEBUG
|
||||
std::cout << "Loading " << openArg.value() << std::endl;
|
||||
#endif
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
auto ptsaveArg = arguments["ptsave"];
|
||||
if (ptsaveArg.has_value())
|
||||
{
|
||||
engine->g->fillrect((engine->GetWidth()/2)-101, (engine->GetHeight()/2)-26, 202, 52, 0, 0, 0, 210);
|
||||
engine->g->drawrect((engine->GetWidth()/2)-100, (engine->GetHeight()/2)-25, 200, 50, 255, 255, 255, 180);
|
||||
engine->g->drawtext((engine->GetWidth()/2)-(Graphics::textwidth("Loading save...")/2), (engine->GetHeight()/2)-5, "Loading save...", style::Colour::InformationTitle.Red, style::Colour::InformationTitle.Green, style::Colour::InformationTitle.Blue, 255);
|
||||
|
||||
blit(engine->g->vid);
|
||||
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");
|
||||
#ifdef DEBUG
|
||||
std::cout << "Got Ptsave: id: " << saveIdPart << std::endl;
|
||||
#endif
|
||||
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", ByteString(e.what()).FromUtf8());
|
||||
}
|
||||
}
|
||||
|
||||
EngineProcess();
|
||||
SaveWindowPosition();
|
||||
};
|
||||
|
||||
#if !defined(DEBUG)
|
||||
if (enableBluescreen)
|
||||
{
|
||||
try
|
||||
{
|
||||
wrapWithBluescreen();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
BlueScreen(ByteString(e.what()).FromUtf8());
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
wrapWithBluescreen(); // the else branch of the if in the #if !defined(DEBUG)
|
||||
|
||||
ui::Engine::Ref().CloseWindow();
|
||||
delete gameController;
|
||||
delete ui::Engine::Ref().g;
|
||||
Client::Ref().Shutdown();
|
||||
if (SDL_GetWindowFlags(sdl_window) & SDL_WINDOW_OPENGL)
|
||||
{
|
||||
// * nvidia-460 egl registers callbacks with x11 that end up being called
|
||||
// after egl is unloaded unless we grab it here and release it after
|
||||
// sdl closes the display. this is an nvidia driver weirdness but
|
||||
// technically an sdl bug. glfw has this fixed:
|
||||
// https://github.com/glfw/glfw/commit/9e6c0c747be838d1f3dc38c2924a47a42416c081
|
||||
SDL_GL_LoadLibrary(NULL);
|
||||
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
||||
SDL_GL_UnloadLibrary();
|
||||
}
|
||||
SDL_Quit();
|
||||
return 0;
|
||||
}
|
||||
|
47
src/PowderToySDL.h
Normal file
47
src/PowderToySDL.h
Normal file
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include "common/String.h"
|
||||
#include "graphics/Pixel.h"
|
||||
#include <cstdint>
|
||||
#include <SDL.h>
|
||||
|
||||
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;
|
||||
extern uint64_t lastFpsUpdate;
|
||||
extern bool showLargeScreenDialog;
|
||||
extern int mousex;
|
||||
extern int mousey;
|
||||
extern int mouseButton;
|
||||
extern bool mouseDown;
|
||||
extern bool calculatedInitialMouse;
|
||||
extern bool hasMouseMoved;
|
||||
|
||||
void EngineProcess();
|
||||
void StartTextInput();
|
||||
void StopTextInput();
|
||||
void SetTextInputRect(int x, int y, int w, int h);
|
||||
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(int scale_, bool resizable_, bool fullscreen_, bool altFullscreen_, bool forceIntegerScaling_);
|
||||
bool RecreateWindow();
|
||||
void LoadWindowPosition();
|
||||
void SaveWindowPosition();
|
||||
void LargeScreenDialog();
|
||||
void TickClient();
|
||||
void EventProcess(const SDL_Event &event);
|
@ -14,10 +14,9 @@
|
||||
*/
|
||||
|
||||
#include "Probability.h"
|
||||
|
||||
#include "common/tpt-rand.h"
|
||||
#include <numeric>
|
||||
#include <cstdlib>
|
||||
#include "common/tpt-rand.h"
|
||||
|
||||
namespace Probability
|
||||
{
|
||||
|
@ -13,12 +13,8 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef tptmath_h
|
||||
#define tptmath_h
|
||||
#include "Config.h"
|
||||
|
||||
#pragma once
|
||||
// This file is used for EMP, to simulate many EMP going off at once at the end of the frame
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace Probability
|
||||
@ -41,5 +37,3 @@ namespace Probability
|
||||
unsigned int calc(float randFloat);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,7 +0,0 @@
|
||||
#include "Config.h"
|
||||
#include <SDL.h>
|
||||
#ifdef INCLUDE_SYSWM
|
||||
# if defined(WIN)
|
||||
# include <SDL_syswm.h>
|
||||
# endif // WIN
|
||||
#endif // INCLUDE_SYSWM
|
63
src/SimulationConfig.h
Normal file
63
src/SimulationConfig.h
Normal file
@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <common/Vec2.h>
|
||||
|
||||
constexpr int MENUSIZE = 40;
|
||||
constexpr int BARSIZE = 17;
|
||||
|
||||
constexpr float M_GRAV = 6.67300e-1f;
|
||||
|
||||
//CELL, the size of the pressure, gravity, and wall maps. Larger than 1 to prevent extreme lag
|
||||
constexpr int CELL = 4;
|
||||
constexpr Vec2<int> CELLS = Vec2(153, 96);
|
||||
constexpr Vec2<int> RES = CELLS * CELL;
|
||||
|
||||
constexpr int XCELLS = CELLS.X;
|
||||
constexpr int YCELLS = CELLS.Y;
|
||||
constexpr int NCELL = XCELLS * YCELLS;
|
||||
constexpr int XRES = RES.X;
|
||||
constexpr int YRES = RES.Y;
|
||||
constexpr int NPART = XRES * YRES;
|
||||
|
||||
constexpr int XCNTR = XRES / 2;
|
||||
constexpr int YCNTR = YRES / 2;
|
||||
|
||||
constexpr Vec2<int> WINDOW = RES + Vec2(BARSIZE, MENUSIZE);
|
||||
|
||||
constexpr int WINDOWW = WINDOW.X;
|
||||
constexpr int WINDOWH = WINDOW.Y;
|
||||
|
||||
constexpr int MAXSIGNS = 16;
|
||||
|
||||
constexpr int ISTP = CELL / 2;
|
||||
constexpr float CFDS = 4.0f / CELL;
|
||||
constexpr float SIM_MAXVELOCITY = 1e4f;
|
||||
|
||||
//Air constants
|
||||
constexpr float AIR_TSTEPP = 0.3f;
|
||||
constexpr float AIR_TSTEPV = 0.4f;
|
||||
constexpr float AIR_VADV = 0.3f;
|
||||
constexpr float AIR_VLOSS = 0.999f;
|
||||
constexpr float AIR_PLOSS = 0.9999f;
|
||||
|
||||
constexpr int NGOL = 24;
|
||||
|
||||
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;
|
||||
constexpr int NORMAL_MIN_EST = 3;
|
||||
constexpr int NORMAL_INTERP = 20;
|
||||
constexpr int NORMAL_FRAC = 16;
|
||||
|
||||
constexpr auto REFRACT = UINT32_C(0x80000000);
|
||||
|
||||
/* heavy flint glass, for awesome refraction/dispersion
|
||||
this way you can make roof prisms easily */
|
||||
constexpr float GLASS_IOR = 1.9f;
|
||||
constexpr float GLASS_DISP = 0.07f;
|
||||
|
||||
constexpr float R_TEMP = 22;
|
155
src/Update.cpp
155
src/Update.cpp
@ -1,155 +0,0 @@
|
||||
#include "Update.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#ifndef WIN
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
#if !defined(MACOSX) && !defined(BSD)
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef WIN
|
||||
# ifndef NOMINMAX
|
||||
# define NOMINMAX
|
||||
# endif
|
||||
# include <windows.h>
|
||||
#else
|
||||
# include <unistd.h>
|
||||
# include <sys/stat.h>
|
||||
#endif
|
||||
#ifdef MACOSX
|
||||
# include <mach-o/dyld.h>
|
||||
# include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "common/Platform.h"
|
||||
|
||||
// returns 1 on failure, 0 on success
|
||||
int update_start(char *data, unsigned int len)
|
||||
{
|
||||
ByteString exeName = Platform::ExecutableName(), updName;
|
||||
FILE *f;
|
||||
|
||||
if (!exeName.length())
|
||||
return 1;
|
||||
|
||||
#ifdef WIN
|
||||
updName = exeName;
|
||||
ByteString extension = exeName.substr(exeName.length() - 4);
|
||||
if (extension == ".exe")
|
||||
updName = exeName.substr(0, exeName.length() - 4);
|
||||
updName = updName + "_upd.exe";
|
||||
|
||||
if (!MoveFile(Platform::WinWiden(exeName).c_str(), Platform::WinWiden(updName).c_str()))
|
||||
return 1;
|
||||
|
||||
f = fopen(exeName.c_str(), "wb");
|
||||
if (!f)
|
||||
return 1;
|
||||
if (fwrite(data, 1, len, f) != len)
|
||||
{
|
||||
fclose(f);
|
||||
Platform::RemoveFile(exeName);
|
||||
return 1;
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
if ((uintptr_t)ShellExecute(NULL, L"open", Platform::WinWiden(exeName).c_str(), NULL, NULL, SW_SHOWNORMAL) <= 32)
|
||||
{
|
||||
Platform::RemoveFile(exeName);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
#else
|
||||
updName = exeName + "-update";
|
||||
|
||||
f = fopen(updName.c_str(), "w");
|
||||
if (!f)
|
||||
return 1;
|
||||
if (fwrite(data, 1, len, f) != len)
|
||||
{
|
||||
fclose(f);
|
||||
unlink(updName.c_str());
|
||||
return 1;
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
if (chmod(updName.c_str(), 0755))
|
||||
{
|
||||
unlink(updName.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (rename(updName.c_str(), exeName.c_str()))
|
||||
{
|
||||
unlink(updName.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
execl(exeName.c_str(), "powder-update", NULL);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// returns 1 on failure, 0 on success
|
||||
int update_finish()
|
||||
{
|
||||
#ifdef WIN
|
||||
ByteString exeName = Platform::ExecutableName(), updName;
|
||||
int timeout = 5, err;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("Update: Current EXE name: %s\n", exeName.c_str());
|
||||
#endif
|
||||
|
||||
updName = exeName;
|
||||
ByteString extension = exeName.substr(exeName.length() - 4);
|
||||
if (extension == ".exe")
|
||||
updName = exeName.substr(0, exeName.length() - 4);
|
||||
updName = updName + "_upd.exe";
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("Update: Temp EXE name: %s\n", updName.c_str());
|
||||
#endif
|
||||
|
||||
while (!Platform::RemoveFile(updName))
|
||||
{
|
||||
err = GetLastError();
|
||||
if (err == ERROR_FILE_NOT_FOUND)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("Update: Temp file not deleted\n");
|
||||
#endif
|
||||
// Old versions of powder toy name their update files with _update.exe, delete that upgrade file here
|
||||
updName = exeName;
|
||||
ByteString extension = exeName.substr(exeName.length() - 4);
|
||||
if (extension == ".exe")
|
||||
updName = exeName.substr(0, exeName.length() - 4);
|
||||
updName = updName + "_update.exe";
|
||||
Platform::RemoveFile(updName);
|
||||
return 0;
|
||||
}
|
||||
Sleep(500);
|
||||
timeout--;
|
||||
if (timeout <= 0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("Update: Delete timeout\n");
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void update_cleanup()
|
||||
{
|
||||
#ifdef WIN
|
||||
update_finish();
|
||||
#endif
|
||||
}
|
10
src/Update.h
10
src/Update.h
@ -1,10 +0,0 @@
|
||||
#ifndef UPDATE_H_
|
||||
#define UPDATE_H_
|
||||
#include "Config.h"
|
||||
|
||||
//char *exe_name(void);
|
||||
int update_start(char *data, unsigned int len);
|
||||
int update_finish();
|
||||
void update_cleanup();
|
||||
|
||||
#endif /* UPDATE_H_ */
|
15
src/WindowIcon.cpp
Normal file
15
src/WindowIcon.cpp
Normal file
@ -0,0 +1,15 @@
|
||||
#include "Format.h"
|
||||
#include "graphics/Graphics.h"
|
||||
#include "WindowIcon.h"
|
||||
|
||||
#include "icon_exe.png.h"
|
||||
|
||||
void WindowIcon(SDL_Window *window)
|
||||
{
|
||||
if (auto image = format::PixelsFromPNG(std::vector<char>(icon_exe_png, icon_exe_png + icon_exe_png_size)))
|
||||
{
|
||||
SDL_Surface *icon = SDL_CreateRGBSurfaceFrom(image->data(), image->Size().X, image->Size().Y, 32, image->Size().Y * sizeof(pixel), 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
|
||||
SDL_SetWindowIcon(window, icon);
|
||||
SDL_FreeSurface(icon);
|
||||
}
|
||||
}
|
4
src/WindowIcon.h
Normal file
4
src/WindowIcon.h
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <SDL.h>
|
||||
|
||||
void WindowIcon(SDL_Window *window);
|
9
src/X86KillDenormals.cpp
Normal file
9
src/X86KillDenormals.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
#include "X86KillDenormals.h"
|
||||
#include <xmmintrin.h>
|
||||
#include <pmmintrin.h>
|
||||
|
||||
void X86KillDenormals()
|
||||
{
|
||||
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
|
||||
_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
|
||||
}
|
3
src/X86KillDenormals.h
Normal file
3
src/X86KillDenormals.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void X86KillDenormals();
|
@ -975,7 +975,7 @@ void bson_fatal_msg( int ok , const char *msg ) {
|
||||
}
|
||||
|
||||
bson_errprintf( "error: %s\n" , msg );
|
||||
exit( -5 );
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
|
@ -18,22 +18,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _BSON_H_
|
||||
#define _BSON_H_
|
||||
#include "Config.h"
|
||||
|
||||
#pragma once
|
||||
#include "common/tpt-inline.h"
|
||||
#include <ctime>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <climits>
|
||||
#include "common/tpt-inline.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#define BSON_OK 0
|
||||
#define BSON_ERROR -1
|
||||
constexpr int BSON_OK = 0;
|
||||
constexpr int BSON_ERROR = -1;
|
||||
|
||||
static const char bson_numstrs[1000][4] = {
|
||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||
@ -1210,5 +1206,3 @@ static TPT_INLINE void bson_swap_endian32( void *outp, const void *inp ) {
|
||||
out[2] = in[1];
|
||||
out[3] = in[0];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,7 +1,5 @@
|
||||
#include "bz2wrap.h"
|
||||
|
||||
#include "bzlib.h"
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,69 +1,42 @@
|
||||
#ifndef CLIENT_H
|
||||
#define CLIENT_H
|
||||
#include "Config.h"
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
#pragma once
|
||||
#include "common/String.h"
|
||||
#include "common/Singleton.h"
|
||||
#include <json/json.h>
|
||||
|
||||
#include "common/ExplicitSingleton.h"
|
||||
#include "StartupInfo.h"
|
||||
#include "User.h"
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <json/json.h>
|
||||
|
||||
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 Request;
|
||||
class StartupRequest;
|
||||
}
|
||||
class Client: public Singleton<Client> {
|
||||
class Client: public ExplicitSingleton<Client> {
|
||||
private:
|
||||
String messageOfTheDay;
|
||||
std::vector<std::pair<String, ByteString> > serverNotifications;
|
||||
std::vector<ServerNotification> serverNotifications;
|
||||
|
||||
http::Request *versionCheckRequest;
|
||||
http::Request *alternateVersionCheckRequest;
|
||||
std::unique_ptr<http::StartupRequest> versionCheckRequest;
|
||||
std::unique_ptr<http::StartupRequest> alternateVersionCheckRequest;
|
||||
bool usingAltUpdateServer;
|
||||
bool updateAvailable;
|
||||
UpdateInfo updateInfo;
|
||||
std::optional<UpdateInfo> updateInfo;
|
||||
|
||||
String lastError;
|
||||
bool firstRun;
|
||||
|
||||
std::list<ByteString> stampIDs;
|
||||
unsigned lastStampTime;
|
||||
int lastStampName;
|
||||
std::vector<ByteString> stampIDs;
|
||||
uint64_t lastStampTime = 0;
|
||||
int lastStampName = 0;
|
||||
|
||||
//Auth session
|
||||
User authUser;
|
||||
@ -71,16 +44,15 @@ private:
|
||||
void notifyUpdateAvailable();
|
||||
void notifyAuthUserChanged();
|
||||
void notifyMessageOfTheDay();
|
||||
void notifyNewNotification(std::pair<String, ByteString> notification);
|
||||
|
||||
// internal preferences handling
|
||||
Json::Value preferences;
|
||||
Json::Value GetPref(Json::Value root, ByteString prop, Json::Value defaultValue = Json::nullValue);
|
||||
Json::Value SetPrefHelper(Json::Value root, ByteString prop, Json::Value value);
|
||||
void notifyNewNotification(ServerNotification notification);
|
||||
|
||||
// Save stealing info
|
||||
Json::Value authors;
|
||||
|
||||
std::unique_ptr<Prefs> stamps;
|
||||
void MigrateStampsDef();
|
||||
void WriteStamps();
|
||||
|
||||
public:
|
||||
|
||||
std::vector<ClientListener*> listeners;
|
||||
@ -94,7 +66,7 @@ public:
|
||||
void ClearAuthorInfo() { authors.clear(); }
|
||||
bool IsAuthorsEmpty() { return authors.size() == 0; }
|
||||
|
||||
UpdateInfo GetUpdateInfo();
|
||||
std::optional<UpdateInfo> GetUpdateInfo();
|
||||
|
||||
Client();
|
||||
~Client();
|
||||
@ -102,80 +74,32 @@ public:
|
||||
ByteString FileOpenDialogue();
|
||||
//std::string FileSaveDialogue();
|
||||
|
||||
bool DoInstallation();
|
||||
|
||||
void AddServerNotification(std::pair<String, ByteString> notification);
|
||||
std::vector<std::pair<String, ByteString> > GetServerNotifications();
|
||||
void AddServerNotification(ServerNotification notification);
|
||||
std::vector<ServerNotification> GetServerNotifications();
|
||||
|
||||
void SetMessageOfTheDay(String message);
|
||||
String GetMessageOfTheDay();
|
||||
|
||||
void Initialise(ByteString proxy, ByteString cafile, ByteString capath, bool disableNetwork);
|
||||
void Initialize();
|
||||
bool IsFirstRun();
|
||||
|
||||
void AddListener(ClientListener * listener);
|
||||
void RemoveListener(ClientListener * listener);
|
||||
|
||||
RequestStatus ExecVote(int saveID, int direction);
|
||||
RequestStatus UploadSave(SaveInfo & save);
|
||||
|
||||
SaveFile * GetStamp(ByteString stampID);
|
||||
std::unique_ptr<SaveFile> GetStamp(ByteString stampID);
|
||||
void DeleteStamp(ByteString stampID);
|
||||
ByteString AddStamp(GameSave * saveData);
|
||||
std::vector<ByteString> GetStamps(int start, int count);
|
||||
ByteString AddStamp(std::unique_ptr<GameSave> saveData);
|
||||
void RescanStamps();
|
||||
int GetStampsCount();
|
||||
SaveFile * GetFirstStamp();
|
||||
const std::vector<ByteString> &GetStamps() const;
|
||||
void MoveStampToFront(ByteString stampID);
|
||||
void updateStamps();
|
||||
|
||||
RequestStatus AddComment(int saveID, String comment);
|
||||
std::unique_ptr<SaveFile> LoadSaveFile(ByteString filename);
|
||||
|
||||
std::vector<char> GetSaveData(int saveID, int saveDate);
|
||||
|
||||
LoginStatus Login(ByteString username, ByteString password, User & user);
|
||||
std::vector<SaveInfo*> * SearchSaves(int start, int count, String query, ByteString sort, ByteString category, int & resultCount);
|
||||
std::vector<std::pair<ByteString, int> > * GetTags(int start, int count, String query, int & resultCount);
|
||||
|
||||
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();
|
||||
bool CheckUpdate(http::Request *updateRequest, bool checkSession);
|
||||
void Shutdown();
|
||||
|
||||
// preferences functions
|
||||
void WritePrefs();
|
||||
|
||||
ByteString GetPrefByteString(ByteString prop, ByteString defaultValue);
|
||||
String GetPrefString(ByteString prop, String defaultValue);
|
||||
double GetPrefNumber(ByteString prop, double defaultValue);
|
||||
int GetPrefInteger(ByteString prop, int defaultValue);
|
||||
unsigned int GetPrefUInteger(ByteString prop, unsigned int defaultValue);
|
||||
bool GetPrefBool(ByteString prop, bool defaultValue);
|
||||
std::vector<ByteString> GetPrefByteStringArray(ByteString prop);
|
||||
std::vector<String> GetPrefStringArray(ByteString prop);
|
||||
std::vector<double> GetPrefNumberArray(ByteString prop);
|
||||
std::vector<int> GetPrefIntegerArray(ByteString prop);
|
||||
std::vector<unsigned int> GetPrefUIntegerArray(ByteString prop);
|
||||
std::vector<bool> GetPrefBoolArray(ByteString prop);
|
||||
|
||||
void SetPref(ByteString prop, Json::Value value);
|
||||
void SetPref(ByteString property, std::vector<Json::Value> value);
|
||||
void SetPrefUnicode(ByteString prop, String value);
|
||||
String DoMigration(ByteString fromDir, ByteString toDir);
|
||||
};
|
||||
|
||||
#endif // CLIENT_H
|
||||
bool AddCustomGol(String ruleString, String nameString, unsigned int highColor, unsigned int lowColor);
|
||||
|
@ -1,7 +1,6 @@
|
||||
#ifndef CLIENTLISTENER_H_
|
||||
#define CLIENTLISTENER_H_
|
||||
|
||||
#pragma once
|
||||
#include "common/String.h"
|
||||
#include "client/ServerNotification.h"
|
||||
|
||||
class Client;
|
||||
class ClientListener
|
||||
@ -13,8 +12,6 @@ public:
|
||||
virtual void NotifyUpdateAvailable(Client * sender) {}
|
||||
virtual void NotifyAuthUserChanged(Client * sender) {}
|
||||
virtual void NotifyMessageOfTheDay(Client * sender) {}
|
||||
virtual void NotifyNewNotification(Client * sender, std::pair<String, ByteString> notification) {}
|
||||
virtual void NotifyNewNotification(Client * sender, ServerNotification notification) {}
|
||||
};
|
||||
|
||||
|
||||
#endif /* CLIENTLISTENER_H_ */
|
||||
|
11
src/client/Comment.h
Normal file
11
src/client/Comment.h
Normal file
@ -0,0 +1,11 @@
|
||||
#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,12 +1,13 @@
|
||||
#pragma once
|
||||
#include "Config.h"
|
||||
|
||||
#include <vector>
|
||||
#include "common/Plane.h"
|
||||
#include "common/String.h"
|
||||
#include "common/tpt-rand.h"
|
||||
#include "simulation/Sign.h"
|
||||
#include "simulation/Particle.h"
|
||||
#include "Misc.h"
|
||||
|
||||
#include "SimulationConfig.h"
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <json/json.h>
|
||||
|
||||
struct sign;
|
||||
@ -53,57 +54,38 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template<class Item>
|
||||
struct Plane
|
||||
{
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
std::vector<Item> items;
|
||||
// invariant: items.size() == width * height
|
||||
|
||||
Item *operator [](int y)
|
||||
{
|
||||
return &items[y * width];
|
||||
}
|
||||
|
||||
const Item *operator [](int y) const
|
||||
{
|
||||
return &items[y * width];
|
||||
}
|
||||
|
||||
Plane() = default;
|
||||
Plane(int newWidth, int newHeight, Item defaultVal) : width(newWidth), height(newHeight), items(width * height, 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;
|
||||
|
||||
public:
|
||||
int blockWidth = 0;
|
||||
int blockHeight = 0;
|
||||
Vec2<int> blockSize = { 0, 0 };
|
||||
bool fromNewerVersion = false;
|
||||
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;
|
||||
Plane<unsigned char> blockMap;
|
||||
Plane<float> fanVelX;
|
||||
Plane<float> fanVelY;
|
||||
Plane<float> pressure;
|
||||
Plane<float> velocityX;
|
||||
Plane<float> velocityY;
|
||||
Plane<float> ambientHeat;
|
||||
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;
|
||||
|
||||
//Simulation Options
|
||||
bool waterEEnabled = false;
|
||||
@ -117,6 +99,7 @@ public:
|
||||
int airMode = 0;
|
||||
float ambientAirTemp = R_TEMP + 273.15f;
|
||||
int edgeMode = 0;
|
||||
bool wantAuthors = true;
|
||||
|
||||
//Signs
|
||||
std::vector<sign> signs;
|
||||
@ -131,20 +114,15 @@ public:
|
||||
|
||||
int pmapbits = 8; // default to 8 bits for older saves
|
||||
|
||||
GameSave(int width, int height);
|
||||
GameSave(const std::vector<char> &data);
|
||||
void setSize(int width, int height);
|
||||
GameSave(Vec2<int> newBlockSize);
|
||||
GameSave(const std::vector<char> &data, bool newWantAuthors = true);
|
||||
void setSize(Vec2<int> newBlockSize);
|
||||
// return value is [ fakeFromNewerVersion, gameData ]
|
||||
std::pair<bool, std::vector<char>> Serialise() const;
|
||||
vector2d Translate(vector2d translate);
|
||||
void Transform(matrix2d transform, vector2d translate);
|
||||
void Transform(matrix2d transform, vector2d translate, vector2d translateReal, int newWidth, int newHeight);
|
||||
void Transform(Mat2<int> transform, Vec2<int> nudge);
|
||||
|
||||
void Expand(const std::vector<char> &data);
|
||||
|
||||
static bool TypeInCtype(int type, int ctype);
|
||||
static bool TypeInTmp(int type);
|
||||
static bool TypeInTmp2(int type, int tmp2);
|
||||
static bool PressureInTmp3(int type);
|
||||
|
||||
GameSave& operator << (Particle &v);
|
||||
|
10
src/client/LoginInfo.h
Normal file
10
src/client/LoginInfo.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include "User.h"
|
||||
#include "ServerNotification.h"
|
||||
#include <vector>
|
||||
|
||||
struct LoginInfo
|
||||
{
|
||||
User user;
|
||||
std::vector<ServerNotification> notifications;
|
||||
};
|
@ -1,6 +1,6 @@
|
||||
// based on public-domain code from Colin Plumb (1993)
|
||||
#include <cstring>
|
||||
#include "MD5.h"
|
||||
#include <cstring>
|
||||
|
||||
static unsigned getu32(const unsigned char *addr)
|
||||
{
|
||||
|
@ -1,6 +1,4 @@
|
||||
#ifndef MD5_H
|
||||
#define MD5_H
|
||||
#include "Config.h"
|
||||
#pragma once
|
||||
|
||||
struct md5_context
|
||||
{
|
||||
@ -15,5 +13,3 @@ 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);
|
||||
|
||||
#endif
|
||||
|
@ -1,20 +1,8 @@
|
||||
#include "SaveFile.h"
|
||||
#include "GameSave.h"
|
||||
#include "common/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);
|
||||
}
|
||||
#include "common/platform/Platform.h"
|
||||
|
||||
SaveFile::SaveFile(ByteString filename, bool newLazyLoad):
|
||||
gameSave(NULL),
|
||||
filename(filename),
|
||||
displayName(filename.FromUtf8()),
|
||||
loadingError(""),
|
||||
@ -23,7 +11,7 @@ SaveFile::SaveFile(ByteString filename, bool newLazyLoad):
|
||||
|
||||
}
|
||||
|
||||
GameSave * SaveFile::GetGameSave()
|
||||
const GameSave *SaveFile::LazyGetGameSave() // non-owning
|
||||
{
|
||||
if (!gameSave && !loadingError.size() && lazyLoad)
|
||||
{
|
||||
@ -32,7 +20,7 @@ GameSave * SaveFile::GetGameSave()
|
||||
std::vector<char> data;
|
||||
if (Platform::ReadFile(data, filename))
|
||||
{
|
||||
gameSave = new GameSave(std::move(data));
|
||||
gameSave = std::make_unique<GameSave>(std::move(data));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -44,24 +32,33 @@ GameSave * SaveFile::GetGameSave()
|
||||
loadingError = ByteString(e.what()).FromUtf8();
|
||||
}
|
||||
}
|
||||
return gameSave;
|
||||
return gameSave.get();
|
||||
}
|
||||
|
||||
const GameSave *SaveFile::GetGameSave() const
|
||||
{
|
||||
return gameSave.get();
|
||||
}
|
||||
|
||||
std::unique_ptr<GameSave> SaveFile::TakeGameSave()
|
||||
{
|
||||
return std::move(gameSave);
|
||||
}
|
||||
|
||||
void SaveFile::LazyUnload()
|
||||
{
|
||||
if (lazyLoad && gameSave)
|
||||
if (lazyLoad)
|
||||
{
|
||||
delete gameSave;
|
||||
gameSave = nullptr;
|
||||
gameSave.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void SaveFile::SetGameSave(GameSave * save)
|
||||
void SaveFile::SetGameSave(std::unique_ptr<GameSave> newGameSave)
|
||||
{
|
||||
gameSave = save;
|
||||
gameSave = std::move(newGameSave);
|
||||
}
|
||||
|
||||
ByteString SaveFile::GetName()
|
||||
const ByteString &SaveFile::GetName() const
|
||||
{
|
||||
return filename;
|
||||
}
|
||||
@ -71,7 +68,7 @@ void SaveFile::SetFileName(ByteString fileName)
|
||||
this->filename = fileName;
|
||||
}
|
||||
|
||||
String SaveFile::GetDisplayName()
|
||||
const String &SaveFile::GetDisplayName() const
|
||||
{
|
||||
return displayName;
|
||||
}
|
||||
@ -81,7 +78,7 @@ void SaveFile::SetDisplayName(String displayName)
|
||||
this->displayName = displayName;
|
||||
}
|
||||
|
||||
String SaveFile::GetError()
|
||||
const String &SaveFile::GetError() const
|
||||
{
|
||||
return loadingError;
|
||||
}
|
||||
@ -90,10 +87,3 @@ void SaveFile::SetLoadingError(String error)
|
||||
{
|
||||
loadingError = error;
|
||||
}
|
||||
|
||||
SaveFile::~SaveFile() {
|
||||
if (gameSave)
|
||||
{
|
||||
delete gameSave;
|
||||
}
|
||||
}
|
||||
|
@ -1,33 +1,29 @@
|
||||
#ifndef SAVEFILE_H_
|
||||
#define SAVEFILE_H_
|
||||
|
||||
#pragma once
|
||||
#include "common/String.h"
|
||||
#include <memory>
|
||||
|
||||
class GameSave;
|
||||
|
||||
class SaveFile {
|
||||
public:
|
||||
SaveFile(SaveFile & save);
|
||||
SaveFile(ByteString filename, bool newLazyLoad = false);
|
||||
|
||||
GameSave * GetGameSave();
|
||||
void SetGameSave(GameSave * save);
|
||||
String GetDisplayName();
|
||||
const GameSave *LazyGetGameSave();
|
||||
const GameSave *GetGameSave() const;
|
||||
std::unique_ptr<GameSave> TakeGameSave();
|
||||
void SetGameSave(std::unique_ptr<GameSave> newSameSave);
|
||||
const String &GetDisplayName() const;
|
||||
void SetDisplayName(String displayName);
|
||||
ByteString GetName();
|
||||
const ByteString &GetName() const;
|
||||
void SetFileName(ByteString fileName);
|
||||
String GetError();
|
||||
const String &GetError() const;
|
||||
void SetLoadingError(String error);
|
||||
|
||||
void LazyUnload();
|
||||
|
||||
virtual ~SaveFile();
|
||||
private:
|
||||
GameSave * gameSave;
|
||||
std::unique_ptr<GameSave> gameSave;
|
||||
ByteString filename;
|
||||
String displayName;
|
||||
String loadingError;
|
||||
bool lazyLoad;
|
||||
};
|
||||
|
||||
#endif /* SAVEFILE_H_ */
|
||||
|
@ -1,31 +1,7 @@
|
||||
#include "SaveInfo.h"
|
||||
#include "GameSave.h"
|
||||
|
||||
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):
|
||||
SaveInfo::SaveInfo(int _id, time_t _createdDate, time_t _updatedDate, int _votesUp, int _votesDown, ByteString _userName, String _name):
|
||||
id(_id),
|
||||
createdDate(_createdDate),
|
||||
updatedDate(_updatedDate),
|
||||
@ -39,14 +15,12 @@ SaveInfo::SaveInfo(int _id, int _createdDate, int _updatedDate, int _votesUp, in
|
||||
userName(_userName),
|
||||
name(_name),
|
||||
Description(""),
|
||||
Published(false),
|
||||
tags(),
|
||||
gameSave(NULL)
|
||||
Published(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
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_):
|
||||
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_):
|
||||
id(_id),
|
||||
createdDate(_createdDate),
|
||||
updatedDate(_updatedDate),
|
||||
@ -60,28 +34,18 @@ SaveInfo::SaveInfo(int _id, int _createdDate, int _updatedDate, int _votesUp, in
|
||||
userName(_userName),
|
||||
name(_name),
|
||||
Description(description_),
|
||||
Published(published_),
|
||||
tags(),
|
||||
gameSave(NULL)
|
||||
Published(published_)
|
||||
{
|
||||
std::list<ByteString> tagsSorted = tags_;
|
||||
tagsSorted.sort();
|
||||
tags=tagsSorted;
|
||||
}
|
||||
|
||||
SaveInfo::~SaveInfo()
|
||||
{
|
||||
if(gameSave)
|
||||
{
|
||||
delete gameSave;
|
||||
}
|
||||
}
|
||||
|
||||
void SaveInfo::SetName(String name)
|
||||
{
|
||||
this->name = name;
|
||||
}
|
||||
String SaveInfo::GetName()
|
||||
const String &SaveInfo::GetName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
@ -90,7 +54,7 @@ void SaveInfo::SetDescription(String description)
|
||||
{
|
||||
Description = description;
|
||||
}
|
||||
String SaveInfo::GetDescription()
|
||||
const String &SaveInfo::GetDescription() const
|
||||
{
|
||||
return Description;
|
||||
}
|
||||
@ -99,7 +63,7 @@ void SaveInfo::SetPublished(bool published)
|
||||
{
|
||||
Published = published;
|
||||
}
|
||||
bool SaveInfo::GetPublished()
|
||||
bool SaveInfo::GetPublished() const
|
||||
{
|
||||
return Published;
|
||||
}
|
||||
@ -108,7 +72,7 @@ void SaveInfo::SetVote(int vote)
|
||||
{
|
||||
this->vote = vote;
|
||||
}
|
||||
int SaveInfo::GetVote()
|
||||
int SaveInfo::GetVote() const
|
||||
{
|
||||
return vote;
|
||||
}
|
||||
@ -118,7 +82,7 @@ void SaveInfo::SetUserName(ByteString userName)
|
||||
this->userName = userName;
|
||||
}
|
||||
|
||||
ByteString SaveInfo::GetUserName()
|
||||
const ByteString &SaveInfo::GetUserName() const
|
||||
{
|
||||
return userName;
|
||||
}
|
||||
@ -127,7 +91,7 @@ void SaveInfo::SetID(int id)
|
||||
{
|
||||
this->id = id;
|
||||
}
|
||||
int SaveInfo::GetID()
|
||||
int SaveInfo::GetID() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
@ -136,7 +100,7 @@ void SaveInfo::SetVotesUp(int votesUp)
|
||||
{
|
||||
this->votesUp = votesUp;
|
||||
}
|
||||
int SaveInfo::GetVotesUp()
|
||||
int SaveInfo::GetVotesUp() const
|
||||
{
|
||||
return votesUp;
|
||||
}
|
||||
@ -145,7 +109,7 @@ void SaveInfo::SetVotesDown(int votesDown)
|
||||
{
|
||||
this->votesDown = votesDown;
|
||||
}
|
||||
int SaveInfo::GetVotesDown()
|
||||
int SaveInfo::GetVotesDown() const
|
||||
{
|
||||
return votesDown;
|
||||
}
|
||||
@ -154,7 +118,7 @@ void SaveInfo::SetVersion(int version)
|
||||
{
|
||||
this->Version = version;
|
||||
}
|
||||
int SaveInfo::GetVersion()
|
||||
int SaveInfo::GetVersion() const
|
||||
{
|
||||
return Version;
|
||||
}
|
||||
@ -166,18 +130,33 @@ void SaveInfo::SetTags(std::list<ByteString> tags)
|
||||
this->tags=tagsSorted;
|
||||
}
|
||||
|
||||
std::list<ByteString> SaveInfo::GetTags()
|
||||
std::list<ByteString> SaveInfo::GetTags() const
|
||||
{
|
||||
return tags;
|
||||
}
|
||||
|
||||
GameSave * SaveInfo::GetGameSave()
|
||||
const GameSave *SaveInfo::GetGameSave() const
|
||||
{
|
||||
return gameSave;
|
||||
return gameSave.get();
|
||||
}
|
||||
|
||||
void SaveInfo::SetGameSave(GameSave * saveGame)
|
||||
std::unique_ptr<GameSave> SaveInfo::TakeGameSave()
|
||||
{
|
||||
delete gameSave;
|
||||
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;
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
#ifndef SAVE_H
|
||||
#define SAVE_H
|
||||
#include "Config.h"
|
||||
|
||||
#include <list>
|
||||
#pragma once
|
||||
#include "common/String.h"
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <ctime>
|
||||
|
||||
#ifdef GetUserName
|
||||
# undef GetUserName // dammit windows
|
||||
@ -16,8 +15,8 @@ class SaveInfo
|
||||
private:
|
||||
public:
|
||||
int id;
|
||||
int createdDate;
|
||||
int updatedDate;
|
||||
time_t createdDate;
|
||||
time_t updatedDate;
|
||||
int votesUp, votesDown;
|
||||
int vote;
|
||||
bool Favourite;
|
||||
@ -32,48 +31,45 @@ public:
|
||||
bool Published;
|
||||
|
||||
std::list<ByteString> tags;
|
||||
GameSave * gameSave;
|
||||
std::unique_ptr<GameSave> gameSave;
|
||||
|
||||
SaveInfo(SaveInfo & save);
|
||||
SaveInfo(int _id, time_t _createdDate, time_t _updatedDate, int _votesUp, int _votesDown, ByteString _userName, String _name);
|
||||
|
||||
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();
|
||||
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);
|
||||
|
||||
void SetName(String name);
|
||||
String GetName();
|
||||
const String &GetName() const;
|
||||
|
||||
void SetDescription(String description);
|
||||
String GetDescription();
|
||||
const String &GetDescription() const;
|
||||
|
||||
void SetPublished(bool published);
|
||||
bool GetPublished();
|
||||
bool GetPublished() const;
|
||||
|
||||
void SetUserName(ByteString userName);
|
||||
ByteString GetUserName();
|
||||
const ByteString &GetUserName() const;
|
||||
|
||||
void SetID(int id);
|
||||
int GetID();
|
||||
int GetID() const;
|
||||
|
||||
void SetVote(int vote);
|
||||
int GetVote();
|
||||
int GetVote() const;
|
||||
|
||||
void SetVotesUp(int votesUp);
|
||||
int GetVotesUp();
|
||||
int GetVotesUp() const;
|
||||
|
||||
void SetVotesDown(int votesDown);
|
||||
int GetVotesDown();
|
||||
int GetVotesDown() const;
|
||||
|
||||
void SetVersion(int version);
|
||||
int GetVersion();
|
||||
int GetVersion() const;
|
||||
|
||||
void SetTags(std::list<ByteString> tags);
|
||||
std::list<ByteString> GetTags();
|
||||
std::list<ByteString> GetTags() const;
|
||||
|
||||
GameSave * GetGameSave();
|
||||
void SetGameSave(GameSave * gameSave);
|
||||
const GameSave *GetGameSave() const;
|
||||
std::unique_ptr<GameSave> TakeGameSave();
|
||||
void SetGameSave(std::unique_ptr<GameSave> newGameSave);
|
||||
|
||||
std::unique_ptr<SaveInfo> CloneInfo() const;
|
||||
};
|
||||
|
||||
#endif // SAVE_H
|
||||
|
17
src/client/Search.h
Normal file
17
src/client/Search.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
namespace http
|
||||
{
|
||||
enum Category
|
||||
{
|
||||
categoryNone,
|
||||
categoryMyOwn,
|
||||
categoryFavourites,
|
||||
};
|
||||
|
||||
enum Sort
|
||||
{
|
||||
sortByVotes,
|
||||
sortByDate,
|
||||
};
|
||||
}
|
8
src/client/ServerNotification.h
Normal file
8
src/client/ServerNotification.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include "common/String.h"
|
||||
|
||||
struct ServerNotification
|
||||
{
|
||||
String text;
|
||||
ByteString link;
|
||||
};
|
29
src/client/StartupInfo.h
Normal file
29
src/client/StartupInfo.h
Normal file
@ -0,0 +1,29 @@
|
||||
#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;
|
||||
};
|
@ -13,13 +13,11 @@ int ThumbnailRendererTask::QueueSize()
|
||||
return queueSize;
|
||||
}
|
||||
|
||||
ThumbnailRendererTask::ThumbnailRendererTask(GameSave *save, int width, int height, bool autoRescale, bool decorations, bool fire) :
|
||||
Save(new GameSave(*save)),
|
||||
Width(width),
|
||||
Height(height),
|
||||
Decorations(decorations),
|
||||
Fire(fire),
|
||||
AutoRescale(autoRescale)
|
||||
ThumbnailRendererTask::ThumbnailRendererTask(GameSave const &save, Vec2<int> size, bool decorations, bool fire):
|
||||
save(std::make_unique<GameSave>(save)),
|
||||
size(size),
|
||||
decorations(decorations),
|
||||
fire(fire)
|
||||
{
|
||||
queueSize += 1;
|
||||
}
|
||||
@ -31,33 +29,11 @@ ThumbnailRendererTask::~ThumbnailRendererTask()
|
||||
|
||||
bool ThumbnailRendererTask::doWork()
|
||||
{
|
||||
thumbnail = std::unique_ptr<VideoBuffer>(SaveRenderer::Ref().Render(Save.get(), Decorations, Fire));
|
||||
thumbnail = std::unique_ptr<VideoBuffer>(SaveRenderer::Ref().Render(save.get(), decorations, fire));
|
||||
if (thumbnail)
|
||||
{
|
||||
if (AutoRescale)
|
||||
{
|
||||
int scaleX = (int)std::ceil((float)thumbnail->Width / Width);
|
||||
int scaleY = (int)std::ceil((float)thumbnail->Height / Height);
|
||||
int scale = scaleX > scaleY ? scaleX : scaleY;
|
||||
int newWidth = thumbnail->Width / scale, newHeight = thumbnail->Height / scale;
|
||||
thumbnail->Resize(newWidth, newHeight, true);
|
||||
newWidth = thumbnail->Width;
|
||||
newHeight = thumbnail->Height;
|
||||
if (newWidth > Width || newHeight > Height)
|
||||
{
|
||||
auto croppedWidth = newWidth > Width ? Width : newWidth;
|
||||
auto croppedHeight = newHeight > Height ? Height : newHeight;
|
||||
thumbnail->Crop(croppedWidth, croppedHeight, (newWidth - croppedWidth) / 2, (newHeight - croppedHeight) / 2);
|
||||
newWidth = thumbnail->Width;
|
||||
newHeight = thumbnail->Height;
|
||||
}
|
||||
Width = newWidth;
|
||||
Height = newHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
thumbnail->Resize(Width, Height, true);
|
||||
}
|
||||
thumbnail->ResizeToFit(size, true);
|
||||
size = thumbnail->Size();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
@ -1,6 +1,5 @@
|
||||
#ifndef THUMBNAILRENDERER_H
|
||||
#define THUMBNAILRENDERER_H
|
||||
|
||||
#pragma once
|
||||
#include "common/Vec2.h"
|
||||
#include "tasks/AbandonableTask.h"
|
||||
|
||||
#include <memory>
|
||||
@ -9,17 +8,16 @@ class GameSave;
|
||||
class VideoBuffer;
|
||||
class ThumbnailRendererTask : public AbandonableTask
|
||||
{
|
||||
std::unique_ptr<GameSave> Save;
|
||||
int Width, Height;
|
||||
bool Decorations;
|
||||
bool Fire;
|
||||
bool AutoRescale;
|
||||
std::unique_ptr<GameSave> save;
|
||||
Vec2<int> size;
|
||||
bool decorations;
|
||||
bool fire;
|
||||
std::unique_ptr<VideoBuffer> thumbnail;
|
||||
|
||||
static int queueSize;
|
||||
|
||||
public:
|
||||
ThumbnailRendererTask(GameSave *save, int width, int height, bool autoRescale = false, bool decorations = true, bool fire = true);
|
||||
ThumbnailRendererTask(GameSave const &, Vec2<int> size, bool decorations, bool fire);
|
||||
virtual ~ThumbnailRendererTask();
|
||||
|
||||
virtual bool doWork() override;
|
||||
@ -27,6 +25,3 @@ public:
|
||||
|
||||
static int QueueSize();
|
||||
};
|
||||
|
||||
#endif // THUMBNAILRENDERER_H
|
||||
|
||||
|
32
src/client/User.cpp
Normal file
32
src/client/User.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
#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";
|
||||
}
|
@ -1,6 +1,4 @@
|
||||
#ifndef USER_H_
|
||||
#define USER_H_
|
||||
|
||||
#pragma once
|
||||
#include "common/String.h"
|
||||
|
||||
|
||||
@ -9,8 +7,14 @@ class User
|
||||
public:
|
||||
enum Elevation
|
||||
{
|
||||
ElevationAdmin, ElevationModerator, ElevationNone
|
||||
ElevationNone,
|
||||
ElevationHalfMod,
|
||||
ElevationMod,
|
||||
ElevationAdmin,
|
||||
};
|
||||
static Elevation ElevationFromString(ByteString str);
|
||||
static ByteString ElevationToString(Elevation elevation);
|
||||
|
||||
int UserID;
|
||||
ByteString Username;
|
||||
ByteString SessionID;
|
||||
@ -27,5 +31,3 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif /* USER_H_ */
|
||||
|
@ -1,6 +1,4 @@
|
||||
#ifndef USERINFO_H_
|
||||
#define USERINFO_H_
|
||||
|
||||
#pragma once
|
||||
#include "common/String.h"
|
||||
|
||||
class UserInfo
|
||||
@ -37,6 +35,3 @@ public:
|
||||
{ }
|
||||
UserInfo() {}
|
||||
};
|
||||
|
||||
|
||||
#endif /* USER_H_ */
|
||||
|
@ -1,41 +1,36 @@
|
||||
#include "APIRequest.h"
|
||||
|
||||
#include "client/Client.h"
|
||||
|
||||
namespace http
|
||||
{
|
||||
APIRequest::APIRequest(ByteString url) : Request(url)
|
||||
APIRequest::APIRequest(ByteString url, AuthMode authMode, bool newCheckStatus) : Request(url), checkStatus(newCheckStatus)
|
||||
{
|
||||
auto user = Client::Ref().GetAuthUser();
|
||||
if (authMode == authRequire && !user.UserID)
|
||||
{
|
||||
FailEarly("Not authenticated");
|
||||
return;
|
||||
}
|
||||
if (authMode != authOmit && user.UserID)
|
||||
{
|
||||
User user = Client::Ref().GetAuthUser();
|
||||
AuthHeaders(ByteString::Build(user.UserID), user.SessionID);
|
||||
}
|
||||
|
||||
APIRequest::~APIRequest()
|
||||
{
|
||||
}
|
||||
|
||||
APIRequest::Result APIRequest::Finish()
|
||||
Json::Value APIRequest::Finish()
|
||||
{
|
||||
Result result;
|
||||
auto [ status, data ] = Request::Finish();
|
||||
ParseResponse(data, status, checkStatus ? responseJson : responseData);
|
||||
Json::Value document;
|
||||
try
|
||||
{
|
||||
ByteString data = Request::Finish(&result.status);
|
||||
// Note that at this point it's not safe to use any member of the
|
||||
// APIRequest object as Request::Finish signals RequestManager
|
||||
// to delete it.
|
||||
Client::Ref().ParseServerReturn(data, result.status, true);
|
||||
if (result.status == 200 && data.size())
|
||||
std::istringstream ss(data);
|
||||
ss >> document;
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
std::istringstream dataStream(data);
|
||||
Json::Value objDocument;
|
||||
dataStream >> objDocument;
|
||||
result.document = std::unique_ptr<Json::Value>(new Json::Value(objDocument));
|
||||
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
||||
}
|
||||
return document;
|
||||
}
|
||||
}
|
||||
catch (std::exception & e)
|
||||
{
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,30 +1,23 @@
|
||||
#ifndef APIREQUEST2_H
|
||||
#define APIREQUEST2_H
|
||||
|
||||
#pragma once
|
||||
#include "Request.h"
|
||||
#include "common/String.h"
|
||||
#include <json/json.h>
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
|
||||
namespace http
|
||||
{
|
||||
class APIRequest : public Request
|
||||
{
|
||||
bool checkStatus;
|
||||
|
||||
public:
|
||||
struct Result
|
||||
enum AuthMode
|
||||
{
|
||||
int status;
|
||||
std::unique_ptr<Json::Value> document;
|
||||
authRequire,
|
||||
authUse,
|
||||
authOmit,
|
||||
};
|
||||
APIRequest(ByteString url, AuthMode authMode, bool newCheckStatus);
|
||||
|
||||
APIRequest(ByteString url);
|
||||
virtual ~APIRequest();
|
||||
|
||||
Result Finish();
|
||||
Json::Value Finish();
|
||||
};
|
||||
}
|
||||
|
||||
#endif // APIREQUEST2_H
|
||||
|
||||
|
21
src/client/http/AddCommentRequest.cpp
Normal file
21
src/client/http/AddCommentRequest.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#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();
|
||||
}
|
||||
}
|
13
src/client/http/AddCommentRequest.h
Normal file
13
src/client/http/AddCommentRequest.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "APIRequest.h"
|
||||
|
||||
namespace http
|
||||
{
|
||||
class AddCommentRequest : public APIRequest
|
||||
{
|
||||
public:
|
||||
AddCommentRequest(int saveID, String comment);
|
||||
|
||||
void Finish();
|
||||
};
|
||||
}
|
29
src/client/http/AddTagRequest.cpp
Normal file
29
src/client/http/AddTagRequest.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#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;
|
||||
}
|
||||
}
|
14
src/client/http/AddTagRequest.h
Normal file
14
src/client/http/AddTagRequest.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include "APIRequest.h"
|
||||
#include <list>
|
||||
|
||||
namespace http
|
||||
{
|
||||
class AddTagRequest : public APIRequest
|
||||
{
|
||||
public:
|
||||
AddTagRequest(int saveID, ByteString tag);
|
||||
|
||||
std::list<ByteString> Finish();
|
||||
};
|
||||
}
|
16
src/client/http/DeleteSaveRequest.cpp
Normal file
16
src/client/http/DeleteSaveRequest.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
#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();
|
||||
}
|
||||
}
|
13
src/client/http/DeleteSaveRequest.h
Normal file
13
src/client/http/DeleteSaveRequest.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "APIRequest.h"
|
||||
|
||||
namespace http
|
||||
{
|
||||
class DeleteSaveRequest : public APIRequest
|
||||
{
|
||||
public:
|
||||
DeleteSaveRequest(int saveID);
|
||||
|
||||
void Finish();
|
||||
};
|
||||
}
|
23
src/client/http/ExecVoteRequest.cpp
Normal file
23
src/client/http/ExecVoteRequest.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
#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);
|
||||
}
|
||||
}
|
20
src/client/http/ExecVoteRequest.h
Normal file
20
src/client/http/ExecVoteRequest.h
Normal file
@ -0,0 +1,20 @@
|
||||
#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;
|
||||
}
|
||||
};
|
||||
}
|
28
src/client/http/FavouriteSaveRequest.cpp
Normal file
28
src/client/http/FavouriteSaveRequest.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
#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();
|
||||
}
|
||||
}
|
20
src/client/http/FavouriteSaveRequest.h
Normal file
20
src/client/http/FavouriteSaveRequest.h
Normal file
@ -0,0 +1,20 @@
|
||||
#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;
|
||||
}
|
||||
};
|
||||
}
|
36
src/client/http/GetCommentsRequest.cpp
Normal file
36
src/client/http/GetCommentsRequest.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
#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()),
|
||||
ByteString(comment["UserID"].asString()).ToNumber<int>() == user.UserID,
|
||||
comment["IsBanned"].asBool(),
|
||||
ByteString(comment["Text"].asString()).FromUtf8(),
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
||||
}
|
||||
return comments;
|
||||
}
|
||||
}
|
14
src/client/http/GetCommentsRequest.h
Normal file
14
src/client/http/GetCommentsRequest.h
Normal file
@ -0,0 +1,14 @@
|
||||
#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();
|
||||
};
|
||||
}
|
28
src/client/http/GetSaveDataRequest.cpp
Normal file
28
src/client/http/GetSaveDataRequest.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
#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());
|
||||
}
|
||||
}
|
13
src/client/http/GetSaveDataRequest.h
Normal file
13
src/client/http/GetSaveDataRequest.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "Request.h"
|
||||
|
||||
namespace http
|
||||
{
|
||||
class GetSaveDataRequest : public Request
|
||||
{
|
||||
public:
|
||||
GetSaveDataRequest(int saveID, int saveDate);
|
||||
|
||||
std::vector<char> Finish();
|
||||
};
|
||||
}
|
69
src/client/http/GetSaveRequest.cpp
Normal file
69
src/client/http/GetSaveRequest.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
#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;
|
||||
}
|
||||
}
|
16
src/client/http/GetSaveRequest.h
Normal file
16
src/client/http/GetSaveRequest.h
Normal file
@ -0,0 +1,16 @@
|
||||
#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();
|
||||
};
|
||||
}
|
@ -1,30 +1,22 @@
|
||||
#include "GetUserInfoRequest.h"
|
||||
|
||||
#include "Config.h"
|
||||
#include "client/UserInfo.h"
|
||||
#include "Config.h"
|
||||
|
||||
namespace http
|
||||
{
|
||||
GetUserInfoRequest::GetUserInfoRequest(ByteString username) :
|
||||
APIRequest(SCHEME SERVER "/User.json?Name=" + username)
|
||||
APIRequest(ByteString::Build(SCHEME, SERVER, "/User.json?Name=", username), authOmit, false)
|
||||
{
|
||||
}
|
||||
|
||||
GetUserInfoRequest::~GetUserInfoRequest()
|
||||
UserInfo GetUserInfoRequest::Finish()
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<UserInfo> GetUserInfoRequest::Finish()
|
||||
{
|
||||
std::unique_ptr<UserInfo> user_info;
|
||||
auto result = APIRequest::Finish();
|
||||
// Note that at this point it's not safe to use any member of the
|
||||
// GetUserInfoRequest object as Request::Finish signals RequestManager
|
||||
// to delete it.
|
||||
if (result.document)
|
||||
UserInfo userInfo;
|
||||
try
|
||||
{
|
||||
auto &user = (*result.document)["User"];
|
||||
user_info = std::unique_ptr<UserInfo>(new UserInfo(
|
||||
auto &user = result["User"];
|
||||
userInfo = UserInfo(
|
||||
user["ID"].asInt(),
|
||||
user["Age"].asInt(),
|
||||
user["Username"].asString(),
|
||||
@ -37,9 +29,13 @@ namespace http
|
||||
user["Forum"]["Topics"].asInt(),
|
||||
user["Forum"]["Replies"].asInt(),
|
||||
user["Forum"]["Reputation"].asInt()
|
||||
));
|
||||
);
|
||||
}
|
||||
return user_info;
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
||||
}
|
||||
return userInfo;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,6 @@
|
||||
#ifndef GETUSERINFOREQUEST2_H
|
||||
#define GETUSERINFOREQUEST2_H
|
||||
|
||||
#pragma once
|
||||
#include "APIRequest.h"
|
||||
|
||||
class UserInfo;
|
||||
#include "client/UserInfo.h"
|
||||
|
||||
namespace http
|
||||
{
|
||||
@ -11,11 +8,7 @@ namespace http
|
||||
{
|
||||
public:
|
||||
GetUserInfoRequest(ByteString username);
|
||||
virtual ~GetUserInfoRequest();
|
||||
|
||||
std::unique_ptr<UserInfo> Finish();
|
||||
UserInfo Finish();
|
||||
};
|
||||
}
|
||||
|
||||
#endif // GETUSERINFOREQUEST2_H
|
||||
|
||||
|
@ -1,47 +1,27 @@
|
||||
#include "ImageRequest.h"
|
||||
|
||||
#include "common/Singleton.h"
|
||||
#include "graphics/Graphics.h"
|
||||
#include "Config.h"
|
||||
|
||||
#include "client/Client.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace http
|
||||
{
|
||||
ImageRequest::ImageRequest(ByteString url, int width, int height) :
|
||||
Request(url),
|
||||
Width(width),
|
||||
Height(height)
|
||||
{
|
||||
}
|
||||
|
||||
ImageRequest::~ImageRequest()
|
||||
ImageRequest::ImageRequest(ByteString url, Vec2<int> newRequestedSize) : Request(url), requestedSize(newRequestedSize)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<VideoBuffer> ImageRequest::Finish()
|
||||
{
|
||||
int width = Width;
|
||||
int height = Height;
|
||||
ByteString data = Request::Finish(nullptr);
|
||||
// Note that at this point it's not safe to use any member of the
|
||||
// ImageRequest object as Request::Finish signals RequestManager
|
||||
// to delete it.
|
||||
std::unique_ptr<VideoBuffer> vb;
|
||||
if (data.size())
|
||||
auto [ status, data ] = Request::Finish();
|
||||
ParseResponse(data, status, responseData);
|
||||
auto vb = VideoBuffer::FromPNG(std::vector<char>(data.begin(), data.end()));
|
||||
if (vb)
|
||||
{
|
||||
int imgw, imgh;
|
||||
std::vector<pixel> imageData;
|
||||
if (PngDataToPixels(imageData, imgw, imgh, data.data(), data.size(), true))
|
||||
{
|
||||
vb = std::unique_ptr<VideoBuffer>(new VideoBuffer(imageData.data(), imgw, imgh));
|
||||
vb->Resize(requestedSize, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
vb = std::unique_ptr<VideoBuffer>(new VideoBuffer(32, 32));
|
||||
vb->SetCharacter(14, 14, 'x', 255, 255, 255, 255);
|
||||
}
|
||||
vb->Resize(width, height, true);
|
||||
vb = std::make_unique<VideoBuffer>(Vec2(15, 16));
|
||||
vb->BlendChar(Vec2(2, 4), 0xE06E, 0xFFFFFF_rgb .WithAlpha(0xFF));
|
||||
}
|
||||
return vb;
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
#ifndef IMAGEREQUEST2_H
|
||||
#define IMAGEREQUEST2_H
|
||||
|
||||
#include "Request.h"
|
||||
#pragma once
|
||||
#include "common/String.h"
|
||||
|
||||
#include "common/Vec2.h"
|
||||
#include "Request.h"
|
||||
#include <memory>
|
||||
|
||||
class VideoBuffer;
|
||||
@ -12,15 +10,11 @@ namespace http
|
||||
{
|
||||
class ImageRequest : public Request
|
||||
{
|
||||
int Width, Height;
|
||||
Vec2<int> requestedSize;
|
||||
|
||||
public:
|
||||
ImageRequest(ByteString url, int width, int height);
|
||||
virtual ~ImageRequest();
|
||||
ImageRequest(ByteString url, Vec2<int> newRequestedSize);
|
||||
|
||||
std::unique_ptr<VideoBuffer> Finish();
|
||||
};
|
||||
}
|
||||
|
||||
#endif // IMAGEREQUEST2_H
|
||||
|
||||
|
45
src/client/http/LoginRequest.cpp
Normal file
45
src/client/http/LoginRequest.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
#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;
|
||||
}
|
||||
}
|
14
src/client/http/LoginRequest.h
Normal file
14
src/client/http/LoginRequest.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include "Request.h"
|
||||
#include "client/LoginInfo.h"
|
||||
|
||||
namespace http
|
||||
{
|
||||
class LoginRequest : public Request
|
||||
{
|
||||
public:
|
||||
LoginRequest(ByteString username, ByteString password);
|
||||
|
||||
LoginInfo Finish();
|
||||
};
|
||||
}
|
16
src/client/http/LogoutRequest.cpp
Normal file
16
src/client/http/LogoutRequest.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
#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, false)
|
||||
{
|
||||
}
|
||||
|
||||
void LogoutRequest::Finish()
|
||||
{
|
||||
APIRequest::Finish();
|
||||
}
|
||||
}
|
13
src/client/http/LogoutRequest.h
Normal file
13
src/client/http/LogoutRequest.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "APIRequest.h"
|
||||
|
||||
namespace http
|
||||
{
|
||||
class LogoutRequest : public APIRequest
|
||||
{
|
||||
public:
|
||||
LogoutRequest();
|
||||
|
||||
void Finish();
|
||||
};
|
||||
}
|
11
src/client/http/PostData.h
Normal file
11
src/client/http/PostData.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "common/String.h"
|
||||
#include <map>
|
||||
#include <variant>
|
||||
|
||||
namespace http
|
||||
{
|
||||
using StringData = ByteString;
|
||||
using FormData = std::map<ByteString, ByteString>;
|
||||
using PostData = std::variant<StringData, FormData>;
|
||||
};
|
19
src/client/http/PublishSaveRequest.cpp
Normal file
19
src/client/http/PublishSaveRequest.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
#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();
|
||||
}
|
||||
}
|
13
src/client/http/PublishSaveRequest.h
Normal file
13
src/client/http/PublishSaveRequest.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "APIRequest.h"
|
||||
|
||||
namespace http
|
||||
{
|
||||
class PublishSaveRequest : public APIRequest
|
||||
{
|
||||
public:
|
||||
PublishSaveRequest(int saveID);
|
||||
|
||||
void Finish();
|
||||
};
|
||||
}
|
29
src/client/http/RemoveTagRequest.cpp
Normal file
29
src/client/http/RemoveTagRequest.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#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;
|
||||
}
|
||||
}
|
14
src/client/http/RemoveTagRequest.h
Normal file
14
src/client/http/RemoveTagRequest.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include "APIRequest.h"
|
||||
#include <list>
|
||||
|
||||
namespace http
|
||||
{
|
||||
class RemoveTagRequest : public APIRequest
|
||||
{
|
||||
public:
|
||||
RemoveTagRequest(int saveID, ByteString tag);
|
||||
|
||||
std::list<ByteString> Finish();
|
||||
};
|
||||
}
|
19
src/client/http/ReportSaveRequest.cpp
Normal file
19
src/client/http/ReportSaveRequest.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
#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();
|
||||
}
|
||||
}
|
13
src/client/http/ReportSaveRequest.h
Normal file
13
src/client/http/ReportSaveRequest.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include "APIRequest.h"
|
||||
|
||||
namespace http
|
||||
{
|
||||
class ReportSaveRequest : public APIRequest
|
||||
{
|
||||
public:
|
||||
ReportSaveRequest(int saveID, String message);
|
||||
|
||||
void Finish();
|
||||
};
|
||||
}
|
@ -1,139 +1,65 @@
|
||||
#include "Request.h"
|
||||
|
||||
#include "RequestManager.h"
|
||||
|
||||
#ifndef NOHTTP
|
||||
void SetupCurlEasyCiphers(CURL *easy)
|
||||
{
|
||||
#ifdef SECURE_CIPHERS_ONLY
|
||||
curl_version_info_data *version_info = curl_version_info(CURLVERSION_NOW);
|
||||
ByteString ssl_type = version_info->ssl_version;
|
||||
if (ssl_type.Contains("OpenSSL"))
|
||||
{
|
||||
curl_easy_setopt(easy, CURLOPT_SSL_CIPHER_LIST, "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");
|
||||
#ifdef REQUEST_USE_CURL_TLSV13CL
|
||||
curl_easy_setopt(easy, CURLOPT_TLS13_CIPHERS, "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");
|
||||
#endif
|
||||
}
|
||||
else if (ssl_type.Contains("Schannel"))
|
||||
{
|
||||
// TODO: add more cipher algorithms
|
||||
curl_easy_setopt(easy, CURLOPT_SSL_CIPHER_LIST, "CALG_ECDH_EPHEM");
|
||||
}
|
||||
#endif
|
||||
// TODO: Find out what TLS1.2 is supported on, might need to also allow TLS1.0
|
||||
curl_easy_setopt(easy, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
|
||||
#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 70, 0)
|
||||
curl_easy_setopt(easy, CURLOPT_SSL_OPTIONS, CURLSSLOPT_REVOKE_BEST_EFFORT);
|
||||
#elif defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 44, 0)
|
||||
curl_easy_setopt(easy, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
|
||||
#elif defined(WIN)
|
||||
# error "That's unfortunate."
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#include "requestmanager/RequestManager.h"
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <json/json.h>
|
||||
|
||||
namespace http
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
Request::Request(ByteString uri_):
|
||||
uri(uri_),
|
||||
rm_total(0),
|
||||
rm_done(0),
|
||||
rm_finished(false),
|
||||
rm_canceled(false),
|
||||
rm_started(false),
|
||||
added_to_multi(false),
|
||||
status(0),
|
||||
headers(NULL),
|
||||
#ifdef REQUEST_USE_CURL_MIMEPOST
|
||||
post_fields(NULL)
|
||||
#else
|
||||
post_fields_first(NULL),
|
||||
post_fields_last(NULL)
|
||||
#endif
|
||||
Request::Request(ByteString newUri)
|
||||
{
|
||||
easy = curl_easy_init();
|
||||
if (!RequestManager::Ref().AddRequest(this))
|
||||
{
|
||||
status = 604;
|
||||
rm_finished = true;
|
||||
handle = RequestHandle::Create();
|
||||
handle->uri = newUri;
|
||||
}
|
||||
}
|
||||
#else
|
||||
Request::Request(ByteString uri_) {}
|
||||
#endif
|
||||
|
||||
Request::~Request()
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
curl_easy_cleanup(easy);
|
||||
#ifdef REQUEST_USE_CURL_MIMEPOST
|
||||
curl_mime_free(post_fields);
|
||||
#else
|
||||
curl_formfree(post_fields_first);
|
||||
#endif
|
||||
curl_slist_free_all(headers);
|
||||
#endif
|
||||
bool tryUnregister;
|
||||
{
|
||||
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)
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
verb = newVerb;
|
||||
#endif
|
||||
assert(handle->state == RequestHandle::ready);
|
||||
handle->verb = newVerb;
|
||||
}
|
||||
|
||||
void Request::AddHeader(ByteString header)
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
headers = curl_slist_append(headers, header.c_str());
|
||||
#endif
|
||||
assert(handle->state == RequestHandle::ready);
|
||||
handle->headers.push_back(header);
|
||||
}
|
||||
|
||||
// add post data to a request
|
||||
void Request::AddPostData(std::map<ByteString, ByteString> data)
|
||||
void Request::AddPostData(PostData data)
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
assert(handle->state == RequestHandle::ready);
|
||||
// Even if the map is empty, calling this function signifies you want to do a POST request
|
||||
isPost = true;
|
||||
if (!data.size())
|
||||
{
|
||||
return;
|
||||
handle->isPost = true;
|
||||
handle->postData = data;
|
||||
}
|
||||
|
||||
if (easy)
|
||||
{
|
||||
#ifdef REQUEST_USE_CURL_MIMEPOST
|
||||
if (!post_fields)
|
||||
{
|
||||
post_fields = curl_mime_init(easy);
|
||||
}
|
||||
|
||||
for (auto &field : data)
|
||||
{
|
||||
curl_mimepart *part = curl_mime_addpart(post_fields);
|
||||
curl_mime_data(part, &field.second[0], field.second.size());
|
||||
if (auto split = field.first.SplitBy(':'))
|
||||
{
|
||||
curl_mime_name(part, split.Before().c_str());
|
||||
curl_mime_filename(part, split.After().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
curl_mime_name(part, field.first.c_str());
|
||||
}
|
||||
}
|
||||
#else
|
||||
post_fields_map.insert(data.begin(), data.end());
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// add userID and sessionID headers to the request
|
||||
void Request::AuthHeaders(ByteString ID, ByteString session)
|
||||
{
|
||||
assert(handle->state == RequestHandle::ready);
|
||||
if (ID.size())
|
||||
{
|
||||
if (session.size())
|
||||
@ -148,263 +74,81 @@ namespace http
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NOHTTP
|
||||
size_t Request::HeaderDataHandler(char *ptr, size_t size, size_t count, void *userdata)
|
||||
{
|
||||
Request *req = (Request *)userdata;
|
||||
auto actual_size = size * count;
|
||||
if (actual_size >= 2 && ptr[actual_size - 2] == '\r' && ptr[actual_size - 1] == '\n')
|
||||
{
|
||||
if (actual_size > 2) // don't include header list terminator (but include the status line)
|
||||
{
|
||||
req->response_headers.push_back(ByteString(ptr, ptr + actual_size - 2));
|
||||
}
|
||||
return actual_size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t Request::WriteDataHandler(char *ptr, size_t size, size_t count, void *userdata)
|
||||
{
|
||||
Request *req = (Request *)userdata;
|
||||
auto actual_size = size * count;
|
||||
req->response_body.append(ptr, actual_size);
|
||||
return actual_size;
|
||||
}
|
||||
#endif
|
||||
|
||||
// start the request thread
|
||||
void Request::Start()
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
if (CheckStarted() || CheckDone())
|
||||
{
|
||||
return;
|
||||
assert(handle->state == RequestHandle::ready);
|
||||
handle->state = RequestHandle::running;
|
||||
RequestManager::Ref().RegisterRequest(*this);
|
||||
}
|
||||
|
||||
if (easy)
|
||||
bool Request::CheckDone() const
|
||||
{
|
||||
#ifdef REQUEST_USE_CURL_MIMEPOST
|
||||
if (post_fields)
|
||||
{
|
||||
curl_easy_setopt(easy, CURLOPT_MIMEPOST, post_fields);
|
||||
}
|
||||
#else
|
||||
if (!post_fields_map.empty())
|
||||
{
|
||||
for (auto &field : post_fields_map)
|
||||
{
|
||||
if (auto split = field.first.SplitBy(':'))
|
||||
{
|
||||
curl_formadd(&post_fields_first, &post_fields_last,
|
||||
CURLFORM_COPYNAME, split.Before().c_str(),
|
||||
CURLFORM_BUFFER, split.After().c_str(),
|
||||
CURLFORM_BUFFERPTR, &field.second[0],
|
||||
CURLFORM_BUFFERLENGTH, field.second.size(),
|
||||
CURLFORM_END);
|
||||
}
|
||||
else
|
||||
{
|
||||
curl_formadd(&post_fields_first, &post_fields_last,
|
||||
CURLFORM_COPYNAME, field.first.c_str(),
|
||||
CURLFORM_PTRCONTENTS, &field.second[0],
|
||||
CURLFORM_CONTENTLEN, field.second.size(),
|
||||
CURLFORM_END);
|
||||
}
|
||||
}
|
||||
curl_easy_setopt(easy, CURLOPT_HTTPPOST, post_fields_first);
|
||||
}
|
||||
#endif
|
||||
else if (isPost)
|
||||
{
|
||||
curl_easy_setopt(easy, CURLOPT_POST, 1L);
|
||||
curl_easy_setopt(easy, CURLOPT_POSTFIELDS, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L);
|
||||
}
|
||||
if (verb.size())
|
||||
{
|
||||
curl_easy_setopt(easy, CURLOPT_CUSTOMREQUEST, verb.c_str());
|
||||
std::lock_guard lk(handle->stateMx);
|
||||
assert(handle->state == RequestHandle::running || handle->state == RequestHandle::done);
|
||||
return handle->state == RequestHandle::done;
|
||||
}
|
||||
|
||||
curl_easy_setopt(easy, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 85, 0)
|
||||
# ifdef ENFORCE_HTTPS
|
||||
curl_easy_setopt(easy, CURLOPT_PROTOCOLS_STR, "https");
|
||||
curl_easy_setopt(easy, CURLOPT_REDIR_PROTOCOLS_STR, "https");
|
||||
# else
|
||||
curl_easy_setopt(easy, CURLOPT_PROTOCOLS_STR, "https,http");
|
||||
curl_easy_setopt(easy, CURLOPT_REDIR_PROTOCOLS_STR, "https,http");
|
||||
# endif
|
||||
#else
|
||||
# ifdef ENFORCE_HTTPS
|
||||
curl_easy_setopt(easy, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
|
||||
curl_easy_setopt(easy, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS);
|
||||
# else
|
||||
curl_easy_setopt(easy, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP);
|
||||
curl_easy_setopt(easy, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
SetupCurlEasyCiphers(easy);
|
||||
|
||||
curl_easy_setopt(easy, CURLOPT_MAXREDIRS, 10L);
|
||||
|
||||
curl_easy_setopt(easy, CURLOPT_ERRORBUFFER, error_buffer);
|
||||
error_buffer[0] = 0;
|
||||
|
||||
curl_easy_setopt(easy, CURLOPT_CONNECTTIMEOUT, timeout);
|
||||
curl_easy_setopt(easy, CURLOPT_HTTPHEADER, headers);
|
||||
curl_easy_setopt(easy, CURLOPT_URL, uri.c_str());
|
||||
|
||||
if (proxy.size())
|
||||
std::pair<int, int> Request::CheckProgress() const
|
||||
{
|
||||
curl_easy_setopt(easy, CURLOPT_PROXY, proxy.c_str());
|
||||
}
|
||||
if (cafile.size())
|
||||
{
|
||||
curl_easy_setopt(easy, CURLOPT_CAINFO, cafile.c_str());
|
||||
}
|
||||
if (capath.size())
|
||||
{
|
||||
curl_easy_setopt(easy, CURLOPT_CAPATH, capath.c_str());
|
||||
std::lock_guard lk(handle->stateMx);
|
||||
assert(handle->state == RequestHandle::running || handle->state == RequestHandle::done);
|
||||
return { handle->bytesTotal, handle->bytesDone };
|
||||
}
|
||||
|
||||
curl_easy_setopt(easy, CURLOPT_PRIVATE, (void *)this);
|
||||
curl_easy_setopt(easy, CURLOPT_USERAGENT, user_agent.c_str());
|
||||
|
||||
curl_easy_setopt(easy, CURLOPT_HEADERDATA, (void *)this);
|
||||
curl_easy_setopt(easy, CURLOPT_HEADERFUNCTION, Request::HeaderDataHandler);
|
||||
|
||||
curl_easy_setopt(easy, CURLOPT_WRITEDATA, (void *)this);
|
||||
curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, Request::WriteDataHandler);
|
||||
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::lock_guard<std::mutex> g(rm_mutex);
|
||||
rm_started = true;
|
||||
}
|
||||
RequestManager::Ref().StartRequest(this);
|
||||
#endif
|
||||
std::unique_lock lk(handle->stateMx);
|
||||
assert(handle->state == RequestHandle::running);
|
||||
handle->stateCv.wait(lk, [this]() {
|
||||
return handle->state == RequestHandle::done;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// finish the request (if called before the request is done, this will block)
|
||||
ByteString Request::Finish(int *status_out, std::vector<ByteString> *headers_out)
|
||||
int Request::StatusCode() const
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
if (CheckCanceled())
|
||||
{
|
||||
return ""; // shouldn't happen but just in case
|
||||
std::unique_lock lk(handle->stateMx);
|
||||
assert(handle->state == RequestHandle::done);
|
||||
}
|
||||
return handle->statusCode;
|
||||
}
|
||||
|
||||
ByteString response_out;
|
||||
std::pair<int, ByteString> Request::Finish()
|
||||
{
|
||||
std::unique_lock<std::mutex> l(rm_mutex);
|
||||
done_cv.wait(l, [this]() { return rm_finished; });
|
||||
rm_started = false;
|
||||
rm_canceled = true;
|
||||
if (status_out)
|
||||
{
|
||||
*status_out = status;
|
||||
std::unique_lock lk(handle->stateMx);
|
||||
assert(handle->state == RequestHandle::done);
|
||||
}
|
||||
if (headers_out)
|
||||
handle->state = RequestHandle::dead;
|
||||
if (handle->error)
|
||||
{
|
||||
*headers_out = std::move(response_headers);
|
||||
throw RequestError(*handle->error);
|
||||
}
|
||||
response_out = std::move(response_body);
|
||||
return std::pair{ handle->statusCode, std::move(handle->responseData) };
|
||||
}
|
||||
|
||||
RequestManager::Ref().RemoveRequest(this);
|
||||
return response_out;
|
||||
#else
|
||||
if (status_out)
|
||||
*status_out = 604;
|
||||
return "";
|
||||
#endif
|
||||
void RequestHandle::MarkDone()
|
||||
{
|
||||
{
|
||||
std::lock_guard lk(stateMx);
|
||||
assert(state == RequestHandle::running);
|
||||
state = RequestHandle::done;
|
||||
}
|
||||
stateCv.notify_one();
|
||||
if (error)
|
||||
{
|
||||
std::cerr << *error << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void Request::CheckProgress(int *total, int *done)
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
std::lock_guard<std::mutex> g(rm_mutex);
|
||||
if (total)
|
||||
{
|
||||
*total = rm_total;
|
||||
}
|
||||
if (done)
|
||||
{
|
||||
*done = rm_done;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// returns true if the request has finished
|
||||
bool Request::CheckDone()
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
std::lock_guard<std::mutex> g(rm_mutex);
|
||||
return rm_finished;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
// returns true if the request was canceled
|
||||
bool Request::CheckCanceled()
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
std::lock_guard<std::mutex> g(rm_mutex);
|
||||
return rm_canceled;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// returns true if the request is running
|
||||
bool Request::CheckStarted()
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
std::lock_guard<std::mutex> g(rm_mutex);
|
||||
return rm_started;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
// cancels the request, the request thread will delete the Request* when it finishes (do not use Request in any way after canceling)
|
||||
void Request::Cancel()
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
{
|
||||
std::lock_guard<std::mutex> g(rm_mutex);
|
||||
rm_canceled = true;
|
||||
}
|
||||
RequestManager::Ref().RemoveRequest(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
ByteString Request::Simple(ByteString uri, int *status, std::map<ByteString, ByteString> post_data)
|
||||
{
|
||||
return SimpleAuth(uri, status, "", "", post_data);
|
||||
}
|
||||
|
||||
ByteString Request::SimpleAuth(ByteString uri, int *status, ByteString ID, ByteString session, std::map<ByteString, ByteString> post_data)
|
||||
{
|
||||
Request *request = new Request(uri);
|
||||
if (!post_data.empty())
|
||||
{
|
||||
request->AddPostData(post_data);
|
||||
}
|
||||
request->AuthHeaders(ID, session);
|
||||
request->Start();
|
||||
return request->Finish(status);
|
||||
}
|
||||
|
||||
String StatusText(int ret)
|
||||
const char *StatusText(int ret)
|
||||
{
|
||||
switch (ret)
|
||||
{
|
||||
@ -475,7 +219,6 @@ namespace http
|
||||
case 607: return "Connection Refused";
|
||||
case 608: return "Proxy Server Not Found";
|
||||
case 609: return "SSL: Invalid Certificate Status";
|
||||
case 610: return "Cancelled by Shutdown";
|
||||
case 611: return "Too Many Redirects";
|
||||
case 612: return "SSL: Connect Error";
|
||||
case 613: return "SSL: Crypto Engine Not Found";
|
||||
@ -487,8 +230,69 @@ 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";
|
||||
default: return "Unknown Status Code";
|
||||
}
|
||||
}
|
||||
return "Unknown Status Code";
|
||||
}
|
||||
|
||||
void Request::ParseResponse(const ByteString &result, int status, ResponseType responseType)
|
||||
{
|
||||
// no server response, return "Malformed Response"
|
||||
if (status == 200 && !result.size())
|
||||
{
|
||||
status = 603;
|
||||
}
|
||||
if (status == 302)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (status != 200)
|
||||
{
|
||||
throw RequestError(ByteString::Build("HTTP Error ", status, ": ", http::StatusText(status)));
|
||||
}
|
||||
|
||||
switch (responseType)
|
||||
{
|
||||
case responseOk:
|
||||
if (strncmp(result.c_str(), "OK", 2))
|
||||
{
|
||||
throw RequestError(result);
|
||||
}
|
||||
break;
|
||||
|
||||
case responseJson:
|
||||
{
|
||||
std::istringstream ss(result);
|
||||
Json::Value root;
|
||||
try
|
||||
{
|
||||
ss >> root;
|
||||
// assume everything is fine if an empty [] is returned
|
||||
if (root.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int status = root.get("Status", 1).asInt();
|
||||
if (status != 1)
|
||||
{
|
||||
throw RequestError(ByteString(root.get("Error", "Unspecified Error").asString()));
|
||||
}
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
// sometimes the server returns a 200 with the text "Error: 401"
|
||||
if (!strncmp(result.c_str(), "Error: ", 7))
|
||||
{
|
||||
status = ByteString(result.begin() + 7, result.end()).ToNumber<int>();
|
||||
throw RequestError(ByteString::Build("HTTP Error ", status, ": ", http::StatusText(status)));
|
||||
}
|
||||
throw RequestError("Could not read response: " + ByteString(ex.what()));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case responseData:
|
||||
// no further processing required
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,91 +1,61 @@
|
||||
#ifndef REQUEST_H
|
||||
#define REQUEST_H
|
||||
#include "Config.h"
|
||||
|
||||
#ifndef NOHTTP
|
||||
# include <curl/curl.h>
|
||||
# include "common/tpt-minmax.h" // for MSVC, ensures windows.h doesn't cause compile errors by defining min/max
|
||||
#pragma once
|
||||
#include "common/String.h"
|
||||
#include "PostData.h"
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
# if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 55, 0)
|
||||
# define REQUEST_USE_CURL_OFFSET_T
|
||||
# endif
|
||||
# if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 56, 0)
|
||||
# define REQUEST_USE_CURL_MIMEPOST
|
||||
# endif
|
||||
# if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 61, 0)
|
||||
# define REQUEST_USE_CURL_TLSV13CL
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
#include "common/String.h"
|
||||
#include <optional>
|
||||
|
||||
namespace http
|
||||
{
|
||||
class RequestManager;
|
||||
struct RequestHandle;
|
||||
|
||||
// Thrown by Finish and ParseResponse
|
||||
struct RequestError : public std::runtime_error
|
||||
{
|
||||
using runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
class Request
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
ByteString uri;
|
||||
std::vector<ByteString> response_headers;
|
||||
ByteString response_body;
|
||||
|
||||
CURL *easy;
|
||||
char error_buffer[CURL_ERROR_SIZE];
|
||||
|
||||
volatile curl_off_t rm_total;
|
||||
volatile curl_off_t rm_done;
|
||||
volatile bool rm_finished;
|
||||
volatile bool rm_canceled;
|
||||
volatile bool rm_started;
|
||||
std::mutex rm_mutex;
|
||||
|
||||
bool added_to_multi;
|
||||
int status;
|
||||
|
||||
ByteString verb;
|
||||
struct curl_slist *headers;
|
||||
|
||||
bool isPost = false;
|
||||
#ifdef REQUEST_USE_CURL_MIMEPOST
|
||||
curl_mime *post_fields;
|
||||
#else
|
||||
curl_httppost *post_fields_first, *post_fields_last;
|
||||
std::map<ByteString, ByteString> post_fields_map;
|
||||
#endif
|
||||
|
||||
std::condition_variable done_cv;
|
||||
|
||||
static size_t HeaderDataHandler(char *ptr, size_t size, size_t count, void *userdata);
|
||||
static size_t WriteDataHandler(char *ptr, size_t size, size_t count, void *userdata);
|
||||
#endif
|
||||
std::shared_ptr<RequestHandle> handle;
|
||||
|
||||
public:
|
||||
Request(ByteString uri);
|
||||
virtual ~Request();
|
||||
Request(ByteString newUri);
|
||||
Request(const Request &) = delete;
|
||||
Request &operator =(const Request &) = delete;
|
||||
~Request();
|
||||
|
||||
void FailEarly(ByteString error);
|
||||
|
||||
void Verb(ByteString newVerb);
|
||||
void AddHeader(ByteString header);
|
||||
void AddPostData(std::map<ByteString, ByteString> data);
|
||||
|
||||
void AddPostData(PostData data);
|
||||
void AuthHeaders(ByteString ID, ByteString session);
|
||||
|
||||
void Start();
|
||||
ByteString Finish(int *status, std::vector<ByteString> *headers = nullptr);
|
||||
void Cancel();
|
||||
bool CheckDone() const;
|
||||
|
||||
void CheckProgress(int *total, int *done);
|
||||
bool CheckDone();
|
||||
bool CheckCanceled();
|
||||
bool CheckStarted();
|
||||
std::pair<int, int> CheckProgress() const; // total, done
|
||||
const std::vector<ByteString> &ResponseHeaders() const;
|
||||
void Wait();
|
||||
|
||||
int StatusCode() const; // status
|
||||
std::pair<int, ByteString> Finish(); // status, data
|
||||
|
||||
enum ResponseType
|
||||
{
|
||||
responseOk,
|
||||
responseJson,
|
||||
responseData,
|
||||
};
|
||||
static void ParseResponse(const ByteString &result, int status, ResponseType responseType);
|
||||
|
||||
friend class RequestManager;
|
||||
|
||||
static ByteString Simple(ByteString uri, int *status, std::map<ByteString, ByteString> post_data = std::map<ByteString, ByteString>{});
|
||||
static ByteString SimpleAuth(ByteString uri, int *status, ByteString ID, ByteString session, std::map<ByteString, ByteString> post_data = std::map<ByteString, ByteString>{});
|
||||
};
|
||||
|
||||
String StatusText(int code);
|
||||
const char *StatusText(int code);
|
||||
}
|
||||
|
||||
#endif // REQUEST_H
|
||||
|
@ -1,272 +0,0 @@
|
||||
#ifndef NOHTTP
|
||||
#include "Request.h" // includes curl.h, needs to come first to silence a warning on windows
|
||||
#include "RequestManager.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
const int curl_multi_wait_timeout_ms = 100;
|
||||
const long curl_max_host_connections = 1;
|
||||
const long curl_max_concurrent_streams = 50;
|
||||
|
||||
namespace http
|
||||
{
|
||||
const long timeout = 15;
|
||||
ByteString proxy;
|
||||
ByteString cafile;
|
||||
ByteString capath;
|
||||
ByteString user_agent;
|
||||
|
||||
void RequestManager::Shutdown()
|
||||
{
|
||||
if (rt_shutting_down)
|
||||
return;
|
||||
{
|
||||
std::lock_guard<std::mutex> g(rt_mutex);
|
||||
rt_shutting_down = true;
|
||||
}
|
||||
rt_cv.notify_one();
|
||||
|
||||
if (initialized)
|
||||
{
|
||||
worker_thread.join();
|
||||
|
||||
curl_multi_cleanup(multi);
|
||||
multi = NULL;
|
||||
curl_global_cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
void RequestManager::Initialise(ByteString newProxy, ByteString newCafile, ByteString newCapath)
|
||||
{
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
multi = curl_multi_init();
|
||||
if (multi)
|
||||
{
|
||||
curl_multi_setopt(multi, CURLMOPT_MAX_HOST_CONNECTIONS, curl_max_host_connections);
|
||||
#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 67, 0)
|
||||
curl_multi_setopt(multi, CURLMOPT_MAX_CONCURRENT_STREAMS, curl_max_concurrent_streams);
|
||||
#endif
|
||||
}
|
||||
|
||||
proxy = newProxy;
|
||||
cafile = newCafile;
|
||||
capath = newCapath;
|
||||
|
||||
user_agent =
|
||||
"PowderToy/" MTOS(SAVE_VERSION) "." MTOS(MINOR_VERSION) " ("
|
||||
IDENT_PLATFORM
|
||||
"; " IDENT_BUILD
|
||||
"; M" MTOS(MOD_ID)
|
||||
"; " IDENT
|
||||
") TPTPP/" MTOS(SAVE_VERSION) "." MTOS(MINOR_VERSION) "." MTOS(BUILD_NUM) IDENT_RELTYPE "." MTOS(SNAPSHOT_ID);
|
||||
|
||||
worker_thread = std::thread([this]() { Worker(); });
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void RequestManager::Worker()
|
||||
{
|
||||
bool shutting_down = false;
|
||||
while (!shutting_down)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> l(rt_mutex);
|
||||
if (!requests_added_to_multi)
|
||||
{
|
||||
while (!rt_shutting_down && requests_to_add.empty() && !requests_to_start && !requests_to_remove)
|
||||
{
|
||||
rt_cv.wait(l);
|
||||
}
|
||||
}
|
||||
shutting_down = rt_shutting_down;
|
||||
requests_to_remove = false;
|
||||
requests_to_start = false;
|
||||
for (Request *request : requests_to_add)
|
||||
{
|
||||
request->status = 0;
|
||||
requests.insert(request);
|
||||
}
|
||||
requests_to_add.clear();
|
||||
}
|
||||
|
||||
if (multi && requests_added_to_multi)
|
||||
{
|
||||
int dontcare;
|
||||
struct CURLMsg *msg;
|
||||
|
||||
curl_multi_wait(multi, nullptr, 0, curl_multi_wait_timeout_ms, &dontcare);
|
||||
curl_multi_perform(multi, &dontcare);
|
||||
while ((msg = curl_multi_info_read(multi, &dontcare)))
|
||||
{
|
||||
if (msg->msg == CURLMSG_DONE)
|
||||
{
|
||||
Request *request;
|
||||
curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &request);
|
||||
|
||||
int finish_with = 600;
|
||||
|
||||
switch (msg->data.result)
|
||||
{
|
||||
case CURLE_OK:
|
||||
long code;
|
||||
curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &code);
|
||||
finish_with = (int)code;
|
||||
break;
|
||||
|
||||
case CURLE_UNSUPPORTED_PROTOCOL: finish_with = 601; break;
|
||||
case CURLE_COULDNT_RESOLVE_HOST: finish_with = 602; break;
|
||||
case CURLE_OPERATION_TIMEDOUT: finish_with = 605; break;
|
||||
case CURLE_URL_MALFORMAT: finish_with = 606; break;
|
||||
case CURLE_COULDNT_CONNECT: finish_with = 607; break;
|
||||
case CURLE_COULDNT_RESOLVE_PROXY: finish_with = 608; break;
|
||||
case CURLE_TOO_MANY_REDIRECTS: finish_with = 611; break;
|
||||
|
||||
case CURLE_SSL_CONNECT_ERROR: finish_with = 612; break;
|
||||
case CURLE_SSL_ENGINE_NOTFOUND: finish_with = 613; break;
|
||||
case CURLE_SSL_ENGINE_SETFAILED: finish_with = 614; break;
|
||||
case CURLE_SSL_CERTPROBLEM: finish_with = 615; break;
|
||||
case CURLE_SSL_CIPHER: finish_with = 616; break;
|
||||
case CURLE_SSL_ENGINE_INITFAILED: finish_with = 617; break;
|
||||
case CURLE_SSL_CACERT_BADFILE: finish_with = 618; break;
|
||||
case CURLE_SSL_CRL_BADFILE: finish_with = 619; break;
|
||||
case CURLE_SSL_ISSUER_ERROR: finish_with = 620; break;
|
||||
case CURLE_SSL_PINNEDPUBKEYNOTMATCH: finish_with = 621; break;
|
||||
case CURLE_SSL_INVALIDCERTSTATUS: finish_with = 609; break;
|
||||
|
||||
case CURLE_HTTP2:
|
||||
case CURLE_HTTP2_STREAM:
|
||||
|
||||
case CURLE_FAILED_INIT:
|
||||
case CURLE_NOT_BUILT_IN:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (finish_with >= 600)
|
||||
{
|
||||
std::cerr << request->error_buffer << std::endl;
|
||||
}
|
||||
|
||||
request->status = finish_with;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
std::set<Request *> requests_to_remove;
|
||||
for (Request *request : requests)
|
||||
{
|
||||
bool signal_done = false;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> g(request->rm_mutex);
|
||||
if (shutting_down)
|
||||
{
|
||||
// In the weird case that a http::Request::Simple* call is
|
||||
// waiting on this Request, we should fail the request
|
||||
// instead of cancelling it ourselves.
|
||||
request->status = 610;
|
||||
}
|
||||
if (!request->rm_canceled && request->rm_started && !request->added_to_multi && !request->status)
|
||||
{
|
||||
if (multi && request->easy)
|
||||
{
|
||||
MultiAdd(request);
|
||||
}
|
||||
else
|
||||
{
|
||||
request->status = 604;
|
||||
}
|
||||
}
|
||||
if (!request->rm_canceled && request->rm_started && !request->rm_finished)
|
||||
{
|
||||
if (multi && request->easy)
|
||||
{
|
||||
#ifdef REQUEST_USE_CURL_OFFSET_T
|
||||
curl_easy_getinfo(request->easy, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &request->rm_total);
|
||||
curl_easy_getinfo(request->easy, CURLINFO_SIZE_DOWNLOAD_T, &request->rm_done);
|
||||
#else
|
||||
double total, done;
|
||||
curl_easy_getinfo(request->easy, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &total);
|
||||
curl_easy_getinfo(request->easy, CURLINFO_SIZE_DOWNLOAD, &done);
|
||||
request->rm_total = (curl_off_t)total;
|
||||
request->rm_done = (curl_off_t)done;
|
||||
#endif
|
||||
}
|
||||
if (request->status)
|
||||
{
|
||||
request->rm_finished = true;
|
||||
MultiRemove(request);
|
||||
signal_done = true;
|
||||
}
|
||||
}
|
||||
if (request->rm_canceled)
|
||||
{
|
||||
requests_to_remove.insert(request);
|
||||
}
|
||||
}
|
||||
|
||||
if (signal_done)
|
||||
{
|
||||
request->done_cv.notify_one();
|
||||
}
|
||||
}
|
||||
for (Request *request : requests_to_remove)
|
||||
{
|
||||
requests.erase(request);
|
||||
MultiRemove(request);
|
||||
delete request;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RequestManager::MultiAdd(Request *request)
|
||||
{
|
||||
if (multi && request->easy && !request->added_to_multi)
|
||||
{
|
||||
curl_multi_add_handle(multi, request->easy);
|
||||
request->added_to_multi = true;
|
||||
++requests_added_to_multi;
|
||||
}
|
||||
}
|
||||
|
||||
void RequestManager::MultiRemove(Request *request)
|
||||
{
|
||||
if (request->added_to_multi)
|
||||
{
|
||||
curl_multi_remove_handle(multi, request->easy);
|
||||
request->added_to_multi = false;
|
||||
--requests_added_to_multi;
|
||||
}
|
||||
}
|
||||
|
||||
bool RequestManager::AddRequest(Request *request)
|
||||
{
|
||||
if (!initialized)
|
||||
return false;
|
||||
{
|
||||
std::lock_guard<std::mutex> g(rt_mutex);
|
||||
requests_to_add.insert(request);
|
||||
}
|
||||
rt_cv.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
void RequestManager::StartRequest(Request *request)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> g(rt_mutex);
|
||||
requests_to_start = true;
|
||||
}
|
||||
rt_cv.notify_one();
|
||||
}
|
||||
|
||||
void RequestManager::RemoveRequest(Request *request)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> g(rt_mutex);
|
||||
requests_to_remove = true;
|
||||
}
|
||||
rt_cv.notify_one();
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,61 +0,0 @@
|
||||
#include "Config.h"
|
||||
#ifndef NOHTTP
|
||||
#ifndef REQUESTMANAGER_H
|
||||
#define REQUESTMANAGER_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "common/tpt-minmax.h" // for MSVC, ensures windows.h doesn't cause compile errors by defining min/max
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <set>
|
||||
#include <curl/curl.h>
|
||||
#include "common/Singleton.h"
|
||||
#include "common/String.h"
|
||||
|
||||
namespace http
|
||||
{
|
||||
class Request;
|
||||
class RequestManager : public Singleton<RequestManager>
|
||||
{
|
||||
std::thread worker_thread;
|
||||
std::set<Request *> requests;
|
||||
int requests_added_to_multi = 0;
|
||||
|
||||
std::set<Request *> requests_to_add;
|
||||
bool requests_to_start = false;
|
||||
bool requests_to_remove = false;
|
||||
bool initialized = false;
|
||||
bool rt_shutting_down = false;
|
||||
std::mutex rt_mutex;
|
||||
std::condition_variable rt_cv;
|
||||
|
||||
CURLM *multi = nullptr;
|
||||
|
||||
void Start();
|
||||
void Worker();
|
||||
void MultiAdd(Request *request);
|
||||
void MultiRemove(Request *request);
|
||||
bool AddRequest(Request *request);
|
||||
void StartRequest(Request *request);
|
||||
void RemoveRequest(Request *request);
|
||||
|
||||
public:
|
||||
RequestManager() { }
|
||||
~RequestManager() { }
|
||||
|
||||
void Initialise(ByteString newProxy, ByteString newCafile, ByteString newCapath);
|
||||
void Shutdown();
|
||||
|
||||
friend class Request;
|
||||
};
|
||||
|
||||
extern const long timeout;
|
||||
extern ByteString proxy;
|
||||
extern ByteString cafile;
|
||||
extern ByteString capath;
|
||||
extern ByteString user_agent;
|
||||
}
|
||||
|
||||
#endif // REQUESTMANAGER_H
|
||||
#endif
|
@ -1,56 +0,0 @@
|
||||
#ifndef REQUESTMONITOR_H
|
||||
#define REQUESTMONITOR_H
|
||||
|
||||
#include <type_traits>
|
||||
#include <cassert>
|
||||
|
||||
namespace http
|
||||
{
|
||||
template<class R>
|
||||
class RequestMonitor
|
||||
{
|
||||
R *request;
|
||||
|
||||
protected:
|
||||
RequestMonitor() :
|
||||
request(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~RequestMonitor()
|
||||
{
|
||||
if (request)
|
||||
{
|
||||
request->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void RequestPoll()
|
||||
{
|
||||
if (request && request->CheckDone())
|
||||
{
|
||||
OnResponse(request->Finish());
|
||||
request = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
void RequestSetup(Args&&... args)
|
||||
{
|
||||
assert(!request);
|
||||
request = new R(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void RequestStart()
|
||||
{
|
||||
assert(request);
|
||||
request->Start();
|
||||
}
|
||||
|
||||
virtual void OnResponse(typename std::invoke_result<decltype(&R::Finish), R>::type v) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // REQUESTMONITOR_H
|
||||
|
||||
|
@ -1,37 +1,20 @@
|
||||
#include "SaveUserInfoRequest.h"
|
||||
|
||||
#include "Config.h"
|
||||
#include "client/UserInfo.h"
|
||||
|
||||
namespace http
|
||||
{
|
||||
SaveUserInfoRequest::SaveUserInfoRequest(UserInfo &info) :
|
||||
APIRequest(SCHEME SERVER "/Profile.json")
|
||||
SaveUserInfoRequest::SaveUserInfoRequest(UserInfo info) :
|
||||
APIRequest(ByteString::Build(SCHEME, SERVER, "/Profile.json"), authRequire, true)
|
||||
{
|
||||
AddPostData({
|
||||
AddPostData(FormData{
|
||||
{ "Location", info.location.ToUtf8() },
|
||||
{ "Biography", info.biography.ToUtf8() }
|
||||
{ "Biography", info.biography.ToUtf8() },
|
||||
});
|
||||
}
|
||||
|
||||
SaveUserInfoRequest::~SaveUserInfoRequest()
|
||||
void SaveUserInfoRequest::Finish()
|
||||
{
|
||||
}
|
||||
|
||||
bool SaveUserInfoRequest::Finish()
|
||||
{
|
||||
auto result = APIRequest::Finish();
|
||||
// Note that at this point it's not safe to use any member of the
|
||||
// SaveUserInfoRequest object as Request::Finish signals RequestManager
|
||||
// to delete it.
|
||||
if (result.document)
|
||||
{
|
||||
return (*result.document)["Status"].asInt() == 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
APIRequest::Finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,21 +1,14 @@
|
||||
#ifndef SAVEUSERINFOREQUEST2_H
|
||||
#define SAVEUSERINFOREQUEST2_H
|
||||
|
||||
#pragma once
|
||||
#include "APIRequest.h"
|
||||
|
||||
class UserInfo;
|
||||
#include "client/UserInfo.h"
|
||||
|
||||
namespace http
|
||||
{
|
||||
class SaveUserInfoRequest : public APIRequest
|
||||
{
|
||||
public:
|
||||
SaveUserInfoRequest(UserInfo &info);
|
||||
virtual ~SaveUserInfoRequest();
|
||||
SaveUserInfoRequest(UserInfo info);
|
||||
|
||||
bool Finish();
|
||||
void Finish();
|
||||
};
|
||||
}
|
||||
|
||||
#endif // SAVEUSERINFOREQUEST2_H
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user