mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-08-24 11:20:53 +00:00
Compiles. Unsure of what commit even with but somewhere between 2.2.3 and 2.2.4. Starting from 202ef1d for now to be safe
This commit is contained in:
commit
d05e4c490a
34 changed files with 4051 additions and 1394 deletions
367
.github/workflows/main.yml
vendored
367
.github/workflows/main.yml
vendored
|
@ -1,8 +1,9 @@
|
|||
name: PR Builds x64
|
||||
name: Builds
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
- "**.ini"
|
||||
|
@ -13,112 +14,117 @@ on:
|
|||
- "**.ini"
|
||||
|
||||
jobs:
|
||||
# windows64-netplay:
|
||||
# windows:
|
||||
# strategy:
|
||||
# fail-fast: false
|
||||
# matrix:
|
||||
# build_type: [Netplay, Playback]
|
||||
# include:
|
||||
# - build_type: Netplay
|
||||
# artifact_name: windows64-netplay
|
||||
# build_config: -G "Ninja" -DCMAKE_BUILD_TYPE="Release" -DQt5_DIR:STRING="D:\a\dolphin\dolphin\Externals\Qt\Qt5.15.0\msvc2019_64\lib\cmake\Qt5" -DSLIPPI_PLAYBACK=false
|
||||
# - build_type: Playback
|
||||
# artifact_name: windows64-playback
|
||||
# build_config: -G "Ninja" -DCMAKE_BUILD_TYPE="Release" -DQt5_DIR:STRING="D:\a\dolphin\dolphin\Externals\Qt\Qt5.15.0\msvc2019_64\lib\cmake\Qt5"
|
||||
# env:
|
||||
# DXSDK_DIR: "C:\\Program Files (x86)\\Microsoft DirectX SDK (June 2010)\\"
|
||||
# name: "Windows Netplay"
|
||||
# name: "Windows ${{ matrix.build_type }}"
|
||||
# runs-on: windows-2019
|
||||
# steps:
|
||||
# - name: "Remove Redistributable"
|
||||
# shell: cmd
|
||||
# run: |
|
||||
# MsiExec.exe /passive /X{F0C3E5D1-1ADE-321E-8167-68EF0DE699A5}
|
||||
# - name: "Setup MSBuild"
|
||||
# uses: microsoft/setup-msbuild@v1
|
||||
# - name: "Install DirectX SDK"
|
||||
# shell: powershell
|
||||
# run: |
|
||||
# choco install directx-sdk
|
||||
# - name: "Checkout"
|
||||
# uses: actions/checkout@v2.3.1
|
||||
# uses: actions/checkout@v2
|
||||
# with:
|
||||
# submodules: recursive
|
||||
# # - name: "Remove Redistributable"
|
||||
# # shell: cmd
|
||||
# # run: |
|
||||
# # MsiExec.exe /passive /X{F0C3E5D1-1ADE-321E-8167-68EF0DE699A5}
|
||||
# # MsiExec.exe /passive /X{1D8E6291-B0D5-35EC-8441-6616F567A0F7}
|
||||
# # mkdir .\Tools\DX
|
||||
# # - name: "Setup MSBuild"
|
||||
# # uses: microsoft/setup-msbuild@v1
|
||||
# # - name: Cache DXSDK_Jun10.exe
|
||||
# # uses: actions/cache@v2
|
||||
# # with:
|
||||
# # path: ./Tools/DX/
|
||||
# # key: ${{ runner.os }}
|
||||
# # - name: "Download DirectX SDK"
|
||||
# # working-directory: ${{ github.workspace }}
|
||||
# # shell: powershell
|
||||
# # run: |
|
||||
# # if (!(Test-Path ".\Tools\DX\DXSDK_Jun10.exe" -PathType Leaf)) { Invoke-WebRequest -Uri https://github.com/project-slippi/Ishiiruka/releases/download/v2.2.5/DXSDK_Jun10.exe -UseBasicParsing -OutFile ".\Tools\DX\DXSDK_Jun10.exe" }
|
||||
# # - name: "Install DirectX SDK"
|
||||
# # working-directory: ${{ github.workspace }}
|
||||
# # shell: cmd
|
||||
# # run: |
|
||||
# # .\Tools\DX\DXSDK_Jun10.exe /U /F
|
||||
# - name: 'Fetch Git Tags'
|
||||
# if: success()
|
||||
# shell: bash
|
||||
# if: success()
|
||||
# run: |
|
||||
# git fetch --prune --unshallow
|
||||
# echo ::set-env name=GIT_BRANCH::$(git rev-parse --abbrev-ref HEAD)
|
||||
# echo ::set-env name=GIT_HASH::$(git rev-parse --short HEAD)
|
||||
# echo ::set-env name=GIT_TAG::$(git describe --tags --abbrev=0)
|
||||
# echo ::set-env name=CURR_DATE::$(date +%Y-%m-%d)
|
||||
# - name: "Build Netplay Dolphin"
|
||||
# echo "GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)" >> $GITHUB_ENV
|
||||
# echo "GIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||
# echo "GIT_TAG=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV
|
||||
# echo "CURR_DATE=$(date +%Y-%m-%d)" >> $GITHUB_ENV
|
||||
# - uses: seanmiddleditch/gha-setup-ninja@master
|
||||
# - uses: egor-tensin/vs-shell@v2
|
||||
# - name: "Build ${{ matrix.build_type }} Dolphin"
|
||||
# shell: cmd
|
||||
# run: |
|
||||
# msbuild /p:Configuration=Release /p:Platform=x64 ${{ github.workspace }}\Source\Dolphin.sln
|
||||
# - name: "Package Netplay"
|
||||
# working-directory: ${{ github.workspace }}
|
||||
# run: |
|
||||
# $env:FILE_NAME="${{ env.CURR_DATE }}-${{ env.OBS_GIT_HASH }}-${{ env.OBS_GIT_TAG }}-win64-netplay.zip"
|
||||
# mkdir artifact
|
||||
# mkdir build
|
||||
# cd build
|
||||
# dir ..\Externals\Qt\Qt5.15.0\msvc2019_64\lib\cmake\Qt5
|
||||
# cmake ${{ matrix.build_config }} ..
|
||||
# ninja
|
||||
# - name: "Package ${{ matrix.build_type }}"
|
||||
# working-directory: ${{ github.workspace }}
|
||||
# run: |
|
||||
# Xcopy /Y /E /I .\Data\Sys .\Binary\x64\Sys
|
||||
# cd .\Binary\x64\
|
||||
# fsutil file createnew portable.txt 0
|
||||
# fsutil file createnew FIX-VCRUNTIME140-ERROR.txt 0
|
||||
# echo "Download and install this: https://aka.ms/vs/16/release/vc_redist.x64.exe" > .\FIX-VCRUNTIME140-ERROR.txt
|
||||
# 7z a ${env:FILE_NAME} .\*
|
||||
# move ${env:FILE_NAME} ..\..\artifact\
|
||||
# - name: "Publish"
|
||||
# if: success()
|
||||
# uses: actions/upload-artifact@v2-preview
|
||||
# with:
|
||||
# name: "windows64-netplay"
|
||||
# path: "./artifact/"
|
||||
# windows64-playback:
|
||||
# env:
|
||||
# DXSDK_DIR: "C:\\Program Files (x86)\\Microsoft DirectX SDK (June 2010)\\"
|
||||
# name: "Windows Playback"
|
||||
# runs-on: windows-2019
|
||||
# steps:
|
||||
# - name: "Remove Redistributable"
|
||||
# shell: cmd
|
||||
# - name: "Add Playback codes"
|
||||
# working-directory: ${{ github.workspace }}
|
||||
# if: matrix.build_type == 'Playback'
|
||||
# run: |
|
||||
# MsiExec.exe /passive /X{F0C3E5D1-1ADE-321E-8167-68EF0DE699A5}
|
||||
# - name: "Setup MSBuild"
|
||||
# uses: microsoft/setup-msbuild@v1
|
||||
# - name: "Install DirectX SDK"
|
||||
# shell: powershell
|
||||
# run: |
|
||||
# choco install directx-sdk
|
||||
# - name: "Checkout"
|
||||
# uses: actions/checkout@v2.3.1
|
||||
# - name: 'Fetch Git Tags'
|
||||
# if: success()
|
||||
# shell: bash
|
||||
# run: |
|
||||
# git fetch --prune --unshallow
|
||||
# echo ::set-env name=GIT_BRANCH::$(git rev-parse --abbrev-ref HEAD)
|
||||
# echo ::set-env name=GIT_HASH::$(git rev-parse --short HEAD)
|
||||
# echo ::set-env name=GIT_TAG::$(git describe --tags --abbrev=0)
|
||||
# echo ::set-env name=CURR_DATE::$(date +%Y-%m-%d)
|
||||
# - name: "Build Playback Dolphin"
|
||||
# shell: cmd
|
||||
# run: |
|
||||
# msbuild /p:Configuration=ReleasePlayback /p:Platform=x64 ${{ github.workspace }}\Source\Dolphin.sln
|
||||
# - name: "Package Playback"
|
||||
# if: success()
|
||||
# Xcopy /Y /E /I .\Data\Sys .\Binary\x64\Sys
|
||||
# Xcopy /Y /E /I .\Data\PlaybackGeckoCodes\* .\Binary\x64\Sys\GameSettings\
|
||||
# - name: Package Artifact
|
||||
# working-directory: ${{ github.workspace }}
|
||||
# run: |
|
||||
# $env:FILE_NAME="${{ env.CURR_DATE }}-${{ env.OBS_GIT_HASH }}-${{ env.OBS_GIT_TAG }}-win64-playback.zip"
|
||||
# $FILE_NAME="${{ env.CURR_DATE }}-${{ env.GIT_HASH }}-${{ env.GIT_TAG }}-${{ matrix.artifact_name }}.zip"
|
||||
# mkdir artifact
|
||||
# git clone https://github.com/project-slippi/slippi-desktop-app
|
||||
# Xcopy /Y /E /I .\Data\Sys .\Binary\x64\Sys
|
||||
# Xcopy /Y /E /I .\slippi-desktop-app\app\dolphin-dev\overwrite\Sys .\Binary\x64\Sys
|
||||
# Xcopy /Y /E /I .\slippi-desktop-app\app\dolphin-dev\overwrite\User .\Binary\x64\User
|
||||
# cd .\Binary\x64\
|
||||
# fsutil file createnew portable.txt 0
|
||||
# 7z a ${env:FILE_NAME} .\*
|
||||
# move ${env:FILE_NAME} ..\..\artifact\
|
||||
# 7z a $FILE_NAME .\*
|
||||
# move $FILE_NAME ..\..\artifact\
|
||||
# - name: "Publish"
|
||||
# if: success()
|
||||
# uses: actions/upload-artifact@v2-preview
|
||||
# with:
|
||||
# name: "windows64-playback"
|
||||
# name: ${{ matrix.artifact_name }}
|
||||
# path: "./artifact/"
|
||||
ubuntu64-netplay:
|
||||
name: "Ubuntu Netplay"
|
||||
linux:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Netplay, Playback]
|
||||
include:
|
||||
- build_type: Netplay
|
||||
artifact_name: linux-netplay
|
||||
build_config: netplay
|
||||
- build_type: Playback
|
||||
artifact_name: linux-playback
|
||||
build_config: playback
|
||||
name: "Linux ${{ matrix.build_type }}"
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: 'Fetch Git Tags'
|
||||
if: success()
|
||||
run: |
|
||||
|
@ -155,6 +161,7 @@ jobs:
|
|||
libsoundtouch-dev \
|
||||
libswscale-dev \
|
||||
libusb-1.0-0-dev \
|
||||
libwebkit2gtk-4.0-dev \
|
||||
libxext-dev \
|
||||
libxrandr-dev \
|
||||
portaudio19-dev \
|
||||
|
@ -168,119 +175,50 @@ jobs:
|
|||
qtbase5-private-dev \
|
||||
libxxf86vm-dev \
|
||||
x11proto-xinerama-dev
|
||||
- name: "Build Netplay Dolphin"
|
||||
- name: "Build ${{ matrix.build_type }} Dolphin"
|
||||
if: success()
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
chmod +x ./build-linux.sh
|
||||
./build-linux.sh
|
||||
- name: "Build Netplay AppImage"
|
||||
chmod +x ./build-linux.sh && ./build-linux.sh ${{ matrix.build_config }}
|
||||
- name: "Build ${{ matrix.build_type }} AppImage"
|
||||
if: success()
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
chmod +x ./build-appimage.sh
|
||||
./build-appimage.sh
|
||||
- name: "Package Netplay"
|
||||
chmod +x ./build-appimage.sh && ./build-appimage.sh ${{ matrix.build_config }}
|
||||
- name: "Package"
|
||||
if: success()
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
mkdir artifact
|
||||
FILE_NAME=${{ env.CURR_DATE }}-${{ env.GIT_HASH }}-${{ env.GIT_TAG }}-linux-appimage-netplay.zip
|
||||
FILE_NAME=${{ env.CURR_DATE }}-${{ env.GIT_HASH }}-${{ env.GIT_TAG }}-${{ matrix.artifact_name }}.zip
|
||||
chmod +x ./*.AppImage
|
||||
zip -r "${FILE_NAME}" ./*.AppImage*
|
||||
mv "${FILE_NAME}" ./artifact/
|
||||
- name: "Publish"
|
||||
if: success()
|
||||
uses: actions/upload-artifact@v2-preview
|
||||
with:
|
||||
name: "linux-netplay"
|
||||
name: ${{ matrix.artifact_name }}
|
||||
path: "./artifact/"
|
||||
ubuntu64-playback:
|
||||
name: "Ubuntu Playback"
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v2
|
||||
- name: 'Fetch Git Tags'
|
||||
if: success()
|
||||
run: |
|
||||
git fetch --prune --unshallow
|
||||
echo "GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)" >> $GITHUB_ENV
|
||||
echo "GIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||
echo "GIT_TAG=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV
|
||||
echo "CURR_DATE=$(date +%Y-%m-%d)" >> $GITHUB_ENV
|
||||
- name: "Install prerequisites"
|
||||
if: success()
|
||||
shell: bash
|
||||
run: |
|
||||
sudo dpkg --add-architecture amd64
|
||||
sudo apt update
|
||||
sudo apt install \
|
||||
cmake \
|
||||
pkg-config \
|
||||
git \
|
||||
wget \
|
||||
libao-dev \
|
||||
libasound2-dev \
|
||||
libavcodec-dev \
|
||||
libavformat-dev \
|
||||
libbluetooth-dev \
|
||||
libenet-dev \
|
||||
libgtk2.0-dev \
|
||||
liblzo2-dev \
|
||||
libminiupnpc-dev \
|
||||
libopenal-dev \
|
||||
libpulse-dev \
|
||||
libreadline-dev \
|
||||
libsfml-dev \
|
||||
libsoil-dev \
|
||||
libsoundtouch-dev \
|
||||
libswscale-dev \
|
||||
libusb-1.0-0-dev \
|
||||
libxext-dev \
|
||||
libxrandr-dev \
|
||||
portaudio19-dev \
|
||||
zlib1g-dev \
|
||||
libudev-dev \
|
||||
libevdev-dev \
|
||||
libmbedtls-dev \
|
||||
libcurl4-openssl-dev \
|
||||
libegl1-mesa-dev \
|
||||
libpng-dev \
|
||||
qtbase5-private-dev \
|
||||
libxxf86vm-dev \
|
||||
x11proto-xinerama-dev
|
||||
- name: "Build Playback Dolphin"
|
||||
if: success()
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
chmod +x ./build-linux.sh
|
||||
./build-linux.sh playback
|
||||
- name: "Build Playback AppImage"
|
||||
if: success()
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
chmod +x ./build-appimage.sh
|
||||
./build-appimage.sh playback
|
||||
- name: "Package Playback"
|
||||
if: success()
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
mkdir artifact
|
||||
FILE_NAME=${{ env.CURR_DATE }}-${{ env.GIT_HASH }}-${{ env.GIT_TAG }}-linux-appimage-playback.zip
|
||||
zip -r "${FILE_NAME}" ./*.AppImage
|
||||
mv "${FILE_NAME}" ./artifact/
|
||||
- name: "Publish"
|
||||
if: success()
|
||||
uses: actions/upload-artifact@v2-preview
|
||||
with:
|
||||
name: "linux-playback"
|
||||
path: "./artifact/"
|
||||
macOS64-netplay:
|
||||
name: "macOS Netplay"
|
||||
macOS:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Netplay, Playback]
|
||||
include:
|
||||
- build_type: Netplay
|
||||
artifact_name: macOS-netplay
|
||||
build_config: netplay
|
||||
- build_type: Playback
|
||||
artifact_name: macOS-playback
|
||||
build_config: playback
|
||||
name: "macOS ${{ matrix.build_type }}"
|
||||
runs-on: macos-10.15
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: 'Fetch Git Tags'
|
||||
if: success()
|
||||
run: |
|
||||
|
@ -302,7 +240,8 @@ jobs:
|
|||
if: success()
|
||||
shell: bash
|
||||
run: |
|
||||
brew update
|
||||
rm '/usr/local/bin/2to3' || true
|
||||
echo "HOMEBREW_NO_AUTO_UPDATE=1" >> $GITHUB_ENV
|
||||
brew upgrade cmake
|
||||
brew install \
|
||||
ffmpeg \
|
||||
|
@ -312,100 +251,26 @@ jobs:
|
|||
libao \
|
||||
sound-touch \
|
||||
hidapi \
|
||||
qt
|
||||
- name: "Build Netplay"
|
||||
qt@5
|
||||
- name: "Build ${{ matrix.build_type }} Dolphin"
|
||||
if: success()
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
export LIBRARY_PATH=/usr/lib/
|
||||
cmake -DSLIPPI_PLAYBACK=false -DQt5_DIR=$(brew --prefix qt)/lib/cmake/Qt5 -DENABLE_NOGUI=false ..
|
||||
make -j7
|
||||
- name: "Package Netplay"
|
||||
chmod +x ./build-mac.sh && ./build-mac.sh ${{ matrix.build_config }}
|
||||
- name: Package ${{ matrix.build_type }}
|
||||
if: success()
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
FILE_NAME=${{ env.CURR_DATE }}-${{ env.GIT_HASH }}-${{ env.GIT_TAG }}-macOS-netplay.tar.gz
|
||||
echo "::set-env name=FILE_NAME::${FILE_NAME}"
|
||||
cp -Rf Data/Sys build/Binaries/Dolphin.app/Contents/Resources/
|
||||
FILE_NAME=${{ env.CURR_DATE }}-${{ env.GIT_HASH }}-${{ env.GIT_TAG }}-${{ matrix.artifact_name }}.tar.gz
|
||||
mkdir artifact
|
||||
cd ./build/Binaries/
|
||||
zip -r "${FILE_NAME}" Dolphin.app
|
||||
zip -r "${FILE_NAME}" "Slippi_Dolphin.app"
|
||||
mv "${FILE_NAME}" ../../artifact/
|
||||
- name: "Publish"
|
||||
if: success()
|
||||
uses: actions/upload-artifact@v2-preview
|
||||
with:
|
||||
name: "macOS-netplay"
|
||||
path: "./artifact/"
|
||||
macOS64-playback:
|
||||
name: "macOS Playback"
|
||||
runs-on: macos-10.15
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v2
|
||||
- name: 'Fetch Git Tags'
|
||||
if: success()
|
||||
run: |
|
||||
git fetch --prune --unshallow
|
||||
echo "GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)" >> $GITHUB_ENV
|
||||
echo "GIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||
echo "GIT_TAG=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV
|
||||
echo "CURR_DATE=$(date +%Y-%m-%d)" >> $GITHUB_ENV
|
||||
- name: "Install 10.14 SDK"
|
||||
if: success()
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
wget https://github.com/phracker/MacOSX-SDKs/releases/download/10.15/MacOSX10.14.sdk.tar.xz
|
||||
tar -xf MacOSX10.14.sdk.tar.xz
|
||||
rm MacOSX10.14.sdk.tar.xz
|
||||
sudo mv MacOSX10.14.sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/
|
||||
- name: "Download and Install prerequisites"
|
||||
if: success()
|
||||
shell: bash
|
||||
run: |
|
||||
brew update
|
||||
brew upgrade cmake
|
||||
brew install \
|
||||
ffmpeg \
|
||||
libpng \
|
||||
libav \
|
||||
pkgconfig \
|
||||
libao \
|
||||
sound-touch \
|
||||
hidapi \
|
||||
qt
|
||||
- name: "Build Playback"
|
||||
if: success()
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
mkdir build
|
||||
export LD_LIBRARY_PATH=/usr/lib/
|
||||
cd build
|
||||
cmake -DQt5_DIR=$(brew --prefix qt)/lib/cmake/Qt5 -DENABLE_NOGUI=false ..
|
||||
make -j7
|
||||
- name: "Package Playback"
|
||||
if: success()
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
mkdir artifact
|
||||
git clone https://github.com/project-slippi/slippi-desktop-app
|
||||
cd ./build/Binaries/
|
||||
cp -Rf ../../Data/Sys ./Dolphin.app/Contents/Resources
|
||||
cp -Rf ../../slippi-desktop-app/app/dolphin-dev/overwrite/Sys ./Dolphin.app/Contents/Resources
|
||||
cp -Rf ../../slippi-desktop-app/app/dolphin-dev/overwrite/User ./Dolphin.app/Contents/Resources
|
||||
FILE_NAME=${{ env.CURR_DATE }}-${{ env.GIT_HASH }}-${{ env.GIT_TAG }}-macOS-playback.zip
|
||||
zip -r "${FILE_NAME}" Dolphin.app
|
||||
mv "${FILE_NAME}" ../../artifact/
|
||||
- name: "Publish"
|
||||
if: success()
|
||||
uses: actions/upload-artifact@v2-preview
|
||||
with:
|
||||
name: "macOS-playback"
|
||||
name: ${{ matrix.artifact_name }}
|
||||
path: "./artifact/"
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -11,6 +11,8 @@ Source/Core/Common/scmrev.h
|
|||
/obj/
|
||||
# Ignore files output by Android cmake build
|
||||
/Source/Android/app/.cxx/
|
||||
# Ignore build output
|
||||
/Source/out/**
|
||||
/libs/
|
||||
# Ignore various files created by visual studio/msbuild
|
||||
*.ipch
|
||||
|
|
|
@ -549,6 +549,7 @@ else()
|
|||
check_vendoring_approved(fmt)
|
||||
message(STATUS "Using static fmt from Externals")
|
||||
add_subdirectory(Externals/fmt EXCLUDE_FROM_ALL)
|
||||
include_directories(Externals/fmt/include)
|
||||
endif()
|
||||
add_subdirectory(Externals/imgui)
|
||||
add_subdirectory(Externals/glslang)
|
||||
|
|
1262
Data/PlaybackGeckoCodes/GALE01r2.ini
Normal file
1262
Data/PlaybackGeckoCodes/GALE01r2.ini
Normal file
File diff suppressed because it is too large
Load diff
1262
Data/PlaybackGeckoCodes/GALJ01r2.ini
Normal file
1262
Data/PlaybackGeckoCodes/GALJ01r2.ini
Normal file
File diff suppressed because it is too large
Load diff
15
Data/linux-env.sh
Normal file
15
Data/linux-env.sh
Normal file
|
@ -0,0 +1,15 @@
|
|||
#!/bin/bash -e
|
||||
# linux-env.sh
|
||||
|
||||
# Add /usr/lib/ to LD_LIBRARY_PATH cause Ubuntu is dumb
|
||||
export LD_LIBRARY_PATH="/usr/lib/:$LD_LIBRARY_PATH"
|
||||
|
||||
if [[ $(env | grep -i wayland) ]]; then
|
||||
# wxWidgets 3.14 is GTK3, which seemingly has an issue or two when
|
||||
# running under Wayland. Explicitly setting this for Slippi avoids
|
||||
# those issues.
|
||||
export GDK_BACKEND=x11
|
||||
|
||||
# Disable Webkit compositing on Wayland cause it breaks stuff
|
||||
export WEBKIT_DISABLE_COMPOSITING_MODE=1
|
||||
fi
|
BIN
Data/slippi_dmg_background.png
Normal file
BIN
Data/slippi_dmg_background.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 41 KiB |
BIN
Data/slippi_dmg_icon.icns
Normal file
BIN
Data/slippi_dmg_icon.icns
Normal file
Binary file not shown.
206
Externals/SlippiLib/SlippiGame.cpp
vendored
206
Externals/SlippiLib/SlippiGame.cpp
vendored
|
@ -1,17 +1,20 @@
|
|||
#include <string>
|
||||
#include <codecvt>
|
||||
#include <locale>
|
||||
#include <string>
|
||||
|
||||
#include "SlippiGame.h"
|
||||
|
||||
namespace Slippi {
|
||||
|
||||
namespace Slippi
|
||||
{
|
||||
//**********************************************************************
|
||||
//* Event Handlers
|
||||
//**********************************************************************
|
||||
//The read operators will read a value and increment the index so the next read will read in the correct location
|
||||
uint8_t readByte(uint8_t* a, int& idx, uint32_t maxSize, uint8_t defaultValue) {
|
||||
if (idx >= (int)maxSize) {
|
||||
// The read operators will read a value and increment the index so the next read
|
||||
// will read in the correct location
|
||||
uint8_t readByte(uint8_t* a, int& idx, uint32_t maxSize, uint8_t defaultValue)
|
||||
{
|
||||
if (idx >= (int)maxSize)
|
||||
{
|
||||
idx += 1;
|
||||
return defaultValue;
|
||||
}
|
||||
|
@ -19,8 +22,10 @@ namespace Slippi {
|
|||
return a[idx++];
|
||||
}
|
||||
|
||||
uint16_t readHalf(uint8_t* a, int& idx, uint32_t maxSize, uint16_t defaultValue) {
|
||||
if (idx >= (int)maxSize) {
|
||||
uint16_t readHalf(uint8_t* a, int& idx, uint32_t maxSize, uint16_t defaultValue)
|
||||
{
|
||||
if (idx >= (int)maxSize)
|
||||
{
|
||||
idx += 2;
|
||||
return defaultValue;
|
||||
}
|
||||
|
@ -30,8 +35,10 @@ namespace Slippi {
|
|||
return value;
|
||||
}
|
||||
|
||||
uint32_t readWord(uint8_t* a, int& idx, uint32_t maxSize, uint32_t defaultValue) {
|
||||
if (idx >= (int)maxSize) {
|
||||
uint32_t readWord(uint8_t* a, int& idx, uint32_t maxSize, uint32_t defaultValue)
|
||||
{
|
||||
if (idx >= (int)maxSize)
|
||||
{
|
||||
idx += 4;
|
||||
return defaultValue;
|
||||
}
|
||||
|
@ -41,21 +48,25 @@ namespace Slippi {
|
|||
return value;
|
||||
}
|
||||
|
||||
float readFloat(uint8_t* a, int& idx, uint32_t maxSize, float defaultValue) {
|
||||
float readFloat(uint8_t* a, int& idx, uint32_t maxSize, float defaultValue)
|
||||
{
|
||||
uint32_t bytes = readWord(a, idx, maxSize, *(uint32_t*)(&defaultValue));
|
||||
return *(float*)(&bytes);
|
||||
}
|
||||
|
||||
void handleGameInit(Game* game, uint32_t maxSize) {
|
||||
void handleGameInit(Game* game, uint32_t maxSize)
|
||||
{
|
||||
int idx = 0;
|
||||
|
||||
// Read version number
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
game->version[i] = readByte(data, idx, maxSize, 0);
|
||||
}
|
||||
|
||||
// Read entire game info header
|
||||
for (int i = 0; i < GAME_INFO_HEADER_SIZE; i++) {
|
||||
for (int i = 0; i < GAME_INFO_HEADER_SIZE; i++)
|
||||
{
|
||||
game->settings.header[i] = readWord(data, idx, maxSize, 0);
|
||||
}
|
||||
|
||||
|
@ -64,15 +75,18 @@ namespace Slippi {
|
|||
|
||||
// Read UCF toggle bytes
|
||||
bool shouldRead = game->version[0] >= 1;
|
||||
for (int i = 0; i < UCF_TOGGLE_SIZE; i++) {
|
||||
for (int i = 0; i < UCF_TOGGLE_SIZE; i++)
|
||||
{
|
||||
uint32_t value = shouldRead ? readWord(data, idx, maxSize, 0) : 0;
|
||||
game->settings.ucfToggles[i] = value;
|
||||
}
|
||||
|
||||
// Read nametag for each player
|
||||
std::array<std::array<uint16_t, NAMETAG_SIZE>, 4> playerNametags;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < NAMETAG_SIZE; j++) {
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
for (int j = 0; j < NAMETAG_SIZE; j++)
|
||||
{
|
||||
playerNametags[i][j] = readHalf(data, idx, maxSize, 0);
|
||||
}
|
||||
}
|
||||
|
@ -86,13 +100,16 @@ namespace Slippi {
|
|||
// Pull header data into struct
|
||||
int player1Pos = 24; // This is the index of the first players character info
|
||||
std::array<uint32_t, Slippi::GAME_INFO_HEADER_SIZE> gameInfoHeader = game->settings.header;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
// this is the position in the array that this player's character info is stored
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
// this is the position in the array that this player's character info is
|
||||
// stored
|
||||
int pos = player1Pos + (9 * i);
|
||||
|
||||
uint32_t playerInfo = gameInfoHeader[pos];
|
||||
uint8_t playerType = (playerInfo & 0x00FF0000) >> 16;
|
||||
if (playerType == 0x3) {
|
||||
if (playerType == 0x3)
|
||||
{
|
||||
// Player type 3 is an empty slot
|
||||
continue;
|
||||
}
|
||||
|
@ -114,13 +131,15 @@ namespace Slippi {
|
|||
|
||||
auto majorVersion = game->version[0];
|
||||
auto minorVersion = game->version[1];
|
||||
if (majorVersion > 3 || (majorVersion == 3 && minorVersion >= 1)) {
|
||||
if (majorVersion > 3 || (majorVersion == 3 && minorVersion >= 1))
|
||||
{
|
||||
// After version 3.1.0 we added a dynamic gecko loading process. These
|
||||
// are needed before starting the game. areSettingsLoaded will be set
|
||||
// to true when they are received
|
||||
game->areSettingsLoaded = false;
|
||||
}
|
||||
else if (majorVersion > 1 || (majorVersion == 1 && minorVersion >= 6)) {
|
||||
else if (majorVersion > 1 || (majorVersion == 1 && minorVersion >= 6))
|
||||
{
|
||||
// Indicate settings loaded immediately if after version 1.6.0
|
||||
// Sheik game info was added in this version and so we no longer
|
||||
// need to wait
|
||||
|
@ -128,7 +147,8 @@ namespace Slippi {
|
|||
}
|
||||
}
|
||||
|
||||
void handleGeckoList(Game* game, uint32_t maxSize) {
|
||||
void handleGeckoList(Game* game, uint32_t maxSize)
|
||||
{
|
||||
game->settings.geckoCodes.clear();
|
||||
game->settings.geckoCodes.insert(game->settings.geckoCodes.end(), data, data + maxSize);
|
||||
|
||||
|
@ -136,7 +156,8 @@ namespace Slippi {
|
|||
game->areSettingsLoaded = true;
|
||||
}
|
||||
|
||||
void handleFrameStart(Game* game, uint32_t maxSize) {
|
||||
void handleFrameStart(Game* game, uint32_t maxSize)
|
||||
{
|
||||
int idx = 0;
|
||||
|
||||
// Check frame count
|
||||
|
@ -157,7 +178,8 @@ namespace Slippi {
|
|||
game->framesByIndex[frameCount] = frame;
|
||||
}
|
||||
|
||||
void handlePreFrameUpdate(Game* game, uint32_t maxSize) {
|
||||
void handlePreFrameUpdate(Game* game, uint32_t maxSize)
|
||||
{
|
||||
int idx = 0;
|
||||
|
||||
// Check frame count
|
||||
|
@ -168,7 +190,8 @@ namespace Slippi {
|
|||
FrameData* frame = frameUniquePtr.get();
|
||||
bool isNewFrame = true;
|
||||
|
||||
if (game->framesByIndex.count(frameCount)) {
|
||||
if (game->framesByIndex.count(frameCount))
|
||||
{
|
||||
// If this frame already exists, get the current frame
|
||||
frame = game->frames.back().get();
|
||||
isNewFrame = false;
|
||||
|
@ -203,7 +226,8 @@ namespace Slippi {
|
|||
p.lTrigger = readFloat(data, idx, maxSize, 0);
|
||||
p.rTrigger = readFloat(data, idx, maxSize, 0);
|
||||
|
||||
if (asmEvents[EVENT_PRE_FRAME_UPDATE] >= 59) {
|
||||
if (asmEvents[EVENT_PRE_FRAME_UPDATE] >= 59)
|
||||
{
|
||||
p.joystickXRaw = readByte(data, idx, maxSize, 0);
|
||||
}
|
||||
|
||||
|
@ -218,27 +242,31 @@ namespace Slippi {
|
|||
target->operator[](playerSlot) = p;
|
||||
|
||||
// Add frame to game
|
||||
if (isNewFrame) {
|
||||
if (isNewFrame)
|
||||
{
|
||||
frame->numSinceStart = game->frames.size();
|
||||
game->frames.push_back(std::move(frameUniquePtr));
|
||||
game->framesByIndex[frameCount] = frame;
|
||||
}
|
||||
}
|
||||
|
||||
void handlePostFrameUpdate(Game* game, uint32_t maxSize) {
|
||||
void handlePostFrameUpdate(Game* game, uint32_t maxSize)
|
||||
{
|
||||
int idx = 0;
|
||||
|
||||
// Check frame count
|
||||
int32_t frameCount = readWord(data, idx, maxSize, 0);
|
||||
|
||||
FrameData* frame;
|
||||
if (game->framesByIndex.count(frameCount)) {
|
||||
if (game->framesByIndex.count(frameCount))
|
||||
{
|
||||
// If this frame already exists, get the current frame
|
||||
frame = game->frames.back().get();
|
||||
}
|
||||
|
||||
// As soon as a post frame update happens, we know we have received all the inputs
|
||||
// This is used to determine if a frame is ready to be used for a replay (for mirroring)
|
||||
// As soon as a post frame update happens, we know we have received all the
|
||||
// inputs This is used to determine if a frame is ready to be used for a
|
||||
// replay (for mirroring)
|
||||
frame->inputsFullyFetched = true;
|
||||
|
||||
uint8_t playerSlot = readByte(data, idx, maxSize, 0);
|
||||
|
@ -249,24 +277,24 @@ namespace Slippi {
|
|||
p->internalCharacterId = readByte(data, idx, maxSize, 0);
|
||||
|
||||
// Check if a player started as sheik and update
|
||||
if (frameCount == GAME_FIRST_FRAME && p->internalCharacterId == GAME_SHEIK_INTERNAL_ID) {
|
||||
if (frameCount == GAME_FIRST_FRAME && p->internalCharacterId == GAME_SHEIK_INTERNAL_ID)
|
||||
{
|
||||
game->settings.players[playerSlot].characterId = GAME_SHEIK_EXTERNAL_ID;
|
||||
}
|
||||
|
||||
// Set settings loaded if this is the last character
|
||||
if (frameCount == GAME_FIRST_FRAME) {
|
||||
if (frameCount == GAME_FIRST_FRAME)
|
||||
{
|
||||
uint8_t lastPlayerIndex = 0;
|
||||
for (auto it = frame->players.begin(); it != frame->players.end(); ++it) {
|
||||
if (it->first <= lastPlayerIndex) {
|
||||
for (auto it = frame->players.begin(); it != frame->players.end(); ++it)
|
||||
{
|
||||
if (it->first <= lastPlayerIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
lastPlayerIndex = it->first;
|
||||
}
|
||||
|
||||
if (playerSlot >= lastPlayerIndex) {
|
||||
game->areSettingsLoaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -279,34 +307,41 @@ namespace Slippi {
|
|||
game->lastFinalizedFrame = lastFinalizedFrame;
|
||||
}
|
||||
|
||||
void handleGameEnd(Game* game, uint32_t maxSize) {
|
||||
void handleGameEnd(Game* game, uint32_t maxSize)
|
||||
{
|
||||
int idx = 0;
|
||||
|
||||
game->winCondition = readByte(data, idx, maxSize, 0);
|
||||
}
|
||||
|
||||
// This function gets the position where the raw data starts
|
||||
int getRawDataPosition(std::ifstream* f) {
|
||||
int getRawDataPosition(std::ifstream* f)
|
||||
{
|
||||
char buffer[2];
|
||||
f->seekg(0, std::ios::beg);
|
||||
f->read(buffer, 2);
|
||||
|
||||
if (buffer[0] == 0x36) {
|
||||
if (buffer[0] == 0x36)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (buffer[0] != '{') {
|
||||
if (buffer[0] != '{')
|
||||
{
|
||||
// TODO: Do something here to cause an error
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO: Read ubjson file to find the "raw" element and return the start of it
|
||||
// TODO: For now since raw is the first element the data will always start at 15
|
||||
// TODO: For now since raw is the first element the data will always start at
|
||||
// 15
|
||||
return 15;
|
||||
}
|
||||
|
||||
uint32_t getRawDataLength(std::ifstream* f, int position, int fileSize) {
|
||||
if (position == 0) {
|
||||
uint32_t getRawDataLength(std::ifstream* f, int position, int fileSize)
|
||||
{
|
||||
if (position == 0)
|
||||
{
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
|
@ -319,23 +354,23 @@ namespace Slippi {
|
|||
return length;
|
||||
}
|
||||
|
||||
std::unordered_map<uint8_t, uint32_t> getMessageSizes(std::ifstream* f, int position) {
|
||||
std::unordered_map<uint8_t, uint32_t> getMessageSizes(std::ifstream* f, int position)
|
||||
{
|
||||
char buffer[2];
|
||||
f->seekg(position, std::ios::beg);
|
||||
f->read(buffer, 2);
|
||||
if (buffer[0] != EVENT_PAYLOAD_SIZES) {
|
||||
if (buffer[0] != EVENT_PAYLOAD_SIZES)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
int payloadLength = buffer[1];
|
||||
std::unordered_map<uint8_t, uint32_t> messageSizes = {
|
||||
{ EVENT_PAYLOAD_SIZES, payloadLength }
|
||||
};
|
||||
|
||||
std::unordered_map<uint8_t, uint32_t> messageSizes = { {EVENT_PAYLOAD_SIZES, payloadLength} };
|
||||
|
||||
std::vector<char> messageSizesBuffer(payloadLength - 1);
|
||||
f->read(&messageSizesBuffer[0], payloadLength - 1);
|
||||
for (int i = 0; i < payloadLength - 1; i += 3) {
|
||||
for (int i = 0; i < payloadLength - 1; i += 3)
|
||||
{
|
||||
uint8_t command = messageSizesBuffer[i];
|
||||
|
||||
// Extract the bytes in u8s. Without this the chars don't or together well
|
||||
|
@ -349,8 +384,10 @@ namespace Slippi {
|
|||
return messageSizes;
|
||||
}
|
||||
|
||||
void SlippiGame::processData() {
|
||||
if (isProcessingComplete) {
|
||||
void SlippiGame::processData()
|
||||
{
|
||||
if (isProcessingComplete)
|
||||
{
|
||||
// If we have finished processing this file, return
|
||||
return;
|
||||
}
|
||||
|
@ -358,17 +395,20 @@ namespace Slippi {
|
|||
// This function will process as much data as possible
|
||||
int startPos = (int)file->tellg();
|
||||
file->seekg(startPos);
|
||||
if (startPos == 0) {
|
||||
if (startPos == 0)
|
||||
{
|
||||
file->seekg(0, std::ios::end);
|
||||
int len = (int)file->tellg();
|
||||
if (len < 2) {
|
||||
if (len < 2)
|
||||
{
|
||||
// If we can't read message sizes payload size yet, return
|
||||
return;
|
||||
}
|
||||
|
||||
int rawDataPos = getRawDataPosition(file.get());
|
||||
int rawDataLen = len - rawDataPos;
|
||||
if (rawDataLen < 2) {
|
||||
if (rawDataLen < 2)
|
||||
{
|
||||
// If we don't have enough raw data yet to read the replay file, return
|
||||
// Reset to begining so that the startPos condition will be hit again
|
||||
file->seekg(0);
|
||||
|
@ -382,7 +422,8 @@ namespace Slippi {
|
|||
file->read(buffer, 2);
|
||||
file->seekg(startPos);
|
||||
auto messageSizesSize = (int)buffer[1];
|
||||
if (rawDataLen < messageSizesSize) {
|
||||
if (rawDataLen < messageSizesSize)
|
||||
{
|
||||
// If we haven't received the full payload sizes message, return
|
||||
// Reset to begining so that the startPos condition will be hit again
|
||||
file->seekg(0);
|
||||
|
@ -400,7 +441,8 @@ namespace Slippi {
|
|||
// log << "Size to read: " << sizeToRead << "\n";
|
||||
// log << "Start Pos: " << startPos << "\n";
|
||||
// log << "End Pos: " << endPos << "\n\n";
|
||||
if (sizeToRead <= 0) {
|
||||
if (sizeToRead <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -408,7 +450,8 @@ namespace Slippi {
|
|||
file->read(&newData[0], sizeToRead);
|
||||
|
||||
int newDataPos = 0;
|
||||
while (newDataPos < sizeToRead) {
|
||||
while (newDataPos < sizeToRead)
|
||||
{
|
||||
auto command = newData[newDataPos];
|
||||
auto payloadSize = asmEvents[command];
|
||||
|
||||
|
@ -417,7 +460,8 @@ namespace Slippi {
|
|||
// log << "Command: " << buff << " | Payload Size: " << payloadSize << "\n";
|
||||
|
||||
auto remainingLen = sizeToRead - newDataPos;
|
||||
if (remainingLen < ((int)payloadSize + 1)) {
|
||||
if (remainingLen < ((int)payloadSize + 1))
|
||||
{
|
||||
// Here we don't have enough data to read the whole payload
|
||||
// Will be processed after getting more data (hopefully)
|
||||
file->seekg(-remainingLen, std::ios::cur);
|
||||
|
@ -430,7 +474,8 @@ namespace Slippi {
|
|||
uint32_t outerPayloadSize = payloadSize;
|
||||
|
||||
// Handle a split message, combining in until we possess the entire message
|
||||
if (command == EVENT_SPLIT_MESSAGE) {
|
||||
if (command == EVENT_SPLIT_MESSAGE)
|
||||
{
|
||||
if (shouldResetSplitMessageBuf)
|
||||
{
|
||||
splitMessageBuf.clear();
|
||||
|
@ -452,7 +497,8 @@ namespace Slippi {
|
|||
}
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
switch (command)
|
||||
{
|
||||
case EVENT_GAME_INIT:
|
||||
handleGameInit(game.get(), payloadSize);
|
||||
break;
|
||||
|
@ -468,9 +514,6 @@ namespace Slippi {
|
|||
case EVENT_POST_FRAME_UPDATE:
|
||||
handlePostFrameUpdate(game.get(), payloadSize);
|
||||
break;
|
||||
case EVENT_FRAME_END:
|
||||
handleFrameEnd(game.get(), payloadSize);
|
||||
break;
|
||||
case EVENT_GAME_END:
|
||||
handleGameEnd(game.get(), payloadSize);
|
||||
isProcessingComplete = true;
|
||||
|
@ -491,7 +534,8 @@ namespace Slippi {
|
|||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<SlippiGame> SlippiGame::FromFile(std::string path) {
|
||||
std::unique_ptr<SlippiGame> SlippiGame::FromFile(std::string path)
|
||||
{
|
||||
auto result = std::make_unique<SlippiGame>();
|
||||
result->game = std::make_unique<Game>();
|
||||
result->path = path;
|
||||
|
@ -505,7 +549,8 @@ namespace Slippi {
|
|||
#endif
|
||||
|
||||
// result->log.open("log.txt");
|
||||
if (!result->file->is_open()) {
|
||||
if (!result->file->is_open())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -523,16 +568,19 @@ namespace Slippi {
|
|||
return std::move(result);
|
||||
}
|
||||
|
||||
bool SlippiGame::IsProcessingComplete() {
|
||||
bool SlippiGame::IsProcessingComplete()
|
||||
{
|
||||
return isProcessingComplete;
|
||||
}
|
||||
|
||||
bool SlippiGame::AreSettingsLoaded() {
|
||||
bool SlippiGame::AreSettingsLoaded()
|
||||
{
|
||||
processData();
|
||||
return game->areSettingsLoaded;
|
||||
}
|
||||
|
||||
bool SlippiGame::DoesFrameExist(int32_t frame) {
|
||||
bool SlippiGame::DoesFrameExist(int32_t frame)
|
||||
{
|
||||
processData();
|
||||
return (bool)game->framesByIndex.count(frame);
|
||||
}
|
||||
|
@ -555,11 +603,12 @@ namespace Slippi {
|
|||
return game->framesByIndex.at(frame);
|
||||
}
|
||||
|
||||
FrameData* SlippiGame::GetFrameAt(uint32_t pos) {
|
||||
if (pos >= game->frames.size()) {
|
||||
FrameData* SlippiGame::GetFrameAt(uint32_t pos)
|
||||
{
|
||||
if (pos >= game->frames.size())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get the frame we want
|
||||
return game->frames[pos].get();
|
||||
}
|
||||
|
@ -574,7 +623,8 @@ namespace Slippi {
|
|||
return game->frameCount;
|
||||
}
|
||||
|
||||
GameSettings* SlippiGame::GetSettings() {
|
||||
GameSettings* SlippiGame::GetSettings()
|
||||
{
|
||||
processData();
|
||||
return &game->settings;
|
||||
}
|
||||
|
@ -586,4 +636,4 @@ namespace Slippi {
|
|||
uint8_t SlippiGame::GetGameEndMethod() {
|
||||
return game->winCondition;
|
||||
}
|
||||
}
|
||||
} // namespace Slippi
|
||||
|
|
8
Externals/SlippiLib/SlippiGame.h
vendored
8
Externals/SlippiLib/SlippiGame.h
vendored
|
@ -1,12 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace Slippi {
|
||||
const uint8_t EVENT_SPLIT_MESSAGE = 0x10;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -7,9 +7,8 @@
|
|||
#include <SlippiGame.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/File.h"
|
||||
#include "EXI_Device.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Core/Slippi/SlippiGameFileLoader.h"
|
||||
#include "Core/Slippi/SlippiMatchmaking.h"
|
||||
#include "Core/Slippi/SlippiNetplay.h"
|
||||
|
@ -18,6 +17,7 @@
|
|||
#include "Core/Slippi/SlippiSavestate.h"
|
||||
#include "Core/Slippi/SlippiSpectate.h"
|
||||
#include "Core/Slippi/SlippiUser.h"
|
||||
#include "EXI_Device.h"
|
||||
|
||||
#define ROLLBACK_MAX_FRAMES 7
|
||||
#define MAX_NAME_LENGTH 15
|
||||
|
@ -71,6 +71,7 @@ namespace ExpansionInterface
|
|||
CMD_GET_ONLINE_STATUS = 0xB9,
|
||||
CMD_CLEANUP_CONNECTION = 0xBA,
|
||||
CMD_GET_NEW_SEED = 0xBC,
|
||||
|
||||
// Misc
|
||||
CMD_LOG_MESSAGE = 0xD0,
|
||||
CMD_FILE_LENGTH = 0xD1,
|
||||
|
@ -113,6 +114,7 @@ namespace ExpansionInterface
|
|||
{CMD_GET_ONLINE_STATUS, 0},
|
||||
{CMD_CLEANUP_CONNECTION, 0},
|
||||
{CMD_GET_NEW_SEED, 0},
|
||||
|
||||
// Misc
|
||||
{CMD_LOG_MESSAGE, 0xFFFF}, // Variable size... will only work if by itself
|
||||
{CMD_FILE_LENGTH, 0x40},
|
||||
|
@ -142,6 +144,7 @@ namespace ExpansionInterface
|
|||
void closeFile();
|
||||
std::string generateFileName();
|
||||
bool checkFrameFullyFetched(s32 frameIndex);
|
||||
// bool shouldFFWFrame(s32 frameIndex);
|
||||
|
||||
// std::ofstream log;
|
||||
|
||||
|
@ -196,6 +199,7 @@ namespace ExpansionInterface
|
|||
|
||||
std::vector<u8> m_read_queue;
|
||||
std::unique_ptr<Slippi::SlippiGame> m_current_game = nullptr;
|
||||
SlippiSpectateServer* m_slippiserver = nullptr;
|
||||
SlippiMatchmaking::MatchSearchSettings lastSearch;
|
||||
|
||||
std::vector<u16> stagePool;
|
||||
|
@ -226,4 +230,4 @@ namespace ExpansionInterface
|
|||
std::map<s32, std::unique_ptr<SlippiSavestate>> activeSavestates;
|
||||
std::deque<std::unique_ptr<SlippiSavestate>> availableSavestates;
|
||||
};
|
||||
}
|
||||
} // namespace ExpansionInterface
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
#include "SlippiGameFileLoader.h"
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/File.h"
|
||||
#include "Core/HW/DVD/DVDThread.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Core/Boot/Boot.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/HW/DVD/DVDThread.h"
|
||||
|
||||
std::string getFilePath(std::string fileName)
|
||||
{
|
||||
|
@ -49,8 +49,10 @@ u32 SlippiGameFileLoader::LoadFile(std::string fileName, std::string& data)
|
|||
std::string fileContents;
|
||||
File::ReadFileToString(gameFilePath, fileContents);
|
||||
|
||||
// If the file was a diff file and the game is running, load the main file from ISO and apply patch
|
||||
if (gameFilePath.substr(gameFilePath.length() - 5) == ".diff" && Core::GetState() == Core::State::Running)
|
||||
// If the file was a diff file and the game is running, load the main file from ISO and apply
|
||||
// patch
|
||||
if (gameFilePath.substr(gameFilePath.length() - 5) == ".diff" &&
|
||||
Core::GetState() == Core::State::Running)
|
||||
{
|
||||
std::vector<u8> buf;
|
||||
INFO_LOG(SLIPPI, "Will process diff");
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include <open-vcdiff/src/google/vcdecoder.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
class SlippiGameFileLoader
|
||||
{
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
#include "SlippiMatchmaking.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "Common/Common.h"
|
||||
#include "Common/Version.h"
|
||||
#include "Common/ENetUtil.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "Common/Version.h"
|
||||
|
||||
class MmMessageType
|
||||
{
|
||||
|
@ -28,7 +28,8 @@ SlippiMatchmaking::SlippiMatchmaking(SlippiUser* user)
|
|||
m_client = nullptr;
|
||||
m_server = nullptr;
|
||||
|
||||
MM_HOST = Common::scm_slippi_semver_str.find("dev") == std::string::npos ? MM_HOST_PROD : MM_HOST_DEV;
|
||||
MM_HOST =
|
||||
Common::scm_slippi_semver_str.find("dev") == std::string::npos ? MM_HOST_PROD : MM_HOST_DEV;
|
||||
|
||||
generator = std::default_random_engine(Common::Timer::GetTimeMs());
|
||||
}
|
||||
|
@ -116,7 +117,8 @@ int SlippiMatchmaking::receiveMessage(json& msg, int timeoutMs)
|
|||
case ENET_EVENT_TYPE_RECEIVE:
|
||||
{
|
||||
std::vector<u8> buf;
|
||||
buf.insert(buf.end(), netEvent.packet->data, netEvent.packet->data + netEvent.packet->dataLength);
|
||||
buf.insert(buf.end(), netEvent.packet->data,
|
||||
netEvent.packet->data + netEvent.packet->dataLength);
|
||||
|
||||
std::string str(buf.begin(), buf.end());
|
||||
INFO_LOG(SLIPPI_ONLINE, "[Matchmaking] Received: %s", str.c_str());
|
||||
|
@ -291,7 +293,7 @@ void SlippiMatchmaking::startMatchmaking()
|
|||
// Send message to server to create ticket
|
||||
json request;
|
||||
request["type"] = MmMessageType::CREATE_TICKET;
|
||||
request["user"] = { {"uid", userInfo.uid}, {"playKey", userInfo.playKey} };
|
||||
request["user"] = {{"uid", userInfo.uid}, {"playKey", userInfo.play_key}};
|
||||
request["search"] = {{"mode", m_searchSettings.mode}, {"connectCode", connectCodeBuf}};
|
||||
request["appVersion"] = Common::scm_slippi_semver_str;
|
||||
sendMessage(request);
|
||||
|
@ -301,7 +303,8 @@ void SlippiMatchmaking::startMatchmaking()
|
|||
int rcvRes = receiveMessage(response, 5000);
|
||||
if (rcvRes != 0)
|
||||
{
|
||||
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Did not receive response from server for create ticket");
|
||||
ERROR_LOG(SLIPPI_ONLINE,
|
||||
"[Matchmaking] Did not receive response from server for create ticket");
|
||||
m_state = ProcessState::ERROR_ENCOUNTERED;
|
||||
m_errorMsg = "Failed to join mm queue";
|
||||
return;
|
||||
|
@ -369,7 +372,8 @@ void SlippiMatchmaking::handleMatchmaking()
|
|||
if (latestVersion != "")
|
||||
{
|
||||
// Update file to get new version number when the mm server tells us our version is outdated
|
||||
m_user->OverwriteLatestVersion(latestVersion); // Force latest version for people whose file updates dont work
|
||||
m_user->OverwriteLatestVersion(
|
||||
latestVersion); // Force latest version for people whose file updates dont work
|
||||
}
|
||||
|
||||
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Received error from server for get ticket");
|
||||
|
@ -391,15 +395,16 @@ void SlippiMatchmaking::handleMatchmaking()
|
|||
if (oppUser.is_object())
|
||||
{
|
||||
m_oppUser.uid = oppUser.value("uid", "");
|
||||
m_oppUser.displayName = oppUser.value("displayName", "");
|
||||
m_oppUser.connectCode = oppUser.value("connectCode", "");
|
||||
m_oppUser.display_name = oppUser.value("displayName", "");
|
||||
m_oppUser.connect_code = oppUser.value("connectCode", "");
|
||||
}
|
||||
|
||||
// Disconnect and destroy enet client to mm server
|
||||
terminateMmConnection();
|
||||
|
||||
m_state = ProcessState::OPPONENT_CONNECTING;
|
||||
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Opponent found. isDecider: %s", m_isHost ? "true" : "false");
|
||||
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Opponent found. isDecider: %s",
|
||||
m_isHost ? "true" : "false");
|
||||
}
|
||||
|
||||
void SlippiMatchmaking::handleConnecting()
|
||||
|
@ -407,7 +412,8 @@ void SlippiMatchmaking::handleConnecting()
|
|||
std::vector<std::string> ipParts = SplitString(m_oppIp, ':');
|
||||
|
||||
// Is host is now used to specify who the decider is
|
||||
auto client = std::make_unique<SlippiNetplayClient>(ipParts[0], std::stoi(ipParts[1]), m_hostPort, m_isHost);
|
||||
auto client = std::make_unique<SlippiNetplayClient>(ipParts[0], std::stoi(ipParts[1]), m_hostPort,
|
||||
m_isHost);
|
||||
|
||||
while (!m_netplayClient)
|
||||
{
|
||||
|
@ -425,7 +431,8 @@ void SlippiMatchmaking::handleConnecting()
|
|||
}
|
||||
else if (status != SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_CONNECTED)
|
||||
{
|
||||
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Connection attempt failed, looking for someone else.");
|
||||
ERROR_LOG(SLIPPI_ONLINE,
|
||||
"[Matchmaking] Connection attempt failed, looking for someone else.");
|
||||
|
||||
// Return to the start to get a new ticket to find someone else we can hopefully connect with
|
||||
m_netplayClient = nullptr;
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
#include "Core/Slippi/SlippiUser.h"
|
||||
|
||||
#include <enet/enet.h>
|
||||
#include <random>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <random>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using json = nlohmann::json;
|
||||
|
|
|
@ -11,10 +11,10 @@
|
|||
#include "Common/MD5.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Common/Timer.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/NetPlayProto.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/Config/NetplaySettings.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/NetPlayProto.h"
|
||||
//#include "Core/HW/EXI_DeviceIPL.h"
|
||||
//#include "Core/HW/SI.h"
|
||||
//#include "Core/HW/SI_DeviceGCController.h"
|
||||
|
@ -22,16 +22,16 @@
|
|||
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
||||
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
||||
//#include "Core/IPC_HLE/WII_IPC_HLE_Device_usb_bt_emu.h"
|
||||
#include "Core/Movie.h"
|
||||
#include "InputCommon/GCAdapter.h"
|
||||
#include "VideoCommon/OnScreenDisplay.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
#include <SlippiGame.h>
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <mbedtls/md5.h>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include "Core/Movie.h"
|
||||
#include "InputCommon/GCAdapter.h"
|
||||
#include "VideoCommon/OnScreenDisplay.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
static std::mutex pad_mutex;
|
||||
static std::mutex ack_mutex;
|
||||
|
@ -62,25 +62,26 @@ SlippiNetplayClient::~SlippiNetplayClient()
|
|||
}
|
||||
|
||||
// called from ---SLIPPI EXI--- thread
|
||||
SlippiNetplayClient::SlippiNetplayClient(const std::string& address, const u16 remotePort, const u16 localPort,
|
||||
bool isDecider)
|
||||
SlippiNetplayClient::SlippiNetplayClient(const std::string& address, const u16 remotePort,
|
||||
const u16 localPort, bool isDecider)
|
||||
#ifdef _WIN32
|
||||
: m_qos_handle(nullptr)
|
||||
, m_qos_flow_id(0)
|
||||
: m_qos_handle(nullptr), m_qos_flow_id(0)
|
||||
#endif
|
||||
{
|
||||
WARN_LOG(SLIPPI_ONLINE, "Initializing Slippi Netplay for port: %d, with host: %s", localPort,
|
||||
isDecider ? "true" : "false");
|
||||
|
||||
this->isDecider = isDecider;
|
||||
// this->playerIdx = isDecider ? 0 : 1;
|
||||
|
||||
// Local address
|
||||
ENetAddress* localAddr = nullptr;
|
||||
ENetAddress localAddrDef;
|
||||
|
||||
// It is important to be able to set the local port to listen on even in a client connection because
|
||||
// not doing so will break hole punching, the host is expecting traffic to come from a specific ip/port
|
||||
// and if the port does not match what it is expecting, it will not get through the NAT on some routers
|
||||
// It is important to be able to set the local port to listen on even in a client connection
|
||||
// because not doing so will break hole punching, the host is expecting traffic to come from a
|
||||
// specific ip/port and if the port does not match what it is expecting, it will not get through
|
||||
// the NAT on some routers
|
||||
if (localPort > 0)
|
||||
{
|
||||
INFO_LOG(SLIPPI_ONLINE, "Setting up local address");
|
||||
|
@ -91,7 +92,8 @@ SlippiNetplayClient::SlippiNetplayClient(const std::string& address, const u16 r
|
|||
localAddr = &localAddrDef;
|
||||
}
|
||||
|
||||
// TODO: Figure out how to use a local port when not hosting without accepting incoming connections
|
||||
// TODO: Figure out how to use a local port when not hosting without accepting incoming
|
||||
// connections
|
||||
m_client = enet_host_create(localAddr, 2, 3, 0, 0);
|
||||
|
||||
if (m_client == nullptr)
|
||||
|
@ -126,7 +128,8 @@ SlippiNetplayClient::SlippiNetplayClient(bool isDecider)
|
|||
unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
|
||||
{
|
||||
NetPlay::MessageId mid = 0;
|
||||
if (!(packet >> mid)) {
|
||||
if (!(packet >> mid))
|
||||
{
|
||||
ERROR_LOG(SLIPPI_ONLINE, "Received empty netplay packet");
|
||||
return 0;
|
||||
}
|
||||
|
@ -136,7 +139,8 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
|
|||
case NetPlay::NP_MSG_SLIPPI_PAD:
|
||||
{
|
||||
int32_t frame;
|
||||
if (!(packet >> frame)) {
|
||||
if (!(packet >> frame))
|
||||
{
|
||||
ERROR_LOG(SLIPPI_ONLINE, "Netplay packet too small to read frame count");
|
||||
break;
|
||||
}
|
||||
|
@ -151,8 +155,8 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
|
|||
auto timing = lastFrameTiming;
|
||||
if (!hasGameStarted)
|
||||
{
|
||||
// Handle case where opponent starts sending inputs before our game has reached frame 1. This will
|
||||
// continuously say frame 0 is now to prevent opp from getting too far ahead
|
||||
// Handle case where opponent starts sending inputs before our game has reached frame 1. This
|
||||
// will continuously say frame 0 is now to prevent opp from getting too far ahead
|
||||
timing.frame = 0;
|
||||
timing.timeUs = curTime;
|
||||
}
|
||||
|
@ -161,8 +165,8 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
|
|||
s64 frameDiffOffsetUs = 16683 * (timing.frame - frame);
|
||||
s64 timeOffsetUs = opponentSendTimeUs - timing.timeUs + frameDiffOffsetUs;
|
||||
|
||||
INFO_LOG(SLIPPI_ONLINE, "[Offset] Opp Frame: %d, My Frame: %d. Time offset: %lld", frame, timing.frame,
|
||||
timeOffsetUs);
|
||||
INFO_LOG(SLIPPI_ONLINE, "[Offset] Opp Frame: %d, My Frame: %d. Time offset: %lld", frame,
|
||||
timing.frame, timeOffsetUs);
|
||||
|
||||
// Add this offset to circular buffer for use later
|
||||
if (frameOffsetData.buf.size() < SLIPPI_ONLINE_LOCKSTEP_INTERVAL)
|
||||
|
@ -183,19 +187,22 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
|
|||
int inputsToCopy = frame - headFrame;
|
||||
|
||||
// Check that the packet actually contains the data it claims to
|
||||
//if ((5 + inputsToCopy * SLIPPI_PAD_DATA_SIZE) > (int)packet.getDataSize())
|
||||
//{
|
||||
// ERROR_LOG(SLIPPI_ONLINE, "Netplay packet too small to read pad buffer");
|
||||
// break;
|
||||
//}
|
||||
if ((5 + inputsToCopy * SLIPPI_PAD_DATA_SIZE) > (int)packet.getDataSize())
|
||||
{
|
||||
ERROR_LOG(SLIPPI_ONLINE,
|
||||
"Netplay packet too small to read pad buffer. Size: %d, Inputs: %d, MinSize: %d",
|
||||
(int)packet.getDataSize(), inputsToCopy, 5 + inputsToCopy * SLIPPI_PAD_DATA_SIZE);
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = inputsToCopy - 1; i >= 0; i--)
|
||||
{
|
||||
auto pad = std::make_unique<SlippiPad>(frame - i, &packetData[5 + i * SLIPPI_PAD_DATA_SIZE]);
|
||||
auto pad =
|
||||
std::make_unique<SlippiPad>(frame - i, &packetData[5 + i * SLIPPI_PAD_DATA_SIZE]);
|
||||
|
||||
INFO_LOG(SLIPPI_ONLINE, "Rcv [%d] -> %02X %02X %02X %02X %02X %02X %02X %02X", pad->frame,
|
||||
pad->padBuf[0], pad->padBuf[1], pad->padBuf[2], pad->padBuf[3], pad->padBuf[4], pad->padBuf[5],
|
||||
pad->padBuf[6], pad->padBuf[7]);
|
||||
pad->padBuf[0], pad->padBuf[1], pad->padBuf[2], pad->padBuf[3], pad->padBuf[4],
|
||||
pad->padBuf[5], pad->padBuf[6], pad->padBuf[7]);
|
||||
|
||||
remotePadQueue.push_front(std::move(pad));
|
||||
}
|
||||
|
@ -215,7 +222,8 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
|
|||
|
||||
// Store last frame acked
|
||||
int32_t frame;
|
||||
if (!(packet >> frame)) {
|
||||
if (!(packet >> frame))
|
||||
{
|
||||
ERROR_LOG(SLIPPI_ONLINE, "Ack packet too small to read frame");
|
||||
break;
|
||||
}
|
||||
|
@ -240,8 +248,9 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
|
|||
pingUs = Common::Timer::GetTimeUs() - sendTime;
|
||||
if (g_ActiveConfig.bShowNetPlayPing && frame % SLIPPI_PING_DISPLAY_INTERVAL == 0)
|
||||
{
|
||||
OSD::AddTypedMessage(OSD::MessageType::NetPlayPing, StringFromFormat("Ping: %u", pingUs / 1000),
|
||||
OSD::Duration::NORMAL, OSD::Color::CYAN);
|
||||
OSD::AddTypedMessage(OSD::MessageType::NetPlayPing,
|
||||
StringFromFormat("Ping: %u", pingUs / 1000), OSD::Duration::NORMAL,
|
||||
OSD::Color::CYAN);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -254,15 +263,18 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet)
|
|||
|
||||
// This might be a good place to reset some logic? Game can't start until we receive this msg
|
||||
// so this should ensure that everything is initialized before the game starts
|
||||
// TODO: This could cause issues in the case of a desync? If this is ever received mid-game, bad things
|
||||
// TODO: will happen. Consider improving this
|
||||
hasGameStarted = false;
|
||||
|
||||
// Reset remote pad queue such that next inputs that we get are not compared to inputs from last
|
||||
// game
|
||||
remotePadQueue.clear();
|
||||
}
|
||||
break;
|
||||
|
||||
case NetPlay::NP_MSG_SLIPPI_CONN_SELECTED:
|
||||
{
|
||||
// Currently this is unused but the intent is to support two-way simultaneous connection attempts
|
||||
// Currently this is unused but the intent is to support two-way simultaneous connection
|
||||
// attempts
|
||||
isConnectionSelected = true;
|
||||
}
|
||||
break;
|
||||
|
@ -283,7 +295,8 @@ void SlippiNetplayClient::writeToPacket(sf::Packet& packet, SlippiPlayerSelectio
|
|||
packet << s.rngOffset;
|
||||
}
|
||||
|
||||
std::unique_ptr<SlippiPlayerSelections> SlippiNetplayClient::readSelectionsFromPacket(sf::Packet& packet)
|
||||
std::unique_ptr<SlippiPlayerSelections>
|
||||
SlippiNetplayClient::readSelectionsFromPacket(sf::Packet& packet)
|
||||
{
|
||||
auto s = std::make_unique<SlippiPlayerSelections>();
|
||||
|
||||
|
@ -414,7 +427,8 @@ void SlippiNetplayClient::ThreadFunc()
|
|||
|
||||
// this will fail if we're not admin
|
||||
// sets DSCP to the same as linux (0x2e)
|
||||
QOSSetFlow(m_qos_handle, m_qos_flow_id, QOSSetOutgoingDSCPValue, sizeof(DWORD), &dscp, 0, nullptr);
|
||||
QOSSetFlow(m_qos_handle, m_qos_flow_id, QOSSetOutgoingDSCPValue, sizeof(DWORD), &dscp, 0,
|
||||
nullptr);
|
||||
|
||||
qos_success = true;
|
||||
}
|
||||
|
@ -431,7 +445,8 @@ void SlippiNetplayClient::ThreadFunc()
|
|||
// https://www.tucny.com/Home/dscp-tos
|
||||
// ef is better than cs7
|
||||
int tos_val = 0xb8;
|
||||
qos_success = setsockopt(m_server->host->socket, IPPROTO_IP, IP_TOS, &tos_val, sizeof(tos_val)) == 0;
|
||||
qos_success =
|
||||
setsockopt(m_server->host->socket, IPPROTO_IP, IP_TOS, &tos_val, sizeof(tos_val)) == 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -514,12 +529,7 @@ void SlippiNetplayClient::StartSlippiGame()
|
|||
|
||||
localPadQueue.clear();
|
||||
|
||||
remotePadQueue.clear();
|
||||
for (s32 i = 1; i <= 2; i++)
|
||||
{
|
||||
std::unique_ptr<SlippiPad> pad = std::make_unique<SlippiPad>(i);
|
||||
remotePadQueue.push_front(std::move(pad));
|
||||
}
|
||||
// Remote pad should have been cleared when receiving the match selections
|
||||
|
||||
// Reset match info for next game
|
||||
matchInfo.Reset();
|
||||
|
@ -540,8 +550,10 @@ void SlippiNetplayClient::SendConnectionSelected()
|
|||
void SlippiNetplayClient::SendSlippiPad(std::unique_ptr<SlippiPad> pad)
|
||||
{
|
||||
auto status = slippiConnectStatus;
|
||||
bool connectionFailed = status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_FAILED;
|
||||
bool connectionDisconnected = status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_DISCONNECTED;
|
||||
bool connectionFailed =
|
||||
status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_FAILED;
|
||||
bool connectionDisconnected =
|
||||
status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_DISCONNECTED;
|
||||
if (connectionFailed || connectionDisconnected)
|
||||
{
|
||||
return;
|
||||
|
@ -549,8 +561,9 @@ void SlippiNetplayClient::SendSlippiPad(std::unique_ptr<SlippiPad> pad)
|
|||
|
||||
// if (pad && isDecider)
|
||||
//{
|
||||
// ERROR_LOG(SLIPPI_ONLINE, "[%d] %X %X %X %X %X %X %X %X", pad->frame, pad->padBuf[0], pad->padBuf[1],
|
||||
// pad->padBuf[2], pad->padBuf[3], pad->padBuf[4], pad->padBuf[5], pad->padBuf[6], pad->padBuf[7]);
|
||||
// ERROR_LOG(SLIPPI_ONLINE, "[%d] %X %X %X %X %X %X %X %X", pad->frame, pad->padBuf[0],
|
||||
// pad->padBuf[1], pad->padBuf[2], pad->padBuf[3], pad->padBuf[4], pad->padBuf[5],
|
||||
// pad->padBuf[6], pad->padBuf[7]);
|
||||
//}
|
||||
|
||||
if (pad)
|
||||
|
@ -577,12 +590,12 @@ void SlippiNetplayClient::SendSlippiPad(std::unique_ptr<SlippiPad> pad)
|
|||
*spac << static_cast<NetPlay::MessageId>(NetPlay::NP_MSG_SLIPPI_PAD);
|
||||
*spac << frame;
|
||||
|
||||
INFO_LOG(SLIPPI_ONLINE, "Sending a packet of inputs [%d]...", frame);
|
||||
// INFO_LOG(SLIPPI_ONLINE, "Sending a packet of inputs [%d]...", frame);
|
||||
for (auto it = localPadQueue.begin(); it != localPadQueue.end(); ++it)
|
||||
{
|
||||
INFO_LOG(SLIPPI_ONLINE, "Send [%d] -> %02X %02X %02X %02X %02X %02X %02X %02X", (*it)->frame, (*it)->padBuf[0],
|
||||
(*it)->padBuf[1], (*it)->padBuf[2], (*it)->padBuf[3], (*it)->padBuf[4], (*it)->padBuf[5],
|
||||
(*it)->padBuf[6], (*it)->padBuf[7]);
|
||||
/*INFO_LOG(SLIPPI_ONLINE, "Send [%d] -> %02X %02X %02X %02X %02X %02X %02X %02X", (*it)->frame,
|
||||
(*it)->padBuf[0], (*it)->padBuf[1], (*it)->padBuf[2], (*it)->padBuf[3],
|
||||
(*it)->padBuf[4], (*it)->padBuf[5], (*it)->padBuf[6], (*it)->padBuf[7]);*/
|
||||
spac->append((*it)->padBuf, SLIPPI_PAD_DATA_SIZE); // only transfer 8 bytes per pad
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,16 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <SFML/Network/Packet.hpp>
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Event.h"
|
||||
#include "Common/Timer.h"
|
||||
|
@ -11,22 +21,13 @@
|
|||
#include "Core/NetPlayProto.h"
|
||||
#include "Core/Slippi/SlippiPad.h"
|
||||
#include "InputCommon/GCPadStatus.h"
|
||||
#include <SFML/Network/Packet.hpp>
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Qos2.h>
|
||||
#endif
|
||||
|
||||
#define SLIPPI_ONLINE_LOCKSTEP_INTERVAL 30 // Number of frames to wait before attempting to time-sync
|
||||
#define SLIPPI_ONLINE_LOCKSTEP_INTERVAL \
|
||||
30 // Number of frames to wait before attempting to time-sync
|
||||
#define SLIPPI_PING_DISPLAY_INTERVAL 60
|
||||
|
||||
struct SlippiRemotePadOutput
|
||||
|
@ -98,7 +99,8 @@ public:
|
|||
void SendAsync(std::unique_ptr<sf::Packet> packet);
|
||||
|
||||
SlippiNetplayClient(bool isDecider); // Make a dummy client
|
||||
SlippiNetplayClient(const std::string& address, const u16 remotePort, const u16 localPort, bool isDecider);
|
||||
SlippiNetplayClient(const std::string& address, const u16 remotePort, const u16 localPort,
|
||||
bool isDecider);
|
||||
~SlippiNetplayClient();
|
||||
|
||||
// Slippi Online
|
||||
|
|
|
@ -15,4 +15,3 @@ public:
|
|||
int32_t frame;
|
||||
u8 padBuf[SLIPPI_PAD_FULL_SIZE];
|
||||
};
|
||||
|
||||
|
|
|
@ -86,8 +86,7 @@ void SlippiPlaybackStatus::prepareSlippiPlayback(s32& frameIndex)
|
|||
// TODO: figure out why sometimes playback frame increments past targetFrameNum
|
||||
if (inSlippiPlayback && frameIndex >= targetFrameNum)
|
||||
{
|
||||
INFO_LOG(SLIPPI, "Reached frame %d. Target was %d. Unblocking", frameIndex,
|
||||
targetFrameNum);
|
||||
INFO_LOG(SLIPPI, "Reached frame %d. Target was %d. Unblocking", frameIndex, targetFrameNum);
|
||||
cv_waitingForTargetFrame.notify_one();
|
||||
}
|
||||
}
|
||||
|
@ -122,7 +121,8 @@ void SlippiPlaybackStatus::processInitialState()
|
|||
// Doing it here to get it out of the way and prevent stutters later
|
||||
// Subsequent calls to SaveToBuffer for cState take ~1 frame
|
||||
State::SaveToBuffer(cState);
|
||||
if (SConfig::GetInstance().m_slippiEnableSeek) {
|
||||
if (SConfig::GetInstance().m_slippiEnableSeek)
|
||||
{
|
||||
SConfig::GetInstance().bHideCursor = false;
|
||||
}
|
||||
};
|
||||
|
@ -138,7 +138,8 @@ void SlippiPlaybackStatus::SavestateThread()
|
|||
{
|
||||
// Wait to hit one of the intervals
|
||||
// Possible while rewinding that we hit this wait again.
|
||||
while (shouldRunThreads && (currentPlaybackFrame - Slippi::PLAYBACK_FIRST_SAVE) % FRAME_INTERVAL != 0)
|
||||
while (shouldRunThreads &&
|
||||
(currentPlaybackFrame - Slippi::PLAYBACK_FIRST_SAVE) % FRAME_INTERVAL != 0)
|
||||
condVar.wait(intervalLock);
|
||||
|
||||
if (!shouldRunThreads)
|
||||
|
@ -171,11 +172,13 @@ void SlippiPlaybackStatus::SavestateThread()
|
|||
|
||||
void SlippiPlaybackStatus::seekToFrame()
|
||||
{
|
||||
if (seekMtx.try_lock()) {
|
||||
if (seekMtx.try_lock())
|
||||
{
|
||||
if (targetFrameNum < Slippi::PLAYBACK_FIRST_SAVE)
|
||||
targetFrameNum = Slippi::PLAYBACK_FIRST_SAVE;
|
||||
|
||||
if (targetFrameNum > lastFrame) {
|
||||
if (targetFrameNum > lastFrame)
|
||||
{
|
||||
targetFrameNum = lastFrame;
|
||||
}
|
||||
|
||||
|
@ -188,8 +191,10 @@ void SlippiPlaybackStatus::seekToFrame()
|
|||
if (prevState != Core::State::Paused)
|
||||
Core::SetState(Core::State::Paused);
|
||||
|
||||
s32 closestStateFrame = targetFrameNum - emod(targetFrameNum - Slippi::PLAYBACK_FIRST_SAVE, FRAME_INTERVAL);
|
||||
bool isLoadingStateOptimal = targetFrameNum < currentPlaybackFrame || closestStateFrame > currentPlaybackFrame;
|
||||
s32 closestStateFrame =
|
||||
targetFrameNum - emod(targetFrameNum - Slippi::PLAYBACK_FIRST_SAVE, FRAME_INTERVAL);
|
||||
bool isLoadingStateOptimal =
|
||||
targetFrameNum < currentPlaybackFrame || closestStateFrame > currentPlaybackFrame;
|
||||
|
||||
if (isLoadingStateOptimal)
|
||||
{
|
||||
|
@ -219,7 +224,8 @@ void SlippiPlaybackStatus::seekToFrame()
|
|||
futureDiffs.count(closestActualStateFrame) == 0)
|
||||
closestActualStateFrame -= FRAME_INTERVAL;
|
||||
|
||||
// only load a savestate if we find one past our current frame since we are seeking forwards
|
||||
// only load a savestate if we find one past our current frame since we are seeking
|
||||
// forwards
|
||||
if (closestActualStateFrame > currentPlaybackFrame)
|
||||
loadState(closestActualStateFrame);
|
||||
}
|
||||
|
@ -236,18 +242,22 @@ void SlippiPlaybackStatus::seekToFrame()
|
|||
setHardFFW(false);
|
||||
}
|
||||
|
||||
// We've reached the frame we want. Reset targetFrameNum and release mutex so another seek can be performed
|
||||
// We've reached the frame we want. Reset targetFrameNum and release mutex so another seek can
|
||||
// be performed
|
||||
g_playbackStatus->currentPlaybackFrame = targetFrameNum;
|
||||
targetFrameNum = INT_MAX;
|
||||
Core::SetState(prevState);
|
||||
seekMtx.unlock();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
INFO_LOG(SLIPPI, "Already seeking. Ignoring this call");
|
||||
}
|
||||
}
|
||||
|
||||
// Set isHardFFW and update OC settings to speed up the FFW
|
||||
void SlippiPlaybackStatus::setHardFFW(bool enable) {
|
||||
void SlippiPlaybackStatus::setHardFFW(bool enable)
|
||||
{
|
||||
if (enable)
|
||||
{
|
||||
SConfig::GetInstance().m_OCEnable = true;
|
||||
|
@ -262,7 +272,6 @@ void SlippiPlaybackStatus::setHardFFW(bool enable) {
|
|||
isHardFFW = enable;
|
||||
}
|
||||
|
||||
|
||||
void SlippiPlaybackStatus::loadState(s32 closestStateFrame)
|
||||
{
|
||||
if (closestStateFrame == Slippi::PLAYBACK_FIRST_SAVE)
|
||||
|
@ -270,7 +279,8 @@ void SlippiPlaybackStatus::loadState(s32 closestStateFrame)
|
|||
else
|
||||
{
|
||||
std::string stateString;
|
||||
decoder.Decode((char*)iState.data(), iState.size(), futureDiffs[closestStateFrame].get(), &stateString);
|
||||
decoder.Decode((char*)iState.data(), iState.size(), futureDiffs[closestStateFrame].get(),
|
||||
&stateString);
|
||||
std::vector<u8> stateToLoad(stateString.begin(), stateString.end());
|
||||
State::LoadFromBuffer(stateToLoad);
|
||||
}
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <SlippiLib/SlippiGame.h>
|
||||
#include <open-vcdiff/src/google/vcdecoder.h>
|
||||
#include <open-vcdiff/src/google/vcencoder.h>
|
||||
#include <SlippiLib/SlippiGame.h>
|
||||
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "../../Common/CommonTypes.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
|
||||
class SlippiPlaybackStatus
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "SlippiReplayComm.h"
|
||||
#include <cctype>
|
||||
#include <memory>
|
||||
#include "SlippiReplayComm.h"
|
||||
#include "Common/CommonPaths.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/Logging/LogManager.h"
|
||||
|
@ -18,7 +18,8 @@ static inline void ltrim(std::string& s)
|
|||
// trim from end (in place)
|
||||
static inline void rtrim(std::string& s)
|
||||
{
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(), s.end());
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(),
|
||||
s.end());
|
||||
}
|
||||
|
||||
// trim from both ends (in place)
|
||||
|
@ -35,7 +36,9 @@ SlippiReplayComm::SlippiReplayComm()
|
|||
configFilePath = SConfig::GetInstance().m_strSlippiInput.c_str();
|
||||
}
|
||||
|
||||
SlippiReplayComm::~SlippiReplayComm() {}
|
||||
SlippiReplayComm::~SlippiReplayComm()
|
||||
{
|
||||
}
|
||||
|
||||
SlippiReplayComm::CommSettings SlippiReplayComm::getSettings()
|
||||
{
|
||||
|
@ -178,8 +181,8 @@ void SlippiReplayComm::loadFile()
|
|||
{
|
||||
WARN_LOG(EXPANSIONINTERFACE, "Comm file load error detected. Check file format");
|
||||
|
||||
// Reset in the case of read error. this fixes a race condition where file mod time changes but
|
||||
// the file is not readable yet?
|
||||
// Reset in the case of read error. this fixes a race condition where file mod time changes
|
||||
// but the file is not readable yet?
|
||||
configLastLoadModTime = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <SlippiGame.h>
|
||||
#include <limits.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <SlippiGame.h>
|
||||
|
||||
#include <Common/CommonTypes.h>
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "SlippiSavestate.h"
|
||||
#include <vector>
|
||||
#include "Common/CommonFuncs.h"
|
||||
#include "Common/MemoryUtil.h"
|
||||
#include "Core/HW/AudioInterface.h"
|
||||
|
@ -11,7 +12,6 @@
|
|||
#include "Core/HW/ProcessorInterface.h"
|
||||
#include "Core/HW/SI/SI.h"
|
||||
#include "Core/HW/VideoInterface.h"
|
||||
#include <vector>
|
||||
|
||||
SlippiSavestate::SlippiSavestate()
|
||||
{
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include <unordered_map>
|
||||
|
||||
class PointerWrap;
|
||||
|
||||
|
@ -14,7 +14,10 @@ public:
|
|||
u32 address;
|
||||
u32 length;
|
||||
|
||||
bool operator==(const PreserveBlock& p) const { return address == p.address && length == p.length; }
|
||||
bool operator==(const PreserveBlock& p) const
|
||||
{
|
||||
return address == p.address && length == p.length;
|
||||
}
|
||||
};
|
||||
|
||||
SlippiSavestate();
|
||||
|
|
|
@ -76,8 +76,8 @@ SlippiUser::SlippiUser()
|
|||
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 5000);
|
||||
|
||||
// Set up HTTP Headers
|
||||
m_curlHeaderList = curl_slist_append(m_curlHeaderList, "Content-Type: application/json");
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, m_curlHeaderList);
|
||||
m_curl_header_list = curl_slist_append(m_curl_header_list, "Content-Type: application/json");
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, m_curl_header_list);
|
||||
|
||||
#ifdef _WIN32
|
||||
// ALPN support is enabled by default but requires Windows >= 8.1.
|
||||
|
@ -91,56 +91,58 @@ SlippiUser::SlippiUser()
|
|||
SlippiUser::~SlippiUser()
|
||||
{
|
||||
// Wait for thread to terminate
|
||||
runThread = false;
|
||||
if (fileListenThread.joinable())
|
||||
fileListenThread.join();
|
||||
m_run_thread = false;
|
||||
if (m_file_listen_thread.joinable())
|
||||
m_file_listen_thread.join();
|
||||
|
||||
if (m_curl)
|
||||
{
|
||||
curl_slist_free_all(m_curlHeaderList);
|
||||
curl_slist_free_all(m_curl_header_list);
|
||||
curl_easy_cleanup(m_curl);
|
||||
}
|
||||
}
|
||||
|
||||
bool SlippiUser::AttemptLogin()
|
||||
{
|
||||
std::string userFilePath = getUserFilePath();
|
||||
std::string user_file_path = getUserFilePath();
|
||||
|
||||
INFO_LOG(SLIPPI_ONLINE, "Looking for file at: %s", userFilePath.c_str());
|
||||
INFO_LOG(SLIPPI_ONLINE, "Looking for file at: %s", user_file_path.c_str());
|
||||
|
||||
{
|
||||
std::string userFilePathTxt =
|
||||
userFilePath + ".txt"; // Put the filename here in its own scope because we don't really need it elsewhere
|
||||
if (File::Exists(userFilePathTxt))
|
||||
// Put the filename here in its own scope because we don't really need it elsewhere
|
||||
std::string user_file_path_txt = user_file_path + ".txt";
|
||||
if (File::Exists(user_file_path_txt))
|
||||
{
|
||||
// If both files exist we just log they exist and take no further action
|
||||
if (File::Exists(userFilePath))
|
||||
if (File::Exists(user_file_path))
|
||||
{
|
||||
INFO_LOG(SLIPPI_ONLINE,
|
||||
"Found both .json.txt and .json file for user data. Using .json and ignoring the .json.txt");
|
||||
INFO_LOG(SLIPPI_ONLINE, "Found both .json.txt and .json file for user data. Using .json "
|
||||
"and ignoring the .json.txt");
|
||||
}
|
||||
// If only the .txt file exists move the contents to a json file and log if it fails
|
||||
else if (!File::Rename(userFilePathTxt, userFilePath))
|
||||
else if (!File::Rename(user_file_path_txt, user_file_path))
|
||||
{
|
||||
WARN_LOG(SLIPPI_ONLINE, "Could not move file %s to %s", userFilePathTxt.c_str(), userFilePath.c_str());
|
||||
WARN_LOG(SLIPPI_ONLINE, "Could not move file %s to %s", user_file_path_txt.c_str(),
|
||||
user_file_path.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get user file
|
||||
std::string userFileContents;
|
||||
File::ReadFileToString(userFilePath, userFileContents);
|
||||
std::string user_file_contents;
|
||||
File::ReadFileToString(user_file_path, user_file_contents);
|
||||
|
||||
userInfo = parseFile(userFileContents);
|
||||
m_user_info = parseFile(user_file_contents);
|
||||
|
||||
isLoggedIn = !userInfo.uid.empty();
|
||||
if (isLoggedIn)
|
||||
m_is_logged_in = !m_user_info.uid.empty();
|
||||
if (m_is_logged_in)
|
||||
{
|
||||
overwriteFromServer();
|
||||
WARN_LOG(SLIPPI_ONLINE, "Found user %s (%s)", userInfo.displayName.c_str(), userInfo.uid.c_str());
|
||||
WARN_LOG(SLIPPI_ONLINE, "Found user %s (%s)", m_user_info.display_name.c_str(),
|
||||
m_user_info.uid.c_str());
|
||||
}
|
||||
|
||||
return isLoggedIn;
|
||||
return m_is_logged_in;
|
||||
}
|
||||
|
||||
void SlippiUser::OpenLogInPage()
|
||||
|
@ -149,27 +151,28 @@ void SlippiUser::OpenLogInPage()
|
|||
std::string path = getUserFilePath();
|
||||
|
||||
#ifdef _WIN32
|
||||
// On windows, sometimes the path can have backslashes and slashes mixed, convert all to backslashes
|
||||
// On windows, sometimes the path can have backslashes and slashes mixed, convert all to
|
||||
// backslashes
|
||||
path = ReplaceAll(path, "\\", "\\");
|
||||
path = ReplaceAll(path, "/", "\\");
|
||||
#endif
|
||||
|
||||
#ifndef __APPLE__
|
||||
char* escapedPath = curl_easy_escape(nullptr, path.c_str(), (int)path.length());
|
||||
path = std::string(escapedPath);
|
||||
curl_free(escapedPath);
|
||||
char* escaped_path = curl_easy_escape(nullptr, path.c_str(), (int)path.length());
|
||||
path = std::string(escaped_path);
|
||||
curl_free(escaped_path);
|
||||
#endif
|
||||
|
||||
std::string fullUrl = url + "?path=" + path;
|
||||
std::string full_url = url + "?path=" + path;
|
||||
|
||||
INFO_LOG(SLIPPI_ONLINE, "[User] Login at path: %s", fullUrl.c_str());
|
||||
INFO_LOG(SLIPPI_ONLINE, "[User] Login at path: %s", full_url.c_str());
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string command = "explorer \"" + fullUrl + "\"";
|
||||
std::string command = "explorer \"" + full_url + "\"";
|
||||
#elif defined(__APPLE__)
|
||||
std::string command = "open \"" + fullUrl + "\"";
|
||||
std::string command = "open \"" + full_url + "\"";
|
||||
#else
|
||||
std::string command = "xdg-open \"" + fullUrl + "\""; // Linux
|
||||
std::string command = "xdg-open \"" + full_url + "\""; // Linux
|
||||
#endif
|
||||
|
||||
RunSystemCommand(command);
|
||||
|
@ -181,16 +184,21 @@ void SlippiUser::UpdateApp()
|
|||
auto isoPath = SConfig::GetInstance().m_strIsoPath;
|
||||
|
||||
std::string path = File::GetExeDirectory() + "/dolphin-slippi-tools.exe";
|
||||
std::string echoMsg = "echo Starting update process. If nothing happen after a few "
|
||||
std::string echo_msg =
|
||||
"echo Starting update process. If nothing happen after a few "
|
||||
"minutes, you may need to update manually from https://slippi.gg/netplay ...";
|
||||
// std::string command =
|
||||
// "start /b cmd /c " + echoMsg + " && \"" + path + "\" app-update -launch -iso \"" + isoPath + "\"";
|
||||
std::string command = "start /b cmd /c " + echoMsg + " && \"" + path + "\" app-update -launch -iso \"" + isoPath +
|
||||
"\" -version \"" + Common::scm_slippi_semver_str + "\"";
|
||||
// "start /b cmd /c " + echo_msg + " && \"" + path + "\" app-update -launch -iso \"" + isoPath
|
||||
// +
|
||||
// "\"";
|
||||
std::string command = "start /b cmd /c " + echo_msg + " && \"" + path +
|
||||
"\" app-update -launch -iso \"" + isoPath + "\" -version \"" +
|
||||
Common::scm_slippi_semver_str + "\"";
|
||||
WARN_LOG(SLIPPI, "Executing app update command: %s", command.c_str());
|
||||
RunSystemCommand(command);
|
||||
#elif defined(__APPLE__)
|
||||
CriticalAlertT("Automatic updates are not available for macOS; please get the latest update from slippi.gg/netplay.");
|
||||
CriticalAlertT("Automatic updates are not available for macOS; please get the latest update from "
|
||||
"slippi.gg/netplay.");
|
||||
#else
|
||||
const char* appimage_path = getenv("APPIMAGE");
|
||||
const char* appmount_path = getenv("APPDIR");
|
||||
|
@ -204,55 +212,56 @@ void SlippiUser::UpdateApp()
|
|||
std::string command = mount_path + "/usr/bin/appimageupdatetool " + path;
|
||||
WARN_LOG(SLIPPI, "Executing app update command: %s", command.c_str());
|
||||
RunSystemCommand(command);
|
||||
CriticalAlertT("Restart Dolphin to finish the update. If there was an issue, please head over to the Slippi "
|
||||
CriticalAlertT(
|
||||
"Restart Dolphin to finish the update. If there was an issue, please head over to the Slippi "
|
||||
"Discord for support.");
|
||||
#endif
|
||||
}
|
||||
|
||||
void SlippiUser::ListenForLogIn()
|
||||
{
|
||||
if (runThread)
|
||||
if (m_run_thread)
|
||||
return;
|
||||
|
||||
if (fileListenThread.joinable())
|
||||
fileListenThread.join();
|
||||
if (m_file_listen_thread.joinable())
|
||||
m_file_listen_thread.join();
|
||||
|
||||
runThread = true;
|
||||
fileListenThread = std::thread(&SlippiUser::FileListenThread, this);
|
||||
m_run_thread = true;
|
||||
m_file_listen_thread = std::thread(&SlippiUser::FileListenThread, this);
|
||||
}
|
||||
|
||||
void SlippiUser::LogOut()
|
||||
{
|
||||
runThread = false;
|
||||
m_run_thread = false;
|
||||
deleteFile();
|
||||
|
||||
UserInfo emptyUser;
|
||||
isLoggedIn = false;
|
||||
userInfo = emptyUser;
|
||||
UserInfo empty_user;
|
||||
m_is_logged_in = false;
|
||||
m_user_info = empty_user;
|
||||
}
|
||||
|
||||
void SlippiUser::OverwriteLatestVersion(std::string version)
|
||||
{
|
||||
userInfo.latestVersion = version;
|
||||
m_user_info.latest_version = version;
|
||||
}
|
||||
|
||||
SlippiUser::UserInfo SlippiUser::GetUserInfo()
|
||||
{
|
||||
return userInfo;
|
||||
return m_user_info;
|
||||
}
|
||||
|
||||
bool SlippiUser::IsLoggedIn()
|
||||
{
|
||||
return isLoggedIn;
|
||||
return m_is_logged_in;
|
||||
}
|
||||
|
||||
void SlippiUser::FileListenThread()
|
||||
{
|
||||
while (runThread)
|
||||
while (m_run_thread)
|
||||
{
|
||||
if (AttemptLogin())
|
||||
{
|
||||
runThread = false;
|
||||
m_run_thread = false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -266,13 +275,14 @@ void SlippiUser::FileListenThread()
|
|||
std::string SlippiUser::getUserFilePath()
|
||||
{
|
||||
#if defined(__APPLE__)
|
||||
std::string userFilePath = File::GetBundleDirectory() + "/Contents/Resources" + DIR_SEP + "user.json";
|
||||
std::string user_file_path =
|
||||
File::GetBundleDirectory() + "/Contents/Resources" + DIR_SEP + "user.json";
|
||||
#elif defined(_WIN32)
|
||||
std::string userFilePath = File::GetExeDirectory() + DIR_SEP + "user.json";
|
||||
std::string user_file_path = File::GetExeDirectory() + DIR_SEP + "user.json";
|
||||
#else
|
||||
std::string userFilePath = File::GetUserPath(F_USERJSON_IDX);
|
||||
std::string user_file_path = File::GetUserPath(F_USERJSON_IDX);
|
||||
#endif
|
||||
return userFilePath;
|
||||
return user_file_path;
|
||||
}
|
||||
|
||||
inline std::string readString(json obj, std::string key)
|
||||
|
@ -286,30 +296,30 @@ inline std::string readString(json obj, std::string key)
|
|||
return obj[key];
|
||||
}
|
||||
|
||||
SlippiUser::UserInfo SlippiUser::parseFile(std::string fileContents)
|
||||
SlippiUser::UserInfo SlippiUser::parseFile(std::string file_contents)
|
||||
{
|
||||
UserInfo info;
|
||||
info.fileContents = fileContents;
|
||||
info.file_contents = file_contents;
|
||||
|
||||
auto res = json::parse(fileContents, nullptr, false);
|
||||
auto res = json::parse(file_contents, nullptr, false);
|
||||
if (res.is_discarded() || !res.is_object())
|
||||
{
|
||||
return info;
|
||||
}
|
||||
|
||||
info.uid = readString(res, "uid");
|
||||
info.displayName = readString(res, "displayName");
|
||||
info.playKey = readString(res, "playKey");
|
||||
info.connectCode = readString(res, "connectCode");
|
||||
info.latestVersion = readString(res, "latestVersion");
|
||||
info.display_name = readString(res, "displayName");
|
||||
info.play_key = readString(res, "playKey");
|
||||
info.connect_code = readString(res, "connectCode");
|
||||
info.latest_version = readString(res, "latestVersion");
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
void SlippiUser::deleteFile()
|
||||
{
|
||||
std::string userFilePath = getUserFilePath();
|
||||
File::Delete(userFilePath);
|
||||
std::string user_file_path = getUserFilePath();
|
||||
File::Delete(user_file_path);
|
||||
}
|
||||
|
||||
void SlippiUser::overwriteFromServer()
|
||||
|
@ -319,7 +329,7 @@ void SlippiUser::overwriteFromServer()
|
|||
|
||||
// Perform curl request
|
||||
std::string resp;
|
||||
curl_easy_setopt(m_curl, CURLOPT_URL, (URL_START + "/" + userInfo.uid).c_str());
|
||||
curl_easy_setopt(m_curl, CURLOPT_URL, (URL_START + "/" + m_user_info.uid).c_str());
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &resp);
|
||||
CURLcode res = curl_easy_perform(m_curl);
|
||||
|
||||
|
@ -329,17 +339,17 @@ void SlippiUser::overwriteFromServer()
|
|||
return;
|
||||
}
|
||||
|
||||
long responseCode;
|
||||
curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &responseCode);
|
||||
if (responseCode != 200)
|
||||
long response_code;
|
||||
curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &response_code);
|
||||
if (response_code != 200)
|
||||
{
|
||||
ERROR_LOG(SLIPPI, "[User] Server responded with non-success status: %d", responseCode);
|
||||
ERROR_LOG(SLIPPI, "[User] Server responded with non-success status: %d", response_code);
|
||||
return;
|
||||
}
|
||||
|
||||
// Overwrite userInfo with data from server
|
||||
// Overwrite user info with data from server
|
||||
auto r = json::parse(resp);
|
||||
userInfo.connectCode = r.value("connectCode", userInfo.connectCode);
|
||||
userInfo.latestVersion = r.value("latestVersion", userInfo.latestVersion);
|
||||
userInfo.displayName = r.value("displayName", userInfo.displayName);
|
||||
m_user_info.connect_code = r.value("connectCode", m_user_info.connect_code);
|
||||
m_user_info.latest_version = r.value("latestVersion", m_user_info.latest_version);
|
||||
m_user_info.display_name = r.value("displayName", m_user_info.display_name);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include <atomic>
|
||||
#include <curl/curl.h>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
class SlippiUser
|
||||
{
|
||||
|
@ -14,11 +14,11 @@ public:
|
|||
struct UserInfo
|
||||
{
|
||||
std::string uid = "";
|
||||
std::string playKey = "";
|
||||
std::string displayName = "";
|
||||
std::string connectCode = "";
|
||||
std::string latestVersion = "";
|
||||
std::string fileContents = "";
|
||||
std::string play_key = "";
|
||||
std::string display_name = "";
|
||||
std::string connect_code = "";
|
||||
std::string latest_version = "";
|
||||
std::string file_contents = "";
|
||||
};
|
||||
|
||||
SlippiUser();
|
||||
|
@ -36,18 +36,18 @@ public:
|
|||
|
||||
protected:
|
||||
std::string getUserFilePath();
|
||||
UserInfo parseFile(std::string fileContents);
|
||||
UserInfo parseFile(std::string file_contents);
|
||||
void deleteFile();
|
||||
void overwriteFromServer();
|
||||
|
||||
UserInfo userInfo;
|
||||
bool isLoggedIn = false;
|
||||
UserInfo m_user_info;
|
||||
bool m_is_logged_in = false;
|
||||
|
||||
const std::string URL_START = "https://users-rest-dot-slippi.uc.r.appspot.com/user";
|
||||
CURL* m_curl = nullptr;
|
||||
struct curl_slist* m_curlHeaderList = nullptr;
|
||||
std::vector<char> receiveBuf;
|
||||
struct curl_slist* m_curl_header_list = nullptr;
|
||||
std::vector<char> m_receive_buf;
|
||||
|
||||
std::thread fileListenThread;
|
||||
std::atomic<bool> runThread;
|
||||
std::thread m_file_listen_thread;
|
||||
std::atomic<bool> m_run_thread;
|
||||
};
|
||||
|
|
|
@ -438,13 +438,13 @@ endif()
|
|||
|
||||
if(APPLE)
|
||||
include(BundleUtilities)
|
||||
set(BUNDLE_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Dolphin.app)
|
||||
set(BUNDLE_PATH "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Slippi_Dolphin.app")
|
||||
|
||||
# Ask for an application bundle.
|
||||
set_target_properties(dolphin-emu PROPERTIES
|
||||
MACOSX_BUNDLE true
|
||||
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in
|
||||
OUTPUT_NAME Dolphin
|
||||
OUTPUT_NAME "Slippi_Dolphin"
|
||||
)
|
||||
|
||||
# Copy qt.conf into the bundle
|
||||
|
|
|
@ -29,11 +29,15 @@
|
|||
</dict>
|
||||
</array>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Dolphin</string>
|
||||
<string>Slippi_Dolphin</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Slippi_Dolphin</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Slippi_Dolphin</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>Dolphin.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.dolphin-emu.dolphin</string>
|
||||
<string>com.project-slippi.dolphin</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
|
|
|
@ -101,10 +101,11 @@ void SlippiPane::CreateLayout()
|
|||
layout->addWidget(playback_settings);
|
||||
|
||||
auto* enable_playback_seek_checkbox = new QCheckBox(tr("Enable Seekbar"));
|
||||
char seekbarTooltip[] = "<html><head/><body><p>Enables video player style controls while watching Slippi replays. Uses more cpu resources and can be stuttery. " \
|
||||
"Space: Pause/Play " \
|
||||
"Left/Right: Jump 5 seconds back/forward" \
|
||||
"Shift + Left/Right: Jump 20 seconds back/forward" \
|
||||
char seekbarTooltip[] = "<html><head/><body><p>Enables video player style controls while "
|
||||
"watching Slippi replays. Uses more cpu resources and can be stuttery. "
|
||||
"Space: Pause/Play "
|
||||
"Left/Right: Jump 5 seconds back/forward"
|
||||
"Shift + Left/Right: Jump 20 seconds back/forward"
|
||||
"Period (while paused): Advance one frame";
|
||||
enable_playback_seek_checkbox->setToolTip(tr(seekbarTooltip));
|
||||
playback_settings_layout->addWidget(enable_playback_seek_checkbox);
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#!/bin/bash -e
|
||||
# build-online-appimage.sh
|
||||
# build-appimage.sh
|
||||
|
||||
ZSYNC_STRING="gh-releases-zsync|project-slippi|Ishiiruka|latest|Slippi_Online-x86_64.AppImage.zsync"
|
||||
NETPLAY_APPIMAGE_STRING="Slippi_Online-x86_64.AppImage"
|
||||
PLAYBACK_APPIMAGE_STRING="Slippi_Playback-x86_64.AppImage"
|
||||
|
||||
LINUXDEPLOY_PATH="https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous"
|
||||
|
@ -16,28 +17,33 @@ UPDATETOOL_PATH="https://github.com/AppImage/AppImageUpdate/releases/download/co
|
|||
UPDATETOOL_FILE="appimageupdatetool-x86_64.AppImage"
|
||||
UPDATETOOL_URL="${UPDATETOOL_PATH}/${UPDATETOOL_FILE}"
|
||||
|
||||
DESKTOP_APP_URL="https://github.com/project-slippi/slippi-desktop-app"
|
||||
DESKTOP_APP_SYS_PATH="./slippi-desktop-app/app/dolphin-dev/overwrite/Sys"
|
||||
PLAYBACK_CODES_PATH="./Data/PlaybackGeckoCodes/"
|
||||
|
||||
APPDIR_BIN="./AppDir/usr/bin"
|
||||
APPDIR_HOOKS="./AppDir/apprun-hooks"
|
||||
|
||||
# Grab various appimage binaries from GitHub if we don't have them
|
||||
if [ ! -e ./Tools/linuxdeploy ]; then
|
||||
wget ${LINUXDEPLOY_URL} -O ./Tools/linuxdeploy
|
||||
chmod +x ./Tools/linuxdeploy
|
||||
fi
|
||||
if [ ! -e ./Tools/linuxdeploy-update-plugin ]; then
|
||||
wget ${UPDATEPLUG_URL} -O ./Tools/linuxdeploy-update-plugin
|
||||
chmod +x ./Tools/linuxdeploy-update-plugin
|
||||
fi
|
||||
if [ ! -e ./Tools/appimageupdatetool ]; then
|
||||
wget ${UPDATEPLUG_URL} -O ./Tools/appimageupdatetool
|
||||
chmod +x ./Tools/appimageupdatetool
|
||||
wget ${UPDATETOOL_URL} -O ./Tools/appimageupdatetool
|
||||
fi
|
||||
|
||||
chmod +x ./Tools/linuxdeploy
|
||||
chmod +x ./Tools/linuxdeploy-update-plugin
|
||||
chmod +x ./Tools/appimageupdatetool
|
||||
|
||||
# Delete the AppDir folder to prevent build issues
|
||||
rm -rf ./AppDir/
|
||||
|
||||
# Add the linux-env script to the AppDir prior to running linuxdeploy
|
||||
mkdir -p ${APPDIR_HOOKS}
|
||||
cp Data/linux-env.sh ${APPDIR_HOOKS}
|
||||
|
||||
# Build the AppDir directory for this image
|
||||
mkdir -p AppDir
|
||||
./Tools/linuxdeploy \
|
||||
|
@ -50,32 +56,29 @@ mkdir -p AppDir
|
|||
cp -r Data/Sys ${APPDIR_BIN}
|
||||
|
||||
# Build type
|
||||
if [ -z "$1" ] # Netplay
|
||||
if [ "$1" == "playback" ] # Playback
|
||||
then
|
||||
echo "Using Playback build config"
|
||||
|
||||
rm -f ${PLAYBACK_APPIMAGE_STRING}
|
||||
|
||||
# Update Sys dir with playback codes
|
||||
echo "Copying Playback gecko codes"
|
||||
rm -rf "${APPDIR_BIN}/Sys/GameSettings" # Delete netplay codes
|
||||
cp -r "${PLAYBACK_CODES_PATH}/." "${APPDIR_BIN}/Sys/GameSettings/"
|
||||
|
||||
OUTPUT="${PLAYBACK_APPIMAGE_STRING}" \
|
||||
./Tools/linuxdeploy-update-plugin --appdir=./AppDir/
|
||||
else
|
||||
echo "Using Netplay build config"
|
||||
|
||||
# remove existing appimage just in case
|
||||
rm -f ${NETPLAY_APPIMAGE_STRING}
|
||||
|
||||
# Package up the update tool within the AppImage
|
||||
cp ./Tools/appimageupdatetool ./AppDir/usr/bin/
|
||||
|
||||
# Bake an AppImage with the update metadata
|
||||
UPDATE_INFORMATION="${ZSYNC_STRING}" \
|
||||
./Tools/linuxdeploy-update-plugin --appdir=./AppDir/
|
||||
elif [ "$1" == "playback" ] # Playback
|
||||
then
|
||||
echo "Using Playback build config"
|
||||
if [ -d "slippi-desktop-app" ]
|
||||
then
|
||||
pushd slippi-desktop-app
|
||||
git checkout master
|
||||
git pull --ff-only
|
||||
popd
|
||||
else
|
||||
git clone ${DESKTOP_APP_URL}
|
||||
fi
|
||||
# Update Sys dir with playback codes
|
||||
rm -rf "${APPDIR_BIN}/GameSettings" # Delete netplay codes
|
||||
cp -r ${DESKTOP_APP_SYS_PATH} ${APPDIR_BIN}
|
||||
|
||||
OUTPUT="${PLAYBACK_APPIMAGE_STRING}" \
|
||||
./Tools/linuxdeploy-update-plugin --appdir=./AppDir/
|
||||
fi
|
||||
|
|
|
@ -3,15 +3,19 @@
|
|||
|
||||
CMAKE_FLAGS='-DLINUX_LOCAL_DEV=true'
|
||||
|
||||
PLAYBACK_CODES_PATH="./Data/PlaybackGeckoCodes/"
|
||||
|
||||
DATA_SYS_PATH="./Data/Sys/"
|
||||
BINARY_PATH="./build/Binaries/"
|
||||
|
||||
# Build type
|
||||
if [ -z "$1" ]
|
||||
if [ "$1" == "playback" ]
|
||||
then
|
||||
echo "Using Playback build config"
|
||||
else
|
||||
# TODO: move this around, playback should be the secondary build
|
||||
CMAKE_FLAGS+=" -DSLIPPI_PLAYBACK=false"
|
||||
echo "Using Netplay build config"
|
||||
elif [ "$1" == "playback" ]
|
||||
then
|
||||
echo "Using Playback build config"
|
||||
fi
|
||||
|
||||
# Move into the build directory, run CMake, and compile the project
|
||||
|
@ -22,6 +26,15 @@ make -j$(nproc)
|
|||
popd
|
||||
|
||||
# Copy the Sys folder in
|
||||
cp -r -n Data/Sys/ build/Binaries/
|
||||
cp -r -n ${DATA_SYS_PATH} ${BINARY_PATH}
|
||||
|
||||
touch ./build/Binaries/portable.txt
|
||||
|
||||
# Copy playback specific codes if needed
|
||||
if [ "$1" == "playback" ]
|
||||
then
|
||||
# Update Sys dir with playback codes
|
||||
echo "Copying Playback gecko codes"
|
||||
rm -rf "${BINARY_PATH}/Sys/GameSettings" # Delete netplay codes
|
||||
cp -r "${PLAYBACK_CODES_PATH}/." "${BINARY_PATH}/Sys/GameSettings/"
|
||||
fi
|
49
build-mac.sh
Normal file
49
build-mac.sh
Normal file
|
@ -0,0 +1,49 @@
|
|||
#!/bin/bash -e
|
||||
# build-mac.sh
|
||||
|
||||
QT_BREW_PATH=$(brew --prefix qt@5)
|
||||
CMAKE_FLAGS="-DQt5_DIR=${QT_BREW_PATH}/lib/cmake/Qt5 -DENABLE_NOGUI=false"
|
||||
|
||||
PLAYBACK_CODES_PATH="./Data/PlaybackGeckoCodes/"
|
||||
|
||||
DATA_SYS_PATH="./Data/Sys/"
|
||||
BINARY_PATH="./build/Binaries/Slippi_Dolphin.app/Contents/Resources/"
|
||||
|
||||
export LIBRARY_PATH=$LIBRARY_PATH:/usr/local/lib:/usr/lib/
|
||||
|
||||
# Build type
|
||||
if [ "$1" == "playback" ]
|
||||
then
|
||||
echo "Using Playback build config"
|
||||
else
|
||||
echo "Using Netplay build config"
|
||||
CMAKE_FLAGS+=" -DSLIPPI_PLAYBACK=false"
|
||||
fi
|
||||
|
||||
if [[ -z "${CERTIFICATE_MACOS_APPLICATION}" ]]
|
||||
then
|
||||
echo "Building without code signing"
|
||||
else
|
||||
echo "Building with code signing"
|
||||
CMAKE_FLAGS+=' -DMACOS_CODE_SIGNING="ON"'
|
||||
fi
|
||||
|
||||
# Move into the build directory, run CMake, and compile the project
|
||||
mkdir -p build
|
||||
pushd build
|
||||
cmake ${CMAKE_FLAGS} ..
|
||||
make -j7
|
||||
popd
|
||||
|
||||
# Copy the Sys folder in
|
||||
echo "Copying Sys files into the bundle"
|
||||
cp -Rfn "${DATA_SYS_PATH}" "${BINARY_PATH}"
|
||||
|
||||
# Copy playback specific codes if needed
|
||||
if [ "$1" == "playback" ]
|
||||
then
|
||||
# Update Sys dir with playback codes
|
||||
echo "Copying playback gecko codes into the bundle"
|
||||
rm -rf "${BINARY_PATH}/Sys/GameSettings" # Delete netplay codes
|
||||
cp -r "${PLAYBACK_CODES_PATH}/." "${BINARY_PATH}/Sys/GameSettings/"
|
||||
fi
|
Loading…
Add table
Add a link
Reference in a new issue