Commit b3a41aed authored by nanahira's avatar nanahira

Merge branch 'server-develop' into server

parents 55f4fe4a 5178a0a6
......@@ -6,6 +6,7 @@ source .ci/asset-branch
apt update && apt -y install wget git libarchive-tools sqlite3
git clone --depth=1 -b "$ASSET_BRANCH_NAME" https://code.moenext.com/mycard/ygopro-database
cp -rf ./ygopro-database/locales/$TARGET_LOCALE/strings.conf .
cp -rf ./ygopro-database/locales/$TARGET_LOCALE/servers.conf .
rm -f cards.cdb
sqlite3 ./ygopro-database/locales/$TARGET_LOCALE/cards.cdb .dump | sqlite3 cards.cdb
# ygopro-images
......
#!/bin/sh
set -x
set -o errexit
if [ -n "$NO_AUDIO" ]; then
echo "Skipping opus build because NO_AUDIO is set"
exit 0
fi
cd miniaudio
external_built_dir="$PWD/external-built"
is_macos=false
if [ "$(uname)" = "Darwin" ]; then
is_macos=true
fi
maybe_patch_configure() {
if $is_macos; then
sed -i.bak 's/-force_cpusubtype_ALL//g' configure*
fi
}
build_single_thing() {
lib_name="$1"
cd "external/$lib_name"
shift
maybe_patch_configure
PKG_CONFIG_PATH="$external_built_dir/lib/pkgconfig" CFLAGS="$OPUS_FLAGS" CXXFLAGS="$OPUS_FLAGS" ./configure --prefix="$external_built_dir" --enable-static=yes --enable-shared=no "$@"
make -j$(nproc)
make install
cd ../..
}
build_single_thing ogg
build_single_thing opus
build_single_thing opusfile --disable-examples --disable-http
build_single_thing vorbis --with-ogg="$external_built_dir"
cd ..
#!/bin/bash
libs_to_configure=(
"ogg"
# "opus"
)
cd miniaudio/external
for lib in "${libs_to_configure[@]}"; do
cd $lib
./configure
cd ..
done
#!/bin/sh
set -x
set -o errexit
cd event
./configure --disable-openssl --enable-static=yes --enable-shared=no
sed -f make-event-config.sed < config.h > ./include/event2/event-config.h
cd ..
......@@ -4,30 +4,19 @@ set -o errexit
TARGET_PLATFORM=$(arch)
TARGET_YGOPRO_BINARY_PATH=./ygopro-platforms/ygopro-platform-$TARGET_PLATFORM
export EVENT_INCLUDE_DIR=$PWD/libevent-stable/include
export EVENT_LIB_DIR=$PWD/libevent-stable/lib
export OPUS_INCLUDE_DIR=$PWD/miniaudio/external-built/include/opus
export OPUS_LIB_DIR=$PWD/miniaudio/external-built/lib
export OPUSFILE_INCLUDE_DIR=$PWD/miniaudio/external-built/include/opus
export OPUSFILE_LIB_DIR=$PWD/miniaudio/external-built/lib
export VORBIS_INCLUDE_DIR=$PWD/miniaudio/external-built/include
export VORBIS_LIB_DIR=$PWD/miniaudio/external-built/lib
export OGG_INCLUDE_DIR=$PWD/miniaudio/external-built/include
export OGG_LIB_DIR=$PWD/miniaudio/external-built/lib
export ACLOCAL=aclocal
export AUTOMAKE=automake
./.ci/libevent-prebuild.sh
./.ci/build-opus.sh
./premake5 gmake --cc=clang --build-freetype --build-sqlite
./.ci/configure-audio.sh
rm -rf sqlite3/VERSION sqlite3/version
./premake5 gmake --cc=clang
cd build
make config=release -j$(sysctl -n hw.ncpu)
cd ..
mkdir ygopro-platforms
mv bin/release/YGOPro.app $TARGET_YGOPRO_BINARY_PATH
mv bin/release/YGOPro $TARGET_YGOPRO_BINARY_PATH
#if [[ $TARGET_PLATFORM == "x86" ]]; then
# install_name_tool -change /usr/local/lib/libirrklang.dylib @executable_path/../Frameworks/libirrklang.dylib $TARGET_YGOPRO_BINARY_PATH
......
#!/bin/sh
set -x
set -o errexit
# PROCESSOR_COUNT=4
if [ -d "libevent-stable" ]; then
rm -rf libevent-stable
fi
if [ ! -d "libevent-2.0.22-stable" ]; then
wget -O - https://cdn02.moecube.com:444/ygopro-build-materials/libevent-2.0.22-stable.tar.gz | tar zfx -
fi
install_path="$PWD/libevent-stable"
cd libevent-2.0.22-stable
./configure "--prefix=$install_path" --disable-openssl --enable-static=yes --enable-shared=no "$@"
make -j$(nproc)
make install
cd ..
......@@ -2,7 +2,7 @@
set -x
set -o errexit
ARCHIVE_FILES=(ygopro cards.cdb locales fonts sound textures strings.conf system.conf pack)
ARCHIVE_FILES=(ygopro cards.cdb locales fonts sound textures strings.conf system.conf servers.conf pack)
if [[ -z "$TARGET_PLATFORM" ]]; then
TARGET_PLATFORM=linux
......
......@@ -2,7 +2,7 @@
set -x
set -o errexit
ARCHIVE_FILES=(ygopro LICENSE README.md lflist.conf strings.conf system.conf cards.cdb script textures deck single pics replay windbot bot bot.conf locales fonts pack)
ARCHIVE_FILES=(ygopro LICENSE README.md lflist.conf strings.conf system.conf servers.conf cards.cdb script textures deck single pics replay windbot bot bot.conf locales fonts pack)
# TARGET_LOCALE
# ARCHIVE_SUFFIX
......
......@@ -2,7 +2,7 @@
set -x
set -o errexit
ARCHIVE_FILES=(ygopro.app cards.cdb locales fonts sound textures strings.conf system.conf pack)
ARCHIVE_FILES=(ygopro.app cards.cdb locales fonts sound textures strings.conf system.conf servers.conf pack)
if [[ -z "$TARGET_PLATFORM" ]]; then
TARGET_PLATFORM=darwin
......
......@@ -2,7 +2,7 @@
set -x
set -o errexit
ARCHIVE_FILES=(ygopro.app LICENSE README.md lflist.conf strings.conf system.conf cards.cdb script textures deck single pics replay sound windbot bot bot.conf locales fonts pack)
ARCHIVE_FILES=(ygopro.app LICENSE README.md lflist.conf strings.conf system.conf servers.conf cards.cdb script textures deck single pics replay sound windbot bot bot.conf locales fonts pack)
# TARGET_LOCALE
# ARCHIVE_SUFFIX
......
......@@ -2,7 +2,7 @@
set -x
set -o errexit
ARCHIVE_FILES=(ygopro.exe LICENSE README.md lflist.conf strings.conf system.conf cards.cdb script textures deck single pics replay sound bot.conf Bot.exe WindBot locales fonts skin pack)
ARCHIVE_FILES=(ygopro.exe vcomp140.dll LICENSE README.md lflist.conf strings.conf system.conf servers.conf cards.cdb script textures deck single pics replay sound bot.conf Bot.exe WindBot locales fonts skin pack)
if [[ "$TARGET_LOCALE" == "zh-CN" ]]; then
ARCHIVE_FILES=("${ARCHIVE_FILES[@]}" update-koishipro)
......
......@@ -2,7 +2,7 @@
set -x
set -o errexit
ARCHIVE_FILES=(ygopro.exe cards.cdb locales fonts sound textures strings.conf system.conf skin pack)
ARCHIVE_FILES=(ygopro.exe vcomp140.dll cards.cdb locales fonts sound textures strings.conf system.conf servers.conf skin pack)
if [[ -z "$TARGET_PLATFORM" ]]; then
TARGET_PLATFORM=win32
......
......@@ -2,7 +2,7 @@
set -x
set -o errexit
ARCHIVE_FILES=(ygopro.exe LICENSE README.md lflist.conf strings.conf system.conf cards.cdb script textures deck single pics replay sound bot.conf Bot.exe WindBot locales fonts skin pack)
ARCHIVE_FILES=(ygopro.exe vcomp140.dll LICENSE README.md lflist.conf strings.conf system.conf servers.conf cards.cdb script textures deck single pics replay sound bot.conf Bot.exe WindBot locales fonts skin pack)
if [[ "$TARGET_LOCALE" == "zh-CN" && "$ARCHIVE_SUFFIX" != "zst" ]]; then
ARCHIVE_FILES=("${ARCHIVE_FILES[@]}" update-koishipro)
......
#!/bin/bash
set -x
set -o errexit
source .ci/asset-branch
IRRLICHT_REPO_URL="https://code.moenext.com/mycard/irrlicht-new.git"
IRRLICHT_BRANCH_NAME="$ASSET_BRANCH_NAME"
if [ ! -d "irrlicht" ]; then
git clone --depth=1 --branch "$IRRLICHT_BRANCH_NAME" "$IRRLICHT_REPO_URL" irrlicht
else
cd irrlicht
git fetch origin "$IRRLICHT_BRANCH_NAME"
git checkout "$IRRLICHT_BRANCH_NAME"
git reset --hard origin/"$IRRLICHT_BRANCH_NAME"
cd ..
fi
source .ci/prepare-repo
prepare_repo "https://code.moenext.com/mycard/irrlicht-new.git" "irrlicht"
#!/bin/sh
#!/bin/bash
set -x
set -o errexit
if [ ! -d "miniaudio" ]; then
git clone --depth=1 --branch 0.11.22 https://github.com/mackron/miniaudio
fi
source .ci/prepare-repo
prepare_repo "https://code.moenext.com/mycard/miniaudio.git" "miniaudio"
cp -rf miniaudio/extras/miniaudio_split/miniaudio.* miniaudio/
......@@ -20,7 +19,7 @@ install_external() {
fi
}
install_external "ogg" "https://github.com/xiph/ogg/releases/download/v1.3.5/libogg-1.3.5.tar.gz"
install_external "opus" "https://github.com/xiph/opus/releases/download/v1.5.2/opus-1.5.2.tar.gz"
install_external "opusfile" "https://github.com/xiph/opusfile/releases/download/v0.12/opusfile-0.12.tar.gz"
install_external "vorbis" "https://github.com/xiph/vorbis/releases/download/v1.3.7/libvorbis-1.3.7.tar.gz"
install_external "ogg" "https://mat-cacher.moenext.com/https://github.com/xiph/ogg/releases/download/v1.3.5/libogg-1.3.5.tar.gz"
install_external "opus" "https://mat-cacher.moenext.com/https://github.com/xiph/opus/releases/download/v1.5.2/opus-1.5.2.tar.gz"
install_external "opusfile" "https://mat-cacher.moenext.com/https://github.com/xiph/opusfile/releases/download/v0.12/opusfile-0.12.tar.gz"
install_external "vorbis" "https://mat-cacher.moenext.com/https://github.com/xiph/vorbis/releases/download/v1.3.7/libvorbis-1.3.7.tar.gz"
source .ci/asset-branch
BRANCH_NAME="$ASSET_BRANCH_NAME"
prepare_repo() {
REPO_URL="$1"
TARGET_DIR_NAME="$2"
if [ ! -d "$TARGET_DIR_NAME" ]; then
echo "Cloning repository $REPO_URL branch $BRANCH_NAME into $TARGET_DIR_NAME"
git clone --depth=1 --branch "$BRANCH_NAME" "$REPO_URL" "$TARGET_DIR_NAME"
else
echo "Repository $REPO_URL already exists in $TARGET_DIR_NAME, updating to $BRANCH_NAME..."
cd "$TARGET_DIR_NAME"
git remote set-url origin "$REPO_URL"
git fetch origin "$BRANCH_NAME"
git checkout "$BRANCH_NAME"
git reset --hard origin/"$BRANCH_NAME"
cd ..
fi
}
This diff is collapsed.
name: Automated Test Build (Server Mode)
on:
push:
branches: [ "server" ]
pull_request:
branches: [ "server" ]
jobs:
build-windows:
strategy:
fail-fast: false
matrix:
name:
- windows
- windows-x64
- windows-ygopro2-ai-server
# - windows-2025
include:
- name: windows
os: windows-2022
vs: vs2022
- name: windows-x64
os: windows-2022
vs: vs2022
x64: true
# - name: windows-2025
# os: windows-2025
# vs: vs2025 # to be enabled after the release of Visual Studio 2025
- name: windows-ygopro2-ai-server
os: windows-2022
vs: vs2022
x64: true
ygopro2: true
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository with submodules
uses: actions/checkout@v4
with:
fetch-depth: 1
submodules: true
- name: Update submodules
run: |
cd ocgcore
git checkout master
git pull origin master
cd ..
# cd script
# git checkout master
# git pull origin master
# cd ..
- name: Download premake
id: premake
uses: mercury233/action-cache-download-file@v1.0.0
with:
url: https://github.com/premake/premake-core/releases/download/v5.0.0-beta6/premake-5.0.0-beta6-windows.zip
filename: premake5.zip
- name: Extract premake
run: |
7z x ${{ steps.premake.outputs.filepath }}
- name: Download libevent
id: libevent
uses: mercury233/action-cache-download-file@v1.0.0
with:
url: https://github.com/libevent/libevent/releases/download/release-2.0.22-stable/libevent-2.0.22-stable.tar.gz
filename: libevent.tar.gz
- name: Extract libevent
run: |
tar xf ${{ steps.libevent.outputs.filepath }}
move libevent-2.0.22-stable event
- name: Download lua
id: lua
uses: mercury233/action-cache-download-file@v1.0.0
with:
url: https://www.lua.org/ftp/lua-5.4.7.tar.gz
- name: Extract lua
run: |
tar xf ${{ steps.lua.outputs.filepath }}
move lua-5.4.7 lua
- name: Download sqlite
id: sqlite
uses: mercury233/action-cache-download-file@v1.0.0
with:
url: https://www.sqlite.org/2025/sqlite-amalgamation-3490100.zip
- name: Extract sqlite
run: |
7z x ${{ steps.sqlite.outputs.filepath }}
move sqlite-amalgamation-3490100 sqlite3
- name: Download irrlicht
if: matrix.ygopro2 == true
run: |
git clone --depth=1 https://github.com/mercury233/irrlicht
- name: Copy premake files
run: |
xcopy /E premake\* .
xcopy /E resource\* .
- name: Use premake to generate Visual Studio solution
run: |
.\premake5.exe ${{ matrix.vs }} ${{ matrix.ygopro2 && '--server-pro2-support' || '' }}
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v2
- name: Build solution
run: |
MSBuild.exe build\YGOPro.sln /m /p:Configuration=Release /p:Platform=${{ matrix.x64 && 'x64' || 'Win32' }}
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: YGOPro-Server-${{ matrix.name }}
path: |
bin/release/x86/ygopro.exe
bin/release/x64/ygopro.exe
bin/release/x64/AI.Server.exe
build-linux:
strategy:
fail-fast: false
matrix:
name:
- ubuntu-22
- ubuntu-24
- ubuntu-static-link
- ubuntu-zip-support
include:
- name: ubuntu-22
os: ubuntu-22.04
premake-version: 5.0.0-beta4
- name: ubuntu-24
os: ubuntu-24.04
premake-version: 5.0.0-beta6
- name: ubuntu-static-link
os: ubuntu-22.04
premake-version: 5.0.0-beta4
static-link: true
- name: ubuntu-zip-support
os: ubuntu-22.04
premake-version: 5.0.0-beta4
zip-support: true
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository with submodules
uses: actions/checkout@v4
with:
fetch-depth: 1
submodules: true
- name: Update submodules
run: |
cd ocgcore
git checkout master
git pull origin master
cd ..
# cd script
# git checkout master
# git pull origin master
# cd ..
- name: Install dependencies
if: matrix.static-link != true
run: |
sudo apt-get update
sudo apt-get install -y libevent-dev libsqlite3-dev
- name: Download premake
id: premake
uses: mercury233/action-cache-download-file@v1.0.0
with:
url: https://github.com/premake/premake-core/releases/download/v${{ matrix.premake-version }}/premake-${{ matrix.premake-version }}-linux.tar.gz
filename: premake5.tar.gz
- name: Extract premake
run: |
tar xf ${{ steps.premake.outputs.filepath }}
chmod +x ./premake5
- name: Download libevent
if: matrix.static-link == true
id: libevent
uses: mercury233/action-cache-download-file@v1.0.0
with:
url: https://github.com/libevent/libevent/releases/download/release-2.1.12-stable/libevent-2.1.12-stable.tar.gz
filename: libevent.tar.gz
- name: Extract libevent
if: matrix.static-link == true
run: |
tar xf ${{ steps.libevent.outputs.filepath }}
mv libevent-2.1.12-stable event
- name: Configure libevent
if: matrix.static-link == true
run: |
cd event
./configure --disable-openssl --enable-static=yes --enable-shared=no
sed -f make-event-config.sed < config.h > ./include/event2/event-config.h
cd ..
- name: Download lua
id: lua
uses: mercury233/action-cache-download-file@v1.0.0
with:
url: https://www.lua.org/ftp/lua-5.4.7.tar.gz
- name: Extract lua
run: |
tar xf ${{ steps.lua.outputs.filepath }}
mv lua-5.4.7 lua
- name: Download sqlite
if: matrix.static-link == true
id: sqlite
uses: mercury233/action-cache-download-file@v1.0.0
with:
url: https://www.sqlite.org/2025/sqlite-amalgamation-3490100.zip
- name: Extract sqlite
if: matrix.static-link == true
run: |
7z x ${{ steps.sqlite.outputs.filepath }}
mv sqlite-amalgamation-3490100 sqlite3
- name: Download irrlicht
if: matrix.zip-support == true
run: |
git clone --depth=1 https://github.com/mercury233/irrlicht
- name: Copy premake files
run: |
cp -r premake/* .
cp -r resource/* .
- name: Use premake to generate make files
run: |
./premake5 gmake ${{ matrix.zip-support && '--server-zip-support' || '' }} \
${{ matrix.static-link && '--build-sqlite --build-event' || '' }}
- name: Make
run: |
cd build
make -j 4 config=release
cd ..
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: YGOPro-Server-${{ matrix.name }}
path: |
bin/release/ygopro
publish:
needs: [build-windows]
if: github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
pattern: YGOPro-Server-windows-* # the "windows" (no hyphen) artifact is ignored
merge-multiple: true
- name: GitHub Release
uses: salix5/action-automatic-releases@node20
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
automatic_release_tag: "server-latest"
prerelease: true
title: "Development Build (Server Mode)"
files: |
x64/ygopro.exe
x64/AI.Server.exe
\ No newline at end of file
......@@ -14,14 +14,17 @@ mat_common:
- linux
script:
# lua
- wget -O - https://cdn02.moecube.com:444/ygopro-build-materials/lua-5.4.4.tar.gz | tar zfx -
- mv lua-5.4.4 lua
- wget -O - https://mat-cacher.moenext.com/https://www.lua.org/ftp/lua-5.4.8.tar.gz | tar zfx -
- mv lua-5.4.8 lua
# sqlite3
- wget -O - https://cdn02.moecube.com:444/ygopro-build-materials/sqlite-autoconf-3390300.tar.gz | tar zfx -
- mv sqlite-autoconf-3390300 sqlite3
- wget -O - https://mat-cacher.moenext.com/https://www.sqlite.org/2025/sqlite-autoconf-3500100.tar.gz | tar zfx -
- mv sqlite-autoconf-3500100 sqlite3
# freetype
#- wget -O - https://cdn02.moecube.com:444/ygopro-build-materials/freetype-2.11.1.tar.gz | tar zfx -
#- mv freetype-2.11.1 freetype
# - wget -O - https://mat-cacher.moenext.com/https://downloads.sourceforge.net/freetype/freetype-2.13.3.tar.gz | tar zfx -
# - mv freetype-2.13.3 freetype
# event
- wget -O - https://mat-cacher.moenext.com/https://github.com/libevent/libevent/releases/download/release-2.1.12-stable/libevent-2.1.12-stable.tar.gz | tar zfx -
- mv libevent-2.1.12-stable event
# irrlicht
- ./.ci/prepare-irrlicht.sh
# miniaudio
......@@ -33,6 +36,7 @@ mat_common:
- lua
#- freetype
- sqlite3
- event
- irrlicht
#- miniaudio
......@@ -68,7 +72,7 @@ mat_macos:
- linux
script:
- apt update; apt -y install wget tar
- wget -O - https://cdn02.moecube.com:444/ygopro-build-materials/premake-5.0.0-beta5-macosx.tar.gz | tar zfx -
- wget -O - https://cdn02.moecube.com:444/premake5-built/premake-5.0.0-beta7-macosx.tar.gz | tar zfx -
- chmod +x premake5
artifacts:
paths:
......@@ -81,25 +85,21 @@ mat_windows:
script:
- apt update; apt -y install wget tar patch p7zip-full
# premake5.exe
- wget https://cdn02.moecube.com:444/ygopro-build-materials/premake-5.0.0-beta5-windows.zip
- 7z x -y premake-5.0.0-beta5-windows.zip
# event
- wget -O - https://cdn02.moecube.com:444/ygopro-build-materials/libevent-2.0.22-stable.tar.gz | tar zfx -
- mv libevent-2.0.22-stable event
- wget https://cdn02.moecube.com:444/premake5-built/premake-5.0.0-beta7-windows.zip
- 7z x -y premake-5.0.0-beta7-windows.zip
artifacts:
paths:
- premake5.exe
- event
._exec_build:
stage: build
#variables:
# NO_LUA_SAFE: '1' # on client no lua safe
cache:
key: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
paths:
- bin/
- obj/
#cache:
# key: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
# paths:
# - bin/
# - obj/
.exec_windows:
extends: ._exec_build
......@@ -119,19 +119,19 @@ exec_windows:
extends: .exec_windows
script:
- '.\premake5.exe vs2019 --server-zip-support'
- cmd /c '"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\msbuild.exe" build\YGOPro.sln /m /p:Configuration=Release'
- cmd /c '"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\msbuild.exe" build\YGOPro.sln /m /p:Configuration=Release /p:Platform=x64'
- mkdir dist
- mkdir dist\windows
- copy bin\release\ygopro.exe dist\windows\ygopro.exe
- copy bin\release\x64\ygopro.exe dist\windows\ygopro.exe
exec_windows_pro3:
extends: .exec_windows
script:
- '.\premake5.exe vs2019 --server-pro3-support --log-in-chat'
- cmd /c '"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\msbuild.exe" build\YGOPro.sln /m /p:Configuration=Release'
- cmd /c '"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\msbuild.exe" build\YGOPro.sln /m /p:Configuration=Release /p:Platform=x64'
- mkdir dist
- mkdir dist\windows
- copy bin\release\ygoserver.dll dist\windows\ygoserver.dll
- copy bin\release\x64\ygoserver.dll dist\windows\ygoserver.dll
.exec_unix_common:
extends: ._exec_build
......@@ -140,6 +140,7 @@ exec_windows_pro3:
TARGET_FILE: ygopro
PREMAKE5_BIN: premake5
script:
- ./.ci/configure-libevent.sh
- $PREMAKE5_BIN gmake
- cd build
- make config=release -j$(nproc)
......@@ -169,8 +170,6 @@ exec_windows_pro3:
variables:
BUILD_SQLITE: '1'
SERVER_ZIP_SUPPORT: '1'
EVENT_INCLUDE_DIR: /usr/share/libevent-stable/include
EVENT_LIB_DIR: /usr/share/libevent-stable/lib
RELEASE_DIR: linux-x64
.exec_debian:
......@@ -182,31 +181,35 @@ exec_windows_pro3:
- apt update; apt -y install git build-essential liblua5.4-dev libsqlite3-dev libevent-dev
.use_arm:
image: git-registry.moenext.com/mycard/docker-ygopro-builder:fpic
tags:
- arm
.use_pro3:
image: git-registry.moenext.com/mycard/docker-ygopro-builder:fpic
variables:
SERVER_PRO3_SUPPORT: '1'
LOG_IN_CHAT: '1'
.use_pro3_linux:
extends: .use_pro3
variables:
TARGET_FILE: libygoserver.so
.use_pro3_macos:
extends: .use_pro3
variables:
SERVER_PRO3_SUPPORT: '1'
LOG_IN_CHAT: '1'
TARGET_FILE: libygoserver.dylib
LIBEVENT_PREBUILD_FLAGS: '-fPIC'
exec_linux:
extends: .exec_linux
tags:
- noavx2
exec_linux_pro3:
extends:
- .exec_linux
- .use_pro3
- .use_pro3_linux
tags:
- avx2
exec_debian:
extends: .exec_debian
......@@ -222,7 +225,7 @@ exec_linuxarm_pro3:
extends:
- .exec_linux
- .use_arm
- .use_pro3
- .use_pro3_linux
variables:
RELEASE_DIR: linux-arm64
......@@ -240,14 +243,13 @@ exec_debianarm:
- mat_common
- mat_macos
- mat_submodules
before_script:
- env CFLAGS=$LIBEVENT_PREBUILD_FLAGS CXXFLAGS=$LIBEVENT_PREBUILD_FLAGS ./.ci/libevent-prebuild.sh
variables:
PREMAKE5_BIN: ./premake5
BUILD_SQLITE: '1'
BUILD_EVENT: '1'
SERVER_ZIP_SUPPORT: '1'
EVENT_INCLUDE_DIR: ../libevent-stable/include
EVENT_LIB_DIR: ../libevent-stable/lib
before_script:
- rm -rf sqlite3/VERSION sqlite3/version
exec_macos_x64:
extends: .exec_macos_platform
......
language: cpp
dist: bionic
git:
submodules: false
addons:
ssh_known_hosts:
- github.com
apt:
# sources:
# - ubuntu-toolchain-r-test
packages:
# - gcc-6
# - g++-6
- libevent-dev
- libsqlite3-dev
- liblua5.3-dev
env:
- DATABASE_FILE=cards.cdb
before_install:
- git submodule update --init --recursive
#- sudo ln -s /usr/bin/gcc-6 /usr/local/bin/gcc
#- sudo ln -s /usr/bin/g++-6 /usr/local/bin/g++
#- g++ --version
- wget -O - https://github.com/premake/premake-core/releases/download/v5.0.0-beta1/premake-5.0.0-beta1-linux.tar.gz | tar zfx -
#- wget -O - https://www.lua.org/ftp/lua-5.3.6.tar.gz | tar zfx -; cd lua-5.3.6; sudo make linux install; cd ..
script:
- ./premake5 gmake
- cd build
- make config=release
- cd ..
- mv -f ./bin/release/ygopro .
- strip ygopro
- mkdir replay
- echo "select id from datas;" | sqlite3 $DATABASE_FILE | xargs -I {} ./ygopro {} 2>&1 | tee ./redtext.txt
- bash -c "exit $(cat ./redtext.txt | wc -l)"
version: '{build}'
image: Visual Studio 2019
image: Visual Studio 2022
skip_tags: true
......@@ -7,37 +7,37 @@ install:
- git submodule update --init --recursive
# environment and system dependency
- bash -c "curl --retry 5 --connect-timeout 30 --location --remote-header-name --remote-name https://github.com/premake/premake-core/releases/download/v5.0.0-beta5/premake-5.0.0-beta5-windows.zip ; exit 0"
- 7z x premake-5.0.0-beta5-windows.zip
- bash -c "curl --retry 5 --connect-timeout 30 --location --remote-header-name --remote-name https://github.com/premake/premake-core/releases/download/v5.0.0-beta6/premake-5.0.0-beta6-windows.zip ; exit 0"
- 7z x premake-5.0.0-beta6-windows.zip
- bash -c "curl --retry 5 --connect-timeout 30 --location --remote-header-name --remote-name https://github.com/libevent/libevent/releases/download/release-2.0.22-stable/libevent-2.0.22-stable.tar.gz ; exit 0"
- tar xf libevent-2.0.22-stable.tar.gz
- move libevent-2.0.22-stable event
- xcopy /E event\WIN32-Code event\include
- bash -c "curl --retry 5 --connect-timeout 30 --location --remote-header-name --remote-name https://github.com/libevent/libevent/releases/download/release-2.1.12-stable/libevent-2.1.12-stable.tar.gz ; exit 0"
- tar xf libevent-2.1.12-stable.tar.gz
- move libevent-2.1.12-stable event
- bash -c "curl --retry 5 --connect-timeout 30 --location --remote-header-name --remote-name https://www.lua.org/ftp/lua-5.4.4.tar.gz ; exit 0"
- tar xf lua-5.4.4.tar.gz
- move lua-5.4.4 lua
- bash -c "curl --retry 5 --connect-timeout 30 --location --remote-header-name --remote-name https://www.lua.org/ftp/lua-5.4.7.tar.gz ; exit 0"
- tar xf lua-5.4.7.tar.gz
- move lua-5.4.7 lua
- bash -c "curl --retry 5 --connect-timeout 30 --location --remote-header-name --remote-name https://www.sqlite.org/2024/sqlite-amalgamation-3470000.zip ; exit 0"
- 7z x sqlite-amalgamation-3470000.zip
- move sqlite-amalgamation-3470000 sqlite3
- bash -c "curl --retry 5 --connect-timeout 30 --location --remote-header-name --remote-name https://www.sqlite.org/2025/sqlite-amalgamation-3490100.zip ; exit 0"
- 7z x sqlite-amalgamation-3490100.zip
- move sqlite-amalgamation-3490100 sqlite3
- git clone --depth=1 https://github.com/mercury233/irrlicht irrlicht
- git clone --depth=1 https://github.com/mercury233/irrlicht
before_build:
- xcopy /E premake\* .
- xcopy /E resource\* .
- premake5 vs2019 --server-zip-support
- premake5 vs2022
configuration: Release
platform: x64
build:
project: build/YGOPro.sln
parallel: true
project: build/YGOPro.sln
parallel: true
after_build:
- ps: move bin\release\ygopro.exe .
- ps: move bin\release\x64\ygopro.exe .
# nodejs
- bash -c "curl --retry 5 --connect-timeout 30 --location --remote-header-name --remote-name https://nodejs.org/dist/v16.9.1/node-v16.9.1-win-x64.7z ; exit 0"
......@@ -90,8 +90,7 @@ deploy:
branch: server
cache:
- premake-5.0.0-beta2-windows.zip
- libevent-2.0.22-stable.tar.gz
- irrlicht-1.8.5.zip
- lua-5.4.4.tar.gz
- sqlite-amalgamation-3390300.zip
- premake-5.0.0-beta6-windows.zip
- libevent-2.1.12-stable.tar.gz
- lua-5.4.7.tar.gz
- sqlite-amalgamation-3490100.zip
......@@ -28,6 +28,7 @@
john@suckerfreegames.com
*/
#define _IRR_STATIC_LIB_
#include <irrlicht.h>
#include "CGUITTFont.h"
......
......@@ -6,7 +6,6 @@
#include "image_manager.h"
#include "game.h"
#include "materials.h"
#include "../ocgcore/common.h"
namespace ygo {
......@@ -15,7 +14,7 @@ ClientField::ClientField() {
mzone[p].resize(7, 0);
szone[p].resize(8, 0);
}
rnd.reset((uint_fast32_t)std::random_device()());
rnd.seed(std::random_device()());
}
ClientField::~ClientField() {
for (int i = 0; i < 2; ++i) {
......@@ -229,12 +228,12 @@ void ClientField::AddCard(ClientCard* pcard, int controler, int location, int se
}
case LOCATION_GRAVE: {
grave[controler].push_back(pcard);
ResetSequence(grave[controler], false);
pcard->sequence = (unsigned char)(grave[controler].size() - 1);
break;
}
case LOCATION_REMOVED: {
remove[controler].push_back(pcard);
ResetSequence(remove[controler], false);
pcard->sequence = (unsigned char)(remove[controler].size() - 1);
break;
}
case LOCATION_EXTRA: {
......@@ -257,8 +256,13 @@ ClientCard* ClientField::RemoveCard(int controler, int location, int sequence) {
switch (location) {
case LOCATION_DECK: {
pcard = deck[controler][sequence];
deck[controler].erase(deck[controler].begin() + sequence);
ResetSequence(deck[controler], true);
for (size_t i = sequence; i < deck[controler].size() - 1; ++i) {
deck[controler][i] = deck[controler][i + 1];
deck[controler][i]->sequence--;
deck[controler][i]->curPos -= irr::core::vector3df(0, 0, 0.01f);
deck[controler][i]->mTransform.setTranslation(deck[controler][i]->curPos);
}
deck[controler].erase(deck[controler].end() - 1);
break;
}
case LOCATION_HAND: {
......@@ -279,20 +283,35 @@ ClientCard* ClientField::RemoveCard(int controler, int location, int sequence) {
}
case LOCATION_GRAVE: {
pcard = grave[controler][sequence];
grave[controler].erase(grave[controler].begin() + sequence);
ResetSequence(grave[controler], true);
for (size_t i = sequence; i < grave[controler].size() - 1; ++i) {
grave[controler][i] = grave[controler][i + 1];
grave[controler][i]->sequence--;
grave[controler][i]->curPos -= irr::core::vector3df(0, 0, 0.01f);
grave[controler][i]->mTransform.setTranslation(grave[controler][i]->curPos);
}
grave[controler].erase(grave[controler].end() - 1);
break;
}
case LOCATION_REMOVED: {
pcard = remove[controler][sequence];
remove[controler].erase(remove[controler].begin() + sequence);
ResetSequence(remove[controler], true);
for (size_t i = sequence; i < remove[controler].size() - 1; ++i) {
remove[controler][i] = remove[controler][i + 1];
remove[controler][i]->sequence--;
remove[controler][i]->curPos -= irr::core::vector3df(0, 0, 0.01f);
remove[controler][i]->mTransform.setTranslation(remove[controler][i]->curPos);
}
remove[controler].erase(remove[controler].end() - 1);
break;
}
case LOCATION_EXTRA: {
pcard = extra[controler][sequence];
extra[controler].erase(extra[controler].begin() + sequence);
ResetSequence(extra[controler], true);
for (size_t i = sequence; i < extra[controler].size() - 1; ++i) {
extra[controler][i] = extra[controler][i + 1];
extra[controler][i]->sequence--;
extra[controler][i]->curPos -= irr::core::vector3df(0, 0, 0.01f);
extra[controler][i]->mTransform.setTranslation(extra[controler][i]->curPos);
}
extra[controler].erase(extra[controler].end() - 1);
if (pcard->position & POS_FACEUP)
extra_p_count[controler]--;
break;
......@@ -418,7 +437,7 @@ void ClientField::ShowSelectCard(bool buttonok, bool chain) {
}
}
if(has_card_in_grave) {
rnd.shuffle_vector(selectable_cards);
std::shuffle(selectable_cards.begin(), selectable_cards.end(), rnd);
}
}
int startpos;
......@@ -1163,6 +1182,14 @@ bool ClientField::ShowSelectSum(bool panelmode) {
}
return false;
}
static void get_sum_params(irr::u32 opParam, int& op1, int& op2) {
op1 = opParam & 0xffff;
op2 = (opParam >> 16) & 0xffff;
if(op2 & 0x8000) {
op1 = opParam & 0x7fffffff;
op2 = 0;
}
}
bool ClientField::CheckSelectSum() {
std::set<ClientCard*> selable;
for(auto sc : selectsum_all) {
......@@ -1203,8 +1230,8 @@ bool ClientField::CheckSelectSum() {
int mm = -1, mx = -1, max = 0, sumc = 0;
bool ret = false;
for (auto sc : selected_cards) {
int op1 = sc->opParam & 0xffff;
int op2 = sc->opParam >> 16;
int op1, op2;
get_sum_params(sc->opParam, op1, op2);
int opmin = (op2 > 0 && op1 > op2) ? op2 : op1;
int opmax = op2 > op1 ? op2 : op1;
if (mm == -1 || opmin < mm)
......@@ -1219,8 +1246,8 @@ bool ClientField::CheckSelectSum() {
if (select_sumval <= max && select_sumval > max - mx)
ret = true;
for(auto sc : selable) {
int op1 = sc->opParam & 0xffff;
int op2 = sc->opParam >> 16;
int op1, op2;
get_sum_params(sc->opParam, op1, op2);
int m = op1;
int sums = sumc;
sums += m;
......@@ -1286,11 +1313,19 @@ bool ClientField::CheckSelectTribute() {
}
return ret;
}
void ClientField::get_sum_params(unsigned int opParam, int& op1, int& op2) {
op1 = opParam & 0xffff;
op2 = (opParam >> 16) & 0xffff;
if (op2 & 0x8000) {
op1 = opParam & 0x7fffffff;
op2 = 0;
}
}
bool ClientField::check_min(const std::set<ClientCard*>& left, std::set<ClientCard*>::const_iterator index, int min, int max) {
if (index == left.end())
return false;
int op1 = (*index)->opParam & 0xffff;
int op2 = (*index)->opParam >> 16;
int op1, op2;
get_sum_params((*index)->opParam, op1, op2);
int m = (op2 > 0 && op1 > op2) ? op2 : op1;
if (m >= min && m <= max)
return true;
......@@ -1309,9 +1344,8 @@ bool ClientField::check_sel_sum_s(const std::set<ClientCard*>& left, int index,
check_sel_sum_t(left, acc);
return false;
}
int l = selected_cards[index]->opParam;
int l1 = l & 0xffff;
int l2 = l >> 16;
int l1, l2;
get_sum_params(selected_cards[index]->opParam, l1, l2);
bool res1 = false, res2 = false;
res1 = check_sel_sum_s(left, index + 1, acc - l1);
if (l2 > 0)
......@@ -1325,9 +1359,8 @@ void ClientField::check_sel_sum_t(const std::set<ClientCard*>& left, int acc) {
continue;
std::set<ClientCard*> testlist(left);
testlist.erase(*sit);
int l = (*sit)->opParam;
int l1 = l & 0xffff;
int l2 = l >> 16;
int l1, l2;
get_sum_params((*sit)->opParam, l1, l2);
if (check_sum(testlist.begin(), testlist.end(), acc - l1, count)
|| (l2 > 0 && check_sum(testlist.begin(), testlist.end(), acc - l2, count))) {
selectsum_cards.insert(*sit);
......@@ -1339,9 +1372,8 @@ bool ClientField::check_sum(std::set<ClientCard*>::const_iterator index, std::se
return count >= select_min && count <= select_max;
if (acc < 0 || index == end)
return false;
int l = (*index)->opParam;
int l1 = l & 0xffff;
int l2 = l >> 16;
int l1, l2;
get_sum_params((*index)->opParam, l1, l2);
if ((l1 == acc || (l2 > 0 && l2 == acc)) && (count + 1 >= select_min) && (count + 1 <= select_max))
return true;
++index;
......@@ -1356,9 +1388,8 @@ bool ClientField::check_sel_sum_trib_s(const std::set<ClientCard*>& left, int in
check_sel_sum_trib_t(left, acc);
return acc >= select_min && acc <= select_max;
}
int l = selected_cards[index]->opParam;
int l1 = l & 0xffff;
int l2 = l >> 16;
int l1, l2;
get_sum_params(selected_cards[index]->opParam, l1, l2);
bool res1 = false, res2 = false;
res1 = check_sel_sum_trib_s(left, index + 1, acc + l1);
if(l2 > 0)
......@@ -1371,9 +1402,8 @@ void ClientField::check_sel_sum_trib_t(const std::set<ClientCard*>& left, int ac
continue;
std::set<ClientCard*> testlist(left);
testlist.erase(*sit);
int l = (*sit)->opParam;
int l1 = l & 0xffff;
int l2 = l >> 16;
int l1, l2;
get_sum_params((*sit)->opParam, l1, l2);
if(check_sum_trib(testlist.begin(), testlist.end(), acc + l1)
|| (l2 > 0 && check_sum_trib(testlist.begin(), testlist.end(), acc + l2))) {
selectsum_cards.insert(*sit);
......@@ -1385,9 +1415,8 @@ bool ClientField::check_sum_trib(std::set<ClientCard*>::const_iterator index, st
return true;
if(acc > select_max || index == end)
return false;
int l = (*index)->opParam;
int l1 = l & 0xffff;
int l2 = l >> 16;
int l1, l2;
get_sum_params((*index)->opParam, l1, l2);
if((acc + l1 >= select_min && acc + l1 <= select_max) || (acc + l2 >= select_min && acc + l2 <= select_max))
return true;
++index;
......
......@@ -2,7 +2,7 @@
#define CLIENT_FIELD_H
#include "config.h"
#include "../ocgcore/mtrandom.h"
#include <random>
#include <vector>
#include <set>
#include <map>
......@@ -92,7 +92,7 @@ public:
bool cant_check_grave{ false };
bool tag_surrender{ false };
bool tag_teammate_surrender{ false };
mt19937 rnd;
std::mt19937 rnd;
ClientField();
~ClientField();
......@@ -121,6 +121,7 @@ public:
bool ShowSelectSum(bool panelmode);
bool CheckSelectSum();
bool CheckSelectTribute();
void get_sum_params(unsigned int opParam, int& op1, int& op2);
bool check_min(const std::set<ClientCard*>& left, std::set<ClientCard*>::const_iterator index, int min, int max);
bool check_sel_sum_s(const std::set<ClientCard*>& left, int index, int acc);
void check_sel_sum_t(const std::set<ClientCard*>& left, int acc);
......
......@@ -13,7 +13,7 @@
#include <windows.h>
#include <ws2tcpip.h>
#ifdef _MSC_VER
#if defined(_MSC_VER) or defined(__MINGW32__)
#define mywcsncasecmp _wcsnicmp
#define mystrncasecmp _strnicmp
#else
......@@ -52,6 +52,17 @@ inline int _wtoi(const wchar_t * str){
}
#endif
// load env things
#ifdef _WIN32
#include <windows.h>
#include <string>
#else
#include <unistd.h>
#include <stdlib.h>
extern char** environ;
#endif
#include <cstdio>
#include <cstdlib>
#include <iostream>
......@@ -105,5 +116,7 @@ extern bool auto_watch_mode;
extern bool open_file;
extern wchar_t open_file_name[256];
extern bool bot_mode;
extern bool expansions_specified;
extern std::vector<std::wstring> expansions_list;
#endif
......@@ -103,12 +103,16 @@ bool DataManager::LoadDB(const wchar_t* wfile) {
else
ret = ReadDB(pDB);
sqlite3_close(pDB);
return ret;
#else
#ifdef _WIN32
auto reader = FileSystem->createAndOpenFile(wfile);
#else
auto reader = FileSystem->createAndOpenFile(file);
#endif
return LoadDB(reader);
}
bool DataManager::LoadDB(irr::io::IReadFile* reader) {
if(reader == nullptr)
return false;
spmemvfs_db_t db;
......@@ -120,14 +124,14 @@ bool DataManager::LoadDB(const wchar_t* wfile) {
reader->drop();
(mem->data)[mem->total] = '\0';
bool ret{};
if (spmemvfs_open_db(&db, file, mem) != SQLITE_OK)
if (spmemvfs_open_db(&db, "temp.db", mem) != SQLITE_OK)
ret = Error(db.handle);
else
ret = ReadDB(db.handle);
spmemvfs_close_db(&db);
spmemvfs_env_fini();
#endif //YGOPRO_SERVER_MODE
return ret;
#endif //SERVER_ZIP_SUPPORT
}
#ifndef YGOPRO_SERVER_MODE
bool DataManager::LoadStrings(const char* file) {
......@@ -141,6 +145,17 @@ bool DataManager::LoadStrings(const char* file) {
std::fclose(fp);
return true;
}
bool DataManager::LoadStrings(const wchar_t* file) {
FILE* fp = mywfopen(file, "r");
if(!fp)
return false;
char linebuf[TEXT_LINE_SIZE]{};
while(std::fgets(linebuf, sizeof linebuf, fp)) {
ReadStringConfLine(linebuf);
}
std::fclose(fp);
return true;
}
bool DataManager::LoadStrings(irr::io::IReadFile* reader) {
char ch{};
std::string linebuf;
......@@ -187,10 +202,162 @@ void DataManager::ReadStringConfLine(const char* linebuf) {
_setnameStrings[value] = strBuffer;
}
}
bool DataManager::LoadServerList(const char* file) {
FILE* fp = myfopen(file, "r");
if(!fp)
return false;
char linebuf[TEXT_LINE_SIZE]{};
while(std::fgets(linebuf, sizeof linebuf, fp)) {
ReadServerConfLine(linebuf);
}
std::fclose(fp);
return true;
}
bool DataManager::LoadServerList(const wchar_t* file) {
FILE* fp = mywfopen(file, "r");
if(!fp)
return false;
char linebuf[TEXT_LINE_SIZE]{};
while(std::fgets(linebuf, sizeof linebuf, fp)) {
ReadServerConfLine(linebuf);
}
std::fclose(fp);
return true;
}
bool DataManager::LoadServerList(irr::io::IReadFile* reader) {
char ch{};
std::string linebuf;
while (reader->read(&ch, 1)) {
if (ch == '\0')
break;
linebuf.push_back(ch);
if (ch == '\n' || linebuf.size() >= TEXT_LINE_SIZE - 1) {
ReadServerConfLine(linebuf.data());
linebuf.clear();
}
}
reader->drop();
return true;
}
void DataManager::ReadServerConfLine(const char* linebuf) {
char buffer[1024];
std::strncpy(buffer, linebuf, sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0';
buffer[strcspn(buffer, "\n")] = '\0';
char* sep1 = std::strchr(buffer, '|');
if (sep1 != nullptr) {
*sep1 = '\0';
char* addrPart = sep1 + 1;
wchar_t wname[256], wip[512];
// read the server name
BufferIO::DecodeUTF8(buffer, wname);
// replace the first '|' with ':'
char* sep2 = std::strchr(addrPart, '|');
if (sep2) {
*sep2 = ':';
}
BufferIO::DecodeUTF8(addrPart, wip);
_serverStrings.emplace_back(wname, wip);
}
}
bool DataManager::LoadCorresSrvIni(const char* file) {
FILE* fp = myfopen(file, "r");
if(!fp)
return false;
char linebuf[TEXT_LINE_SIZE]{};
while(std::fgets(linebuf, sizeof linebuf, fp)) {
ReadCorresSrvIniLine(linebuf);
}
std::fclose(fp);
InsertServerList();
return true;
}
bool DataManager::LoadCorresSrvIni(const wchar_t* file) {
FILE* fp = mywfopen(file, "r");
if(!fp)
return false;
char linebuf[TEXT_LINE_SIZE]{};
while(std::fgets(linebuf, sizeof linebuf, fp)) {
ReadCorresSrvIniLine(linebuf);
}
std::fclose(fp);
InsertServerList();
return true;
}
bool DataManager::LoadCorresSrvIni(irr::io::IReadFile* reader) {
char ch{};
std::string linebuf;
while (reader->read(&ch, 1)) {
if (ch == '\0')
break;
linebuf.push_back(ch);
if (ch == '\n' || linebuf.size() >= TEXT_LINE_SIZE - 1) {
ReadCorresSrvIniLine(linebuf.data());
linebuf.clear();
}
}
reader->drop();
InsertServerList();
return true;
}
void DataManager::ReadCorresSrvIniLine(const char* linebuf) {
std::wstring name = GetINIValue(linebuf, "ServerName = ");
std::wstring host = GetINIValue(linebuf, "ServerHost = ");
std::wstring port = GetINIValue(linebuf, "ServerPort = ");
if (name != L"")
iniName = name;
if (host != L"")
iniHost = host;
if (port != L"")
iniPort = port;
}
std::wstring DataManager::GetINIValue(const char* line, const char* key) {
if (!line || !key) {
return L"";
}
const char* keyPos = strstr(line, key);
if (!keyPos) {
return L"";
}
const char* valStart = keyPos + strlen(key);
while (*valStart == ' ')
valStart++;
const char* valEnd = valStart;
while (*valEnd && *valEnd != '\n' && *valEnd != '\r')
valEnd++;
if (valStart == valEnd)
return L"";
std::string narrowStr(valStart, valEnd);
if (narrowStr.empty())
return L"";
wchar_t wbuf[1024];
BufferIO::DecodeUTF8(narrowStr.c_str(), wbuf);
return wbuf;
}
void DataManager::InsertServerList() {
if (iniName != L"" && iniHost != L"") {
std::wstring ip = iniHost;
if (iniPort != L"") {
ip += L":";
ip += iniPort;
}
_serverStrings.emplace_back(iniName, ip);
}
iniName.clear();
iniHost.clear();
iniPort.clear();
}
#endif //YGOPRO_SERVER_MODE
bool DataManager::Error(sqlite3* pDB, sqlite3_stmt* pStmt) {
errmsg[0] = '\0';
std::strncat(errmsg, sqlite3_errmsg(pDB), sizeof errmsg - 1);
std::snprintf(errmsg, sizeof errmsg, "%s", sqlite3_errmsg(pDB));
if(pStmt)
sqlite3_finalize(pStmt);
return false;
......@@ -435,19 +602,14 @@ unsigned char* DataManager::ScriptReaderEx(const char* script_name, int* slen) {
buffer = ScriptReaderExSingle("specials/", script_name, slen, 9);
if(buffer)
return buffer;
buffer = ScriptReaderExSingle("expansions/", script_name, slen);
if(buffer)
return buffer;
#if defined(SERVER_PRO3_SUPPORT) && !defined(_WIN32)
buffer = ScriptReaderExSingle("Expansions/", script_name, slen);
if(buffer)
return buffer;
#endif
#if !defined(YGOPRO_SERVER_MODE) || defined(SERVER_ZIP_SUPPORT)
for(auto ex : mainGame->GetExpansionsListU("/")) {
buffer = ScriptReaderExSingle(ex.c_str(), script_name, slen);
if(buffer)
return buffer;
}
buffer = ScriptReaderExSingle("", script_name, slen, 2, TRUE);
if(buffer)
return buffer;
#endif
return ScriptReaderExSingle("", script_name, slen);
}
unsigned char* DataManager::ScriptReaderExSingle(const char* path, const char* script_name, int* slen, int pre_len, unsigned int use_irr) {
......
......@@ -45,10 +45,24 @@ public:
DataManager();
bool ReadDB(sqlite3* pDB);
bool LoadDB(const wchar_t* wfile);
#if defined(SERVER_ZIP_SUPPORT) || !defined(YGOPRO_SERVER_MODE)
bool LoadDB(irr::io::IReadFile* reader);
#endif
#ifndef YGOPRO_SERVER_MODE
bool LoadStrings(const char* file);
bool LoadStrings(const wchar_t* file);
bool LoadStrings(irr::io::IReadFile* reader);
void ReadStringConfLine(const char* linebuf);
bool LoadServerList(const char* file);
bool LoadServerList(const wchar_t* file);
bool LoadServerList(irr::io::IReadFile* reader);
void ReadServerConfLine(const char* linebuf);
bool LoadCorresSrvIni(const char* file);
bool LoadCorresSrvIni(const wchar_t* file);
bool LoadCorresSrvIni(irr::io::IReadFile* reader);
void ReadCorresSrvIniLine(const char* linebuf);
std::wstring GetINIValue(const char* line, const char* key);
void InsertServerList();
#endif
bool Error(sqlite3* pDB, sqlite3_stmt* pStmt = nullptr);
......@@ -83,6 +97,7 @@ public:
std::unordered_map<unsigned int, std::wstring> _victoryStrings;
std::unordered_map<unsigned int, std::wstring> _setnameStrings;
std::unordered_map<unsigned int, std::wstring> _sysStrings;
std::vector<std::pair<std::wstring, std::wstring>> _serverStrings;
#endif
char errmsg[512]{};
......@@ -119,6 +134,9 @@ private:
std::unordered_map<unsigned int, CardDataC> _datas;
std::unordered_map<unsigned int, CardString> _strings;
std::unordered_map<unsigned int, std::vector<uint16_t>> extra_setcode;
std::wstring iniName;
std::wstring iniHost;
std::wstring iniPort;
};
extern DataManager dataManager;
......
#include <array>
#include "config.h"
#include "deck_con.h"
#include "myfilesystem.h"
#include "data_manager.h"
#include "deck_manager.h"
#include "image_manager.h"
#include "sound_manager.h"
#include "game.h"
......@@ -54,6 +53,14 @@ static inline void load_current_deck(irr::gui::IGUIComboBox* cbCategory, irr::gu
deckManager.LoadCurrentDeck(cbCategory->getSelected(), cbCategory->getText(), cbDeck->getText());
}
DeckBuilder::DeckBuilder() {
std::random_device rd;
std::array<uint32_t, 8> seed{};
for (auto& x : seed)
x = rd();
std::seed_seq seq(seed.begin(), seed.end());
rnd.seed(seq);
}
void DeckBuilder::Initialize() {
mainGame->is_building = true;
mainGame->is_siding = false;
......@@ -82,7 +89,6 @@ void DeckBuilder::Initialize() {
filterList = &deckManager._lfList.back();
}
ClearSearch();
rnd.reset((uint_fast32_t)std::time(nullptr));
mouse_pos.set(0, 0);
hovered_code = 0;
hovered_pos = 0;
......@@ -176,7 +182,7 @@ bool DeckBuilder::OnEvent(const irr::SEvent& event) {
break;
}
case BUTTON_SHUFFLE_DECK: {
rnd.shuffle_vector(deckManager.current_deck.main);
std::shuffle(deckManager.current_deck.main.begin(), deckManager.current_deck.main.end(), rnd);
break;
}
case BUTTON_SAVE_DECK: {
......@@ -432,6 +438,29 @@ bool DeckBuilder::OnEvent(const irr::SEvent& event) {
prev_operation = id;
break;
}
case BUTTON_IMPORT_DECK_CODE: {
time_t nowtime = std::time(nullptr);
wchar_t timetext[40];
std::wcsftime(timetext, sizeof timetext / sizeof timetext[0], L"%Y-%m-%d %H-%M-%S", std::localtime(&nowtime));
mainGame->gMutex.lock();
mainGame->stDMMessage->setText(dataManager.GetSysString(1471));
mainGame->ebDMName->setVisible(true);
mainGame->ebDMName->setText(timetext);
mainGame->PopupElement(mainGame->wDMQuery);
mainGame->gMutex.unlock();
prev_operation = id;
break;
}
case BUTTON_EXPORT_DECK_CODE: {
std::stringstream textStream;
deckManager.SaveDeck(deckManager.current_deck, textStream);
wchar_t text[0x10000];
BufferIO::DecodeUTF8(textStream.str().c_str(), text);
mainGame->env->getOSOperator()->copyToClipboard(text);
mainGame->stACMessage->setText(dataManager.GetSysString(1480));
mainGame->PopupElement(mainGame->wACMessage, 20);
break;
}
case BUTTON_DM_OK: {
switch(prev_operation) {
case BUTTON_NEW_CATEGORY: {
......@@ -508,7 +537,8 @@ bool DeckBuilder::OnEvent(const irr::SEvent& event) {
}
break;
}
case BUTTON_NEW_DECK: {
case BUTTON_NEW_DECK:
case BUTTON_IMPORT_DECK_CODE: {
const wchar_t* deckname = mainGame->ebDMName->getText();
wchar_t catepath[256];
DeckManager::GetCategoryPath(catepath, mainGame->cbDBCategory->getSelected(), mainGame->cbDBCategory->getText());
......@@ -516,9 +546,19 @@ bool DeckBuilder::OnEvent(const irr::SEvent& event) {
myswprintf(filepath, L"%ls/%ls.ydk", catepath, deckname);
bool res = false;
if(!FileSystem::IsFileExists(filepath)) {
deckManager.current_deck.main.clear();
deckManager.current_deck.extra.clear();
deckManager.current_deck.side.clear();
if(prev_operation == BUTTON_NEW_DECK) {
deckManager.current_deck.main.clear();
deckManager.current_deck.extra.clear();
deckManager.current_deck.side.clear();
} else {
const wchar_t* txt = mainGame->env->getOSOperator()->getTextFromClipboard();
if(txt) {
char text[0x10000];
BufferIO::EncodeUTF8(txt, text);
std::istringstream textStream(text);
deckManager.LoadCurrentDeck(textStream);
}
}
res = DeckManager::SaveDeck(deckManager.current_deck, filepath);
RefreshDeckList();
ChangeCategory(mainGame->lstCategories->getSelected());
......
......@@ -3,15 +3,16 @@
#include <unordered_map>
#include <vector>
#include <random>
#include <irrlicht.h>
#include "data_manager.h"
#include "deck_manager.h"
#include "../ocgcore/mtrandom.h"
namespace ygo {
class DeckBuilder: public irr::IEventReceiver {
public:
DeckBuilder();
bool OnEvent(const irr::SEvent& event) override;
void Initialize();
void Terminate();
......@@ -80,7 +81,7 @@ public:
bool is_modified{};
bool readonly{};
bool showing_pack{};
mt19937 rnd;
std::mt19937 rnd;
const LFList* filterList{};
std::vector<code_pointer> results;
......
......@@ -11,53 +11,49 @@ char DeckManager::deckBuffer[0x10000]{};
#endif
DeckManager deckManager;
void DeckManager::LoadLFListSingle(const char* path) {
auto cur = _lfList.rend();
void DeckManager::LoadLFListSingle(const char* path, bool insert) {
FILE* fp = myfopen(path, "r");
char linebuf[256]{};
wchar_t strBuffer[256]{};
char str1[16]{};
if(fp) {
while(std::fgets(linebuf, sizeof linebuf, fp)) {
if(linebuf[0] == '#')
continue;
if(linebuf[0] == '!') {
auto len = std::strcspn(linebuf, "\r\n");
linebuf[len] = 0;
BufferIO::DecodeUTF8(&linebuf[1], strBuffer);
LFList newlist;
newlist.listName = strBuffer;
newlist.hash = 0x7dfcee6a;
_lfList.push_back(newlist);
cur = _lfList.rbegin();
continue;
if (!fp) return;
_LoadLFListFromLineProvider([&](char* buf, size_t sz) {
return std::fgets(buf, sz, fp) != nullptr;
}, insert);
std::fclose(fp);
}
void DeckManager::LoadLFListSingle(const wchar_t* path, bool insert) {
FILE* fp = mywfopen(path, "r");
if (!fp) return;
_LoadLFListFromLineProvider([&](char* buf, size_t sz) {
return std::fgets(buf, sz, fp) != nullptr;
}, insert);
std::fclose(fp);
}
#if defined(SERVER_ZIP_SUPPORT) || !defined(YGOPRO_SERVER_MODE)
void DeckManager::LoadLFListSingle(irr::io::IReadFile* reader, bool insert) {
std::string linebuf;
char ch{};
_LoadLFListFromLineProvider([&](char* buf, size_t sz) {
while (reader->read(&ch, 1)) {
if (ch == '\0') break;
linebuf.push_back(ch);
if (ch == '\n' || linebuf.size() >= sz - 1) {
std::strncpy(buf, linebuf.c_str(), sz - 1);
buf[sz - 1] = '\0';
linebuf.clear();
return true;
}
if (cur == _lfList.rend())
continue;
unsigned int code = 0;
int count = -1;
if (std::sscanf(linebuf, "%10s%*[ ]%1d", str1, &count) != 2)
continue;
if (count < 0 || count > 2)
continue;
code = std::strtoul(str1, nullptr, 10);
cur->content[code] = count;
cur->hash = cur->hash ^ ((code << 18) | (code >> 14)) ^ ((code << (27 + count)) | (code >> (5 - count)));
}
std::fclose(fp);
}
return false;
}, insert);
reader->drop();
}
#endif
void DeckManager::LoadLFList() {
#ifdef SERVER_PRO2_SUPPORT
LoadLFListSingle("config/lflist.conf");
#endif
#ifdef SERVER_PRO3_SUPPORT
LoadLFListSingle("Data/lflist.conf");
#ifndef _WIN32
LoadLFListSingle("Expansions/lflist.conf");
#endif
#endif
LoadLFListSingle("expansions/lflist.conf");
LoadLFListSingle("specials/lflist.conf");
LoadLFListSingle("lflist.conf");
LFList nolimit;
......@@ -299,6 +295,10 @@ irr::io::IReadFile* DeckManager::OpenDeckReader(const wchar_t* file) {
#endif
return reader;
}
bool DeckManager::LoadCurrentDeck(std::istringstream& deckStream, bool is_packlist) {
LoadDeckFromStream(current_deck, deckStream, is_packlist);
return true; // the above LoadDeck has return value but we ignore it here for now
}
bool DeckManager::LoadCurrentDeck(const wchar_t* file, bool is_packlist) {
current_deck.clear();
auto reader = OpenDeckReader(file);
......@@ -333,21 +333,27 @@ bool DeckManager::LoadCurrentDeck(int category_index, const wchar_t* category_na
mainGame->deckBuilder.RefreshPackListScroll();
return res;
}
void DeckManager::SaveDeck(const Deck& deck, std::stringstream& deckStream) {
deckStream << "#created by ..." << std::endl;
deckStream << "#main" << std::endl;
for(size_t i = 0; i < deck.main.size(); ++i)
deckStream << deck.main[i]->first << std::endl;
deckStream << "#extra" << std::endl;
for(size_t i = 0; i < deck.extra.size(); ++i)
deckStream << deck.extra[i]->first << std::endl;
deckStream << "!side" << std::endl;
for(size_t i = 0; i < deck.side.size(); ++i)
deckStream << deck.side[i]->first << std::endl;
}
bool DeckManager::SaveDeck(const Deck& deck, const wchar_t* file) {
if(!FileSystem::IsDirExists(L"./deck") && !FileSystem::MakeDir(L"./deck"))
return false;
FILE* fp = OpenDeckFile(file, "w");
if(!fp)
return false;
std::fprintf(fp, "#created by ...\n#main\n");
for(size_t i = 0; i < deck.main.size(); ++i)
std::fprintf(fp, "%u\n", deck.main[i]->first);
std::fprintf(fp, "#extra\n");
for(size_t i = 0; i < deck.extra.size(); ++i)
std::fprintf(fp, "%u\n", deck.extra[i]->first);
std::fprintf(fp, "!side\n");
for(size_t i = 0; i < deck.side.size(); ++i)
std::fprintf(fp, "%u\n", deck.side[i]->first);
std::stringstream deckStream;
SaveDeck(deck, deckStream);
std::fwrite(deckStream.str().c_str(), 1, deckStream.str().length(), fp);
std::fclose(fp);
return true;
}
......
......@@ -5,6 +5,7 @@
#include <vector>
#include <sstream>
#include "data_manager.h"
#include "bufferio.h"
#ifndef YGOPRO_MAX_DECK
#define YGOPRO_MAX_DECK 60
......@@ -66,7 +67,11 @@ public:
static char deckBuffer[0x10000];
#endif
void LoadLFListSingle(const char* path);
void LoadLFListSingle(const char* path, bool insert = false);
void LoadLFListSingle(const wchar_t* path, bool insert = false);
#if defined(SERVER_ZIP_SUPPORT) || !defined(YGOPRO_SERVER_MODE)
void LoadLFListSingle(irr::io::IReadFile* reader, bool insert = false);
#endif
void LoadLFList();
const wchar_t* GetLFListName(unsigned int lfhash);
const LFList* GetLFList(unsigned int lfhash);
......@@ -74,11 +79,12 @@ public:
#ifndef YGOPRO_SERVER_MODE
bool LoadCurrentDeck(const wchar_t* file, bool is_packlist = false);
bool LoadCurrentDeck(int category_index, const wchar_t* category_name, const wchar_t* deckname);
bool LoadCurrentDeck(std::istringstream& deckStream, bool is_packlist = false);
wchar_t DeckFormatBuffer[128];
int TypeCount(std::vector<code_pointer> list, unsigned int ctype);
bool LoadDeckFromCode(Deck& deck, const unsigned char *code, int len);
int SaveDeckToCode(Deck &deck, unsigned char *code);
#endif // YGOPRO_SERVER_MODE
#endif //YGOPRO_SERVER_MODE
static uint32_t LoadDeck(Deck& deck, uint32_t dbuf[], int mainc, int sidec, bool is_packlist = false);
static bool LoadSide(Deck& deck, uint32_t dbuf[], int mainc, int sidec);
......@@ -89,12 +95,56 @@ public:
static FILE* OpenDeckFile(const wchar_t* file, const char* mode);
static irr::io::IReadFile* OpenDeckReader(const wchar_t* file);
static bool SaveDeck(const Deck& deck, const wchar_t* file);
static void SaveDeck(const Deck& deck, std::stringstream& deckStream);
static bool DeleteDeck(const wchar_t* file);
static bool CreateCategory(const wchar_t* name);
static bool RenameCategory(const wchar_t* oldname, const wchar_t* newname);
static bool DeleteCategory(const wchar_t* name);
static bool SaveDeckArray(const DeckArray& deck, const wchar_t* name);
#endif // YGOPRO_SERVER_MODE
#endif //YGOPRO_SERVER_MODE
private:
template<typename LineProvider>
void _LoadLFListFromLineProvider(LineProvider getLine, bool insert = false) {
std::vector<LFList> loadedLists;
auto cur = loadedLists.rend(); // 注意:在临时 list 上操作
char line[256]{};
wchar_t strBuffer[256]{};
char str1[16]{};
while (getLine(line, sizeof(line))) {
if (line[0] == '#')
continue;
if (line[0] == '!') {
auto len = std::strcspn(line, "\r\n");
line[len] = 0;
BufferIO::DecodeUTF8(&line[1], strBuffer);
LFList newlist;
newlist.listName = strBuffer;
newlist.hash = 0x7dfcee6a;
loadedLists.push_back(newlist);
cur = loadedLists.rbegin();
continue;
}
if (cur == loadedLists.rend())
continue;
unsigned int code = 0;
int count = -1;
if (std::sscanf(line, "%10s%*[ ]%1d", str1, &count) != 2)
continue;
if (count < 0 || count > 2)
continue;
code = std::strtoul(str1, nullptr, 10);
cur->content[code] = count;
cur->hash = cur->hash ^ ((code << 18) | (code >> 14)) ^ ((code << (27 + count)) | (code >> (5 - count)));
}
if (insert) {
_lfList.insert(_lfList.begin(), loadedLists.begin(), loadedLists.end());
} else {
_lfList.insert(_lfList.end(), loadedLists.begin(), loadedLists.end());
}
}
};
extern DeckManager deckManager;
......
......@@ -5,7 +5,6 @@
#include "deck_manager.h"
#include "sound_manager.h"
#include "duelclient.h"
#include "../ocgcore/common.h"
namespace ygo {
......@@ -1393,12 +1392,8 @@ void Game::DrawDeckBd() {
driver->draw2DRectangle(Resize(805, 160, 1020, 630), 0x400000ff, 0x400000ff, 0x40000000, 0x40000000);
driver->draw2DRectangleOutline(Resize(804, 159, 1020, 630));
}
#ifdef YGOPRO_USE_THUMB_LOAD_THREAD
constexpr int MAX_RESULT = 9;
#else
constexpr int MAX_RESULT = 7;
#endif
for(int i = 0; i < MAX_RESULT && i + scrFilter->getPos() < (int)deckBuilder.results.size(); ++i) {
int max_result = mainGame->gameConf.use_image_load_background_thread ? 9 : 7;
for(int i = 0; i < max_result && i + scrFilter->getPos() < (int)deckBuilder.results.size(); ++i) {
code_pointer ptr = deckBuilder.results[i + scrFilter->getPos()];
if(i >= 7)
{
......
This diff is collapsed.
......@@ -3,8 +3,8 @@
#include <vector>
#include <set>
#include <random>
#include "network.h"
#include "../ocgcore/mtrandom.h"
namespace ygo {
......@@ -65,7 +65,8 @@ private:
static unsigned char last_successful_msg[SIZE_NETWORK_BUFFER];
static size_t last_successful_msg_length;
static wchar_t event_string[256];
static mt19937 rnd;
static std::mt19937 rnd;
static std::uniform_real_distribution<float> real_dist;
static bool is_refreshing;
static int match_kill;
static event* resp_event;
......@@ -92,7 +93,7 @@ public:
static unsigned int LookupHost(char *host);
static bool LookupSRV(char *hostname, HostResult* result);
static bool CheckHostnameSplitter(char *hostname, HostResult *result);
static HostResult ParseHost(char *hostname, unsigned short port);
static HostResult ParseHost(char *hostname);
static void SendPacketToServer(unsigned char proto) {
auto p = duel_client_write;
buffer_write<uint16_t>(p, 1);
......@@ -105,8 +106,7 @@ public:
template<typename ST>
static void SendPacketToServer(unsigned char proto, const ST& st) {
auto p = duel_client_write;
if (sizeof(ST) > MAX_DATA_SIZE)
return;
static_assert(sizeof(ST) <= MAX_DATA_SIZE, "Packet size is too large.");
buffer_write<uint16_t>(p, (uint16_t)(1 + sizeof(ST)));
buffer_write<uint8_t>(p, proto);
std::memcpy(p, &st, sizeof(ST));
......@@ -128,7 +128,7 @@ public:
bufferevent_write(client_bev, duel_client_write, len + 3);
}
static std::vector<HostPacket> hosts;
static std::vector<std::wstring> hosts;
static std::vector<std::wstring> hosts_srvpro;
static bool is_srvpro;
static void BeginRefreshHost();
......
#include "event_handler.h"
#include "client_field.h"
#include "math.h"
#include "network.h"
#include "game.h"
#include "duelclient.h"
......@@ -11,7 +10,6 @@
#include "replay_mode.h"
#include "single_mode.h"
#include "materials.h"
#include "../ocgcore/common.h"
namespace ygo {
......@@ -384,9 +382,9 @@ bool ClientField::OnEvent(const irr::SEvent& event) {
select_options_index.clear();
for (size_t i = 0; i < activatable_cards.size(); ++i) {
if (activatable_cards[i] == menu_card) {
if(activatable_descs[i].second == EDESC_OPERATION)
if(activatable_descs[i].second & EDESC_OPERATION)
continue;
else if(activatable_descs[i].second == EDESC_RESET) {
else if(activatable_descs[i].second & EDESC_RESET) {
if(id == BUTTON_CMD_ACTIVATE) continue;
} else {
if(id == BUTTON_CMD_RESET) continue;
......@@ -675,7 +673,7 @@ bool ClientField::OnEvent(const irr::SEvent& event) {
select_options_index.clear();
for (size_t i = 0; i < activatable_cards.size(); ++i) {
if (activatable_cards[i] == command_card) {
if(activatable_descs[i].second == EDESC_OPERATION) {
if(activatable_descs[i].second & EDESC_OPERATION) {
if(list_command == COMMAND_ACTIVATE) continue;
} else {
if(list_command == COMMAND_OPERATION) continue;
......@@ -1322,7 +1320,7 @@ bool ClientField::OnEvent(const irr::SEvent& event) {
case MSG_SELECT_DISFIELD: {
if (!(hovered_location & LOCATION_ONFIELD))
break;
unsigned int flag = 1 << (hovered_sequence + (hovered_controler << 4) + ((hovered_location == LOCATION_MZONE) ? 0 : 8));
unsigned int flag = 0x1U << (hovered_sequence + (hovered_controler << 4) + ((hovered_location == LOCATION_MZONE) ? 0 : 8));
if (flag & selectable_field) {
if (flag & selected_field) {
selected_field &= ~flag;
......
This diff is collapsed.
......@@ -17,10 +17,10 @@
#include "deck_con.h"
#include "menu_handler.h"
#include "CGUISkinSystem/CGUISkinSystem.h"
#include <ctime>
#else
#include "netserver.h"
#endif //YGOPRO_SERVER_MODE
#include <ctime>
#include <unordered_map>
#include <vector>
#include <list>
......@@ -37,18 +37,40 @@ constexpr int TEXT_LINE_SIZE = 256;
namespace ygo {
bool IsExtension(const wchar_t* filename, const wchar_t* extension);
bool IsExtension(const char* filename, const char* extension);
template<size_t N>
bool IsExtension(const wchar_t* filename, const wchar_t(&extension)[N]) {
auto flen = std::wcslen(filename);
constexpr size_t elen = N - 1;
if (!elen || flen < elen)
return false;
return !mywcsncasecmp(filename + (flen - elen), extension, elen);
}
template<size_t N>
bool IsExtension(const char* filename, const char(&extension)[N]) {
auto flen = std::strlen(filename);
constexpr size_t elen = N - 1;
if (!elen || flen < elen)
return false;
return !mystrncasecmp(filename + (flen - elen), extension, elen);
}
#ifndef YGOPRO_SERVER_MODE
struct Config {
bool use_d3d{ false };
bool use_image_scale{ true };
bool use_image_scale_multi_thread{ true };
#ifdef _OPENMP
bool use_image_load_background_thread{ false };
#else
bool use_image_load_background_thread{ true };
#endif
bool freever{ true };
unsigned short antialias{ 0 };
unsigned short serverport{ 7911 };
unsigned char textfontsize{ 14 };
wchar_t lasthost[100]{};
wchar_t lastport[10]{};
// wchar_t lastport[10]{};
wchar_t nickname[20]{};
wchar_t gamename[20]{};
wchar_t roompass[20]{};
......@@ -172,23 +194,32 @@ public:
#ifdef YGOPRO_SERVER_MODE
void MainServerLoop();
void MainTestLoop(int code);
void LoadExpansions();
void LoadExpansions(const wchar_t* expansions_path);
void LoadExpansionsAll();
std::vector<std::wstring> GetExpansionsList(const wchar_t * suffix = nullptr);
std::vector<std::string> GetExpansionsListU(const char* suffix = nullptr);
void AddDebugMsg(const char* msgbuf);
void initUtils();
void InjectEnvToRegistry(intptr_t pduel);
#else
void MainLoop();
void RefreshTimeDisplay();
void BuildProjectionMatrix(irr::core::matrix4& mProjection, irr::f32 left, irr::f32 right, irr::f32 bottom, irr::f32 top, irr::f32 znear, irr::f32 zfar);
void InitStaticText(irr::gui::IGUIStaticText* pControl, irr::u32 cWidth, irr::u32 cHeight, irr::gui::CGUITTFont* font, const wchar_t* text);
std::wstring SetStaticText(irr::gui::IGUIStaticText* pControl, irr::u32 cWidth, irr::gui::CGUITTFont* font, const wchar_t* text, irr::u32 pos = 0);
void LoadExpansions();
void RefreshCategoryDeck(irr::gui::IGUIComboBox* cbCategory, irr::gui::IGUIComboBox* cbDeck, bool selectlastused = true);
void LoadExpansions(const wchar_t* expansions_path);
void LoadExpansionsAll();
std::vector<std::wstring> GetExpansionsList(const wchar_t * suffix = nullptr);
std::vector<std::string> GetExpansionsListU(const char* suffix = nullptr);
void RefreshCategoryDeck(irr::gui::IGUIComboBox *cbCategory, irr::gui::IGUIComboBox *cbDeck, bool selectlastused = true);
void RefreshDeck(irr::gui::IGUIComboBox* cbCategory, irr::gui::IGUIComboBox* cbDeck);
void RefreshDeck(const wchar_t* deckpath, const std::function<void(const wchar_t*)>& additem);
void RefreshReplay();
void RefreshSingleplay();
void RefreshBot();
void RefreshLocales();
void RefreshLFList();
void RefreshServerList();
void DrawSelectionLine(irr::video::S3DVertex* vec, bool strip, int width, float* cv);
void DrawSelectionLine(irr::gui::IGUIElement* element, int width, irr::video::SColor color);
void DrawBackGround();
......@@ -263,6 +294,7 @@ public:
void FlashWindow();
void takeScreenshot();
void SetCursor(irr::gui::ECURSOR_ICON icon);
void InjectEnvToRegistry(intptr_t pduel);
template<typename T>
static void DrawShadowText(irr::gui::CGUITTFont* font, const T& text, const irr::core::rect<irr::s32>& position, const irr::core::rect<irr::s32>& padding,
irr::video::SColor color = 0xffffffff, irr::video::SColor shadowcolor = 0xff000000, bool hcenter = false, bool vcenter = false, const irr::core::rect<irr::s32>* clip = nullptr);
......@@ -343,7 +375,7 @@ public:
irr::gui::CGUITTFont* numFont;
irr::gui::CGUITTFont* adFont;
irr::gui::CGUITTFont* lpcFont;
std::map<irr::gui::CGUIImageButton*, int> imageLoading;
std::unordered_map<irr::gui::CGUIImageButton*, int> imageLoading;
//card image
irr::gui::IGUIStaticText* wCardImg;
irr::gui::IGUIImage* imgCard;
......@@ -411,7 +443,6 @@ public:
irr::gui::IGUIListBox* lstHostList;
irr::gui::IGUIButton* btnLanRefresh;
irr::gui::IGUIEditBox* ebJoinHost;
irr::gui::IGUIEditBox* ebJoinPort;
irr::gui::IGUIEditBox* ebJoinPass;
irr::gui::IGUIButton* btnJoinHost;
irr::gui::IGUIButton* btnJoinCancel;
......@@ -616,6 +647,8 @@ public:
irr::gui::IGUIButton* btnDMDeleteDeck;
irr::gui::IGUIButton* btnMoveDeck;
irr::gui::IGUIButton* btnCopyDeck;
irr::gui::IGUIButton* btnImportDeckCode;
irr::gui::IGUIButton* btnExportDeckCode;
irr::gui::IGUIWindow* wDMQuery;
irr::gui::IGUIStaticText* stDMMessage;
irr::gui::IGUIStaticText* stDMMessage2;
......@@ -680,17 +713,25 @@ public:
irr::gui::IGUIButton* btnBigCardZoomIn;
irr::gui::IGUIButton* btnBigCardZoomOut;
irr::gui::IGUIButton* btnBigCardClose;
//server list
irr::gui::IGUIButton* btnServerList;
irr::gui::IGUIWindow* wServerList;
irr::gui::IGUIListBox* lstServerList;
irr::gui::IGUIButton* btnServerReturn;
#endif //YGOPRO_SERVER_MODE
};
extern Game* mainGame;
#ifdef YGOPRO_SERVER_MODE
#define MAX_MATCH_COUNT 3
extern unsigned short server_port;
extern unsigned short replay_mode;
extern HostInfo game_info;
extern unsigned int pre_seed[5];
extern unsigned int duel_flags;
extern uint32_t pre_seed[MAX_MATCH_COUNT][SEED_COUNT];
extern uint8_t pre_seed_specified[MAX_MATCH_COUNT];
extern uint32_t duel_flags;
#endif
}
......@@ -882,6 +923,8 @@ extern unsigned int duel_flags;
#define LISTBOX_DECKS 340
#define BUTTON_DM_OK 341
#define BUTTON_DM_CANCEL 342
#define BUTTON_IMPORT_DECK_CODE 343
#define BUTTON_EXPORT_DECK_CODE 344
#define COMBOBOX_LFLIST 349
#define BUTTON_CLEAR_LOG 350
......@@ -917,6 +960,10 @@ extern unsigned int duel_flags;
#define BUTTON_DECK_CODE_SAVE 390
#define BUTTON_DECK_CODE_CANCEL 391
#define BUTTON_SERVER_LIST 392
#define LISTBOX_SERVER_LIST 393
#define BUTTON_SERVER_RETURN 394
#define TEXTURE_DUEL 0
#define TEXTURE_DECK 1
#define TEXTURE_MENU 2
......
......@@ -8,8 +8,17 @@
#ifdef __APPLE__
#import <CoreFoundation/CoreFoundation.h>
#endif
#ifdef YGOPRO_SERVER_MODE
#include "base64.h"
#endif
#ifdef YGOPRO_SERVER_MODE
#include <sstream>
#endif
unsigned int enable_log = 0x3;
bool expansions_specified = false;
std::vector<std::wstring> expansions_list;
#ifndef YGOPRO_SERVER_MODE
bool exit_on_return = false;
bool auto_watch_mode = false;
......@@ -72,6 +81,36 @@ int main(int argc, char* argv[]) {
ygo::Game _game;
#ifdef YGOPRO_SERVER_MODE
enable_log = 1;
bool expansions_specified = false;
wchar_t* expansions_env_val = nullptr;
#ifdef _WIN32
expansions_env_val = _wgetenv(L"YGOPRO_EXPANSIONS");
#else
const char* env_utf8 = std::getenv("YGOPRO_EXPANSIONS");
if(env_utf8) {
expansions_env_val = (wchar_t*)malloc(1024 * sizeof(wchar_t));
BufferIO::DecodeUTF8String(env_utf8, expansions_env_val, 1024);
}
#endif
if (expansions_env_val && expansions_env_val[0] != L'\0') {
expansions_specified = true;
std::wstringstream ss(expansions_env_val);
std::wstring item;
while (std::getline(ss, item, L',')) {
if (!item.empty()) {
expansions_list.push_back(item);
}
}
} else {
expansions_specified = false;
expansions_list.push_back(L"./expansions");
#if defined(SERVER_PRO3_SUPPORT) && !defined(_WIN32) && !defined(__APPLE__)
expansions_list.push_back(L"./Expansions");
#endif
}
ygo::server_port = 7911;
ygo::replay_mode = 0;
ygo::duel_flags = 0;
......@@ -85,8 +124,8 @@ int main(int argc, char* argv[]) {
ygo::game_info.no_shuffle_deck = false;
ygo::game_info.duel_rule = YGOPRO_DEFAULT_DUEL_RULE;
ygo::game_info.time_limit = 180;
for (int i = 0; i < 3; ++i)
ygo::pre_seed[i] = (unsigned int)0;
std::memset(ygo::pre_seed, 0, sizeof(ygo::pre_seed));
std::memset(ygo::pre_seed_specified, 0, sizeof(ygo::pre_seed_specified));
if (argc == 2) {
int code = atoi(argv[1]);
ygo::mainGame = &_game;
......@@ -129,9 +168,28 @@ int main(int argc, char* argv[]) {
ygo::game_info.draw_count = atoi(argv[10]);
ygo::game_info.time_limit = atoi(argv[11]);
ygo::replay_mode = atoi(argv[12]);
for (int i = 13; (i < argc && i <= 17) ; ++i)
for (int i = 13; (i < argc && i < (13 + MAX_MATCH_COUNT)) ; ++i)
{
ygo::pre_seed[i - 13] = (unsigned int)atol(argv[i]);
auto ok = Base64::Decode(
reinterpret_cast<const unsigned char*>(argv[i]),
strlen(argv[i]),
reinterpret_cast<unsigned char*>(ygo::pre_seed[i - 13]),
SEED_COUNT * sizeof(uint32_t)
);
if(ok) {
// check if it isn't all zero
bool all_zero = true;
for (int j = 0; j < SEED_COUNT; ++j) {
if (ygo::pre_seed[i - 13][j] != 0) {
all_zero = false;
break;
}
}
if (!all_zero)
ygo::pre_seed_specified[i - 13] = 1;
}
else
std::fprintf(stderr, "Failed to decode seed %d: %s\n", i - 13, argv[i]);
}
}
ygo::mainGame = &_game;
......@@ -157,7 +215,7 @@ int main(int argc, char* argv[]) {
bool keep_on_return = false;
bool deckCategorySpecified = false;
bool portSpecified = false;
expansions_list.push_back(L"./expansions");
for(int i = 1; i < wargc; ++i) {
if (wargc == 2 && std::wcslen(wargv[1]) >= 4) {
wchar_t* pstrext = wargv[1] + std::wcslen(wargv[1]) - 4;
......@@ -196,21 +254,17 @@ int main(int argc, char* argv[]) {
++i;
if(i < wargc) {
ygo::mainGame->ebJoinHost->setText(wargv[i]);
if(!portSpecified)
ygo::mainGame->ebJoinPort->setText(L"");
}
continue;
} else if(!std::wcscmp(wargv[i], L"-p")) { // host Port
++i;
if(i < wargc) {
portSpecified = true;
auto port = _wtoi(wargv[i]);
if(port) {
wchar_t portStr[6];
myswprintf(portStr, L"%d", port);
ygo::mainGame->ebJoinPort->setText(portStr);
} else {
ygo::mainGame->ebJoinPort->setText(L"");
auto hostText = ygo::mainGame->ebJoinHost->getText();
if(port && hostText) {
wchar_t newHostStr[100];
myswprintf(newHostStr, L"%ls:%d", hostText, port);
ygo::mainGame->ebJoinHost->setText(newHostStr);
}
}
continue;
......@@ -286,6 +340,16 @@ int main(int argc, char* argv[]) {
if(open_file)
ClickButton(ygo::mainGame->btnLoadSinglePlay);
break;
} else if(!std::wcscmp(wargv[i], L"--expansions")) { // specify expansions
++i;
if(i < wargc) {
if(!expansions_specified) {
expansions_list.clear();
expansions_specified = true;
}
expansions_list.push_back(wargv[i]);
}
continue;
}
}
ygo::mainGame->MainLoop();
......
This diff is collapsed.
#ifndef IMAGEMANAGER_H
#define IMAGEMANAGER_H
#ifndef _OPENMP
#define YGOPRO_USE_THUMB_LOAD_THREAD
#endif
#include "config.h"
#include "data_manager.h"
#include <unordered_map>
#ifdef YGOPRO_USE_THUMB_LOAD_THREAD
#include <queue>
#include <mutex>
#endif
namespace ygo {
......@@ -34,19 +28,15 @@ public:
irr::video::ITexture* GetBigPicture(int code, float zoom);
irr::video::ITexture* GetTextureThumb(int code);
irr::video::ITexture* GetTextureField(int code);
#ifdef YGOPRO_USE_THUMB_LOAD_THREAD
static int LoadThumbThread();
#endif
std::unordered_map<int, irr::video::ITexture*> tMap[2];
std::unordered_map<int, irr::video::ITexture*> tThumb;
std::unordered_map<int, irr::video::ITexture*> tFields;
#ifdef YGOPRO_USE_THUMB_LOAD_THREAD
std::unordered_map<int, irr::video::IImage*> tThumbLoading;
std::queue<int> tThumbLoadingCodes;
std::mutex tThumbLoadingMutex;
bool tThumbLoadingThreadRunning;
#endif
irr::IrrlichtDevice* device;
irr::video::IVideoDriver* driver;
irr::video::ITexture* tCover[4];
......@@ -54,9 +44,7 @@ public:
irr::video::ITexture* tUnknownFit;
irr::video::ITexture* tUnknownThumb;
irr::video::ITexture* tBigPicture;
#ifdef YGOPRO_USE_THUMB_LOAD_THREAD
irr::video::ITexture* tLoading;
#endif
irr::video::ITexture* tAct;
irr::video::ITexture* tAttack;
irr::video::ITexture* tNegated;
......
......@@ -64,15 +64,11 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) {
case BUTTON_JOIN_HOST: {
bot_mode = false;
mainGame->TrimText(mainGame->ebJoinHost);
mainGame->TrimText(mainGame->ebJoinPort);
char hostname_tag[100];
wchar_t pstr[100];
wchar_t portstr[10];
BufferIO::CopyWideString(mainGame->ebJoinHost->getText(), pstr);
BufferIO::CopyWideString(mainGame->ebJoinPort->getText(), portstr);
BufferIO::EncodeUTF8(pstr, hostname_tag);
auto port = std::wcstol(portstr, nullptr, 10);
HostResult remote = DuelClient::ParseHost(hostname_tag, port);
HostResult remote = DuelClient::ParseHost(hostname_tag);
if(!remote.isValid()) {
mainGame->gMutex.lock();
soundManager.PlaySoundEffect(SOUND_INFO);
......@@ -85,7 +81,6 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) {
break;
}
BufferIO::CopyWideString(pstr, mainGame->gameConf.lasthost);
BufferIO::CopyWideString(portstr, mainGame->gameConf.lastport);
BufferIO::CopyWideString(mainGame->ebJoinPass->getText(), mainGame->gameConf.roompass);
if(DuelClient::StartClient(remote.host, remote.port, false)) {
mainGame->btnCreateHost->setEnabled(false);
......@@ -96,6 +91,7 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) {
}
case BUTTON_JOIN_CANCEL: {
mainGame->HideElement(mainGame->wLanWindow);
mainGame->HideElement(mainGame->wServerList);
mainGame->ShowElement(mainGame->wMainMenu);
if(exit_on_return)
mainGame->device->closeDevice();
......@@ -109,6 +105,7 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) {
mainGame->btnHostConfirm->setEnabled(true);
mainGame->btnHostCancel->setEnabled(true);
mainGame->HideElement(mainGame->wLanWindow);
mainGame->HideElement(mainGame->wServerList);
mainGame->ShowElement(mainGame->wCreateHost);
break;
}
......@@ -220,8 +217,12 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) {
case BUTTON_LOAD_REPLAY: {
int start_turn = 1;
if(open_file) {
ReplayMode::cur_replay.OpenReplay(open_file_name);
open_file = false;
if (!ReplayMode::cur_replay.OpenReplay(open_file_name)) {
if (exit_on_return)
mainGame->device->closeDevice();
break;
}
} else {
auto selected = mainGame->lstReplayList->getSelected();
if(selected == -1)
......@@ -295,7 +296,7 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) {
myswprintf(replay_path, L"./replay/%ls", replay_filename);
if (!replay.OpenReplay(replay_path))
break;
if (replay.pheader.flag & REPLAY_SINGLE_MODE)
if (replay.pheader.base.flag & REPLAY_SINGLE_MODE)
break;
for (size_t i = 0; i < replay.decks.size(); ++i) {
BufferIO::CopyWideString(replay.players[Replay::GetDeckPlayer(i)].c_str(), namebuf[i]);
......@@ -489,6 +490,15 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) {
prev_sel = -1;
break;
}
case BUTTON_SERVER_LIST: {
mainGame->ShowElement(mainGame->wServerList);
mainGame->PopupElement(mainGame->wServerList);
break;
}
case BUTTON_SERVER_RETURN: {
mainGame->HideElement(mainGame->wServerList);
break;
}
}
break;
}
......@@ -502,21 +512,18 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) {
mainGame->ebJoinPass->setText(DuelClient::hosts_srvpro[sel].c_str());
break;
}
int addr = DuelClient::hosts[sel].ipaddr;
int port = DuelClient::hosts[sel].port;
wchar_t buf[20];
myswprintf(buf, L"%d.%d.%d.%d", addr & 0xff, (addr >> 8) & 0xff, (addr >> 16) & 0xff, (addr >> 24) & 0xff);
mainGame->ebJoinHost->setText(buf);
myswprintf(buf, L"%d", port);
mainGame->ebJoinPort->setText(buf);
mainGame->ebJoinHost->setText(DuelClient::hosts[sel].c_str());
break;
}
case LISTBOX_REPLAY_LIST: {
int sel = mainGame->lstReplayList->getSelected();
if(sel == -1)
if(sel < 0)
break;
auto filename = mainGame->lstReplayList->getListItem(sel);
if (!filename)
break;
wchar_t replay_path[256]{};
myswprintf(replay_path, L"./replay/%ls", mainGame->lstReplayList->getListItem(sel));
myswprintf(replay_path, L"./replay/%ls", filename);
if (!temp_replay.OpenReplay(replay_path)) {
mainGame->stReplayInfo->setText(L"Error");
break;
......@@ -524,20 +531,25 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) {
wchar_t infobuf[256]{};
std::wstring repinfo;
time_t curtime;
if(temp_replay.pheader.flag & REPLAY_UNIFORM)
curtime = temp_replay.pheader.start_time;
else
curtime = temp_replay.pheader.seed;
const auto& rh = temp_replay.pheader.base;
if(temp_replay.pheader.base.flag & REPLAY_UNIFORM)
curtime = rh.start_time;
else{
curtime = rh.seed;
wchar_t version_info[256]{};
myswprintf(version_info, L"version 0x%X\n", rh.version);
repinfo.append(version_info);
}
std::wcsftime(infobuf, sizeof infobuf / sizeof infobuf[0], L"%Y/%m/%d %H:%M:%S\n", std::localtime(&curtime));
repinfo.append(infobuf);
if (temp_replay.pheader.flag & REPLAY_SINGLE_MODE) {
if (rh.flag & REPLAY_SINGLE_MODE) {
wchar_t path[256]{};
BufferIO::DecodeUTF8(temp_replay.script_name.c_str(), path);
repinfo.append(path);
repinfo.append(L"\n");
}
const auto& player_names = temp_replay.players;
if(temp_replay.pheader.flag & REPLAY_TAG)
if(rh.flag & REPLAY_TAG)
myswprintf(infobuf, L"%ls\n%ls\n===VS===\n%ls\n%ls\n", player_names[0].c_str(), player_names[1].c_str(), player_names[2].c_str(), player_names[3].c_str());
else
myswprintf(infobuf, L"%ls\n===VS===\n%ls\n", player_names[0].c_str(), player_names[1].c_str());
......@@ -598,6 +610,13 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) {
mainGame->cbBotDeck->setVisible(mainGame->botInfo[sel].select_deckfile);
break;
}
case LISTBOX_SERVER_LIST: {
int sel = mainGame->lstServerList->getSelected();
auto target = sel == -1 ? L"" : dataManager._serverStrings[sel].second.c_str();
BufferIO::CopyWideString(target, mainGame->gameConf.lasthost);
mainGame->ebJoinHost->setText(target);
break;
}
}
break;
}
......
......@@ -297,7 +297,11 @@ void NetServer::HandleCTOSPacket(DuelPlayer* dp, unsigned char* data, int len) {
case CTOS_CHAT: {
if(!dp->game)
return;
if (len < 1 + (int)sizeof(unsigned char))
if (len < 1 + sizeof(uint16_t) * 1)
return;
if (len > 1 + sizeof(uint16_t) * LEN_CHAT_MSG)
return;
if ((len - 1) % sizeof(uint16_t))
return;
duel_mode->Chat(dp, pdata, len - 1);
break;
......@@ -344,6 +348,15 @@ void NetServer::HandleCTOSPacket(DuelPlayer* dp, unsigned char* data, int len) {
BufferIO::CopyCharArray(pkt->name, dp->name);
break;
}
case CTOS_EXTERNAL_ADDRESS: {
// for other server & reverse proxy use only
/*
wchar_t hostname[LEN_HOSTNAME];
uint32_t real_ip = ntohl(BufferIO::ReadInt32(pdata));
BufferIO::CopyCharArray((uint16_t*)pdata, hostname);
*/
break;
}
case CTOS_CREATE_GAME: {
if(dp->game || duel_mode)
return;
......@@ -424,6 +437,14 @@ void NetServer::HandleCTOSPacket(DuelPlayer* dp, unsigned char* data, int len) {
duel_mode->ToObserver(dp);
break;
}
#ifdef YGOPRO_SERVER_MODE
case CTOS_HS_NOTREADY: {
if (!duel_mode || duel_mode->pduel)
return;
duel_mode->PlayerReady(dp, false);
break;
}
#else
case CTOS_HS_READY:
case CTOS_HS_NOTREADY: {
if (!duel_mode || duel_mode->pduel)
......@@ -431,6 +452,7 @@ void NetServer::HandleCTOSPacket(DuelPlayer* dp, unsigned char* data, int len) {
duel_mode->PlayerReady(dp, (CTOS_HS_NOTREADY - pktType) != 0);
break;
}
#endif
case CTOS_HS_KICK: {
if (!duel_mode || duel_mode->pduel)
return;
......@@ -459,8 +481,6 @@ void NetServer::HandleCTOSPacket(DuelPlayer* dp, unsigned char* data, int len) {
}
}
size_t NetServer::CreateChatPacket(unsigned char* src, int src_size, unsigned char* dst, uint16_t dst_player_type) {
if (!check_msg_size(src_size))
return 0;
uint16_t src_msg[LEN_CHAT_MSG];
std::memcpy(src_msg, src, src_size);
const int src_len = src_size / sizeof(uint16_t);
......
......@@ -58,8 +58,7 @@ public:
template<typename ST>
static void SendPacketToPlayer(DuelPlayer* dp, unsigned char proto, const ST& st) {
auto p = net_server_write;
if (sizeof(ST) > MAX_DATA_SIZE)
return;
static_assert(sizeof(ST) <= MAX_DATA_SIZE, "Packet size is too large.");
buffer_write<uint16_t>(p, (uint16_t)(1 + sizeof(ST)));
buffer_write<uint8_t>(p, proto);
std::memcpy(p, &st, sizeof(ST));
......
......@@ -103,6 +103,14 @@ struct CTOS_Kick {
check_trivially_copyable(CTOS_Kick);
static_assert(sizeof(CTOS_Kick) == 1, "size mismatch: CTOS_Kick");
/*
* CTOS_ExternalAddress
* uint32_t real_ip; (IPv4 address, BE, alway 0 in normal client)
* uint16_t hostname[256]; (UTF-16 string)
*/
constexpr int LEN_HOSTNAME = 256;
// STOC
struct STOC_ErrorMsg {
uint8_t msg{};
......@@ -196,17 +204,6 @@ struct DuelPlayer {
bufferevent* bev{};
};
inline bool check_msg_size(int size) {
// empty string is not allowed
if (size < 2 * sizeof(uint16_t))
return false;
if (size > LEN_CHAT_MSG * sizeof(uint16_t))
return false;
if (size % sizeof(uint16_t) != 0)
return false;
return true;
}
inline unsigned int GetPosition(unsigned char* qbuf, size_t offset) {
unsigned int info = 0;
std::memcpy(&info, qbuf + offset, sizeof info);
......@@ -249,6 +246,7 @@ public:
intptr_t pduel{};
wchar_t name[20]{};
wchar_t pass[20]{};
std::vector<byte> registry_dump;
};
}
......@@ -275,6 +273,7 @@ public:
#define CTOS_SURRENDER 0x14 // no data
#define CTOS_TIME_CONFIRM 0x15 // no data
#define CTOS_CHAT 0x16 // uint16_t array
#define CTOS_EXTERNAL_ADDRESS 0x17 // CTOS_ExternalAddress
#define CTOS_HS_TODUELIST 0x20 // no data
#define CTOS_HS_TOOBSERVER 0x21 // no data
#define CTOS_HS_READY 0x22 // no data
......@@ -298,7 +297,7 @@ public:
#define STOC_LEAVE_GAME 0x14 // reserved
#define STOC_DUEL_START 0x15 // no data
#define STOC_DUEL_END 0x16 // no data
#define STOC_REPLAY 0x17 // ReplayHeader + byte array
#define STOC_REPLAY 0x17 // ExtendedReplayHeader + byte array
#define STOC_TIME_LIMIT 0x18 // STOC_TimeLimit
#define STOC_CHAT 0x19 // uint16_t + uint16_t array
#define STOC_HS_PLAYER_ENTER 0x20 // STOC_HS_PlayerEnter
......
......@@ -45,7 +45,6 @@ end
else
project "ygopro"
kind "WindowedApp"
cppdialect "C++14"
rtti "Off"
openmp "On"
......@@ -54,11 +53,16 @@ project "ygopro"
links { "ocgcore", "clzma", "cspmemvfs", LUA_LIB_NAME, "sqlite3", "irrlicht", "freetype", "event" }
end
if not BUILD_LUA then
libdirs { LUA_LIB_DIR }
end
if BUILD_EVENT then
includedirs { "../event/include" }
else
includedirs { EVENT_INCLUDE_DIR }
libdirs { EVENT_LIB_DIR }
links { "event_pthreads" }
end
if BUILD_IRRLICHT then
......@@ -82,7 +86,7 @@ end
libdirs { SQLITE_LIB_DIR }
end
if USE_AUDIO then
if USE_AUDIO and not SERVER_MODE then
defines { "YGOPRO_USE_AUDIO" }
if AUDIO_LIB == "miniaudio" then
defines { "YGOPRO_USE_MINIAUDIO" }
......@@ -110,43 +114,53 @@ end
end
filter "system:windows"
entrypoint "mainCRTStartup"
defines { "_IRR_WCHAR_FILESYSTEM" }
files "ygopro.rc"
if SERVER_PRO2_SUPPORT then
targetname ("AI.Server")
end
if SERVER_MODE then
links { "ws2_32" }
links { "ws2_32", "iphlpapi" }
else
links { "opengl32", "ws2_32", "winmm", "gdi32", "kernel32", "user32", "imm32", "Dnsapi" }
links { "ws2_32", "Dnsapi", "iphlpapi" }
end
if USE_AUDIO and AUDIO_LIB == "irrklang" then
links { "irrKlang" }
if IRRKLANG_PRO then
defines { "IRRKLANG_STATIC" }
filter { "not configurations:Debug" }
filter { "system:windows", "not configurations:Debug" }
libdirs { IRRKLANG_PRO_RELEASE_LIB_DIR }
filter { "configurations:Debug" }
filter { "system:windows", "configurations:Debug" }
libdirs { IRRKLANG_PRO_DEBUG_LIB_DIR }
filter {}
end
end
if not SERVER_MODE then
filter "not system:windows"
links { "event_pthreads", "dl", "pthread", "resolv" }
links { "resolv" }
end
filter "not action:vs*"
cppdialect "C++14"
filter "system:macosx"
if not SERVER_MODE then
openmp "Off"
links { "z" }
links { "OpenGL.framework", "Cocoa.framework", "IOKit.framework" }
defines { "GL_SILENCE_DEPRECATION" }
end
if MAC_ARM then
buildoptions { "--target=arm64-apple-macos12" }
linkoptions { "-arch arm64" }
end
if MAC_INTEL then
linkoptions { "-arch x86_64" }
end
if USE_AUDIO and AUDIO_LIB == "irrklang" then
links { "irrklang" }
end
filter "system:linux"
links { "dl", "pthread" }
linkoptions { "-static-libstdc++", "-static-libgcc" }
if not SERVER_MODE then
links { "GL", "X11", "Xxf86vm" }
......
......@@ -48,7 +48,7 @@ void Replay::BeginRecord() {
char tmppath[40];
strftime(tmppath, 40, "./replay/%Y-%m-%d %H-%M-%S %%u.yrp", localedtime);
char path[40];
std::sprintf(path, tmppath, server_port);
sprintf(path, tmppath, server_port);
fp = myfopen(path, "wb");
#else
fp = myfopen("./replay/_LastReplay.yrp", "wb");
......@@ -62,7 +62,7 @@ void Replay::BeginRecord() {
Reset();
is_recording = true;
}
void Replay::WriteHeader(ReplayHeader& header) {
void Replay::WriteHeader(ExtendedReplayHeader& header) {
pheader = header;
#ifdef YGOPRO_SERVER_MODE
if(!(replay_mode & REPLAY_MODE_SAVE_IN_SERVER)) return;
......@@ -122,11 +122,11 @@ void Replay::EndRecord() {
#ifdef YGOPRO_SERVER_MODE
}
#endif
pheader.datasize = replay_size;
pheader.flag |= REPLAY_COMPRESSED;
pheader.base.datasize = replay_size;
pheader.base.flag |= REPLAY_COMPRESSED;
size_t propsize = 5;
comp_size = MAX_COMP_SIZE;
int ret = LzmaCompress(comp_data, &comp_size, replay_data, replay_size, pheader.props, &propsize, 5, 1 << 24, 3, 0, 2, 32, 1);
int ret = LzmaCompress(comp_data, &comp_size, replay_data, replay_size, pheader.base.props, &propsize, 5, 0x1U << 24, 3, 0, 2, 32, 1);
if (ret != SZ_OK) {
std::memcpy(comp_data, &ret, sizeof ret);
comp_size = sizeof ret;
......@@ -145,6 +145,7 @@ void Replay::SaveReplay(const wchar_t* name) {
std::fwrite(comp_data, comp_size, 1, rfp);
std::fclose(rfp);
}
#ifndef YGOPRO_SERVER_MODE
bool Replay::OpenReplay(const wchar_t* name) {
FILE* rfp = mywfopen(name, "rb");
if(!rfp) {
......@@ -156,19 +157,32 @@ bool Replay::OpenReplay(const wchar_t* name) {
return false;
Reset();
if(std::fread(&pheader, sizeof pheader, 1, rfp) < 1) {
bool correct_header = true;
if (std::fread(&pheader, sizeof pheader.base, 1, rfp) < 1)
correct_header = false;
else if (pheader.base.id != REPLAY_ID_YRP1 && pheader.base.id != REPLAY_ID_YRP2)
correct_header = false;
else if (pheader.base.version < 0x12d0u)
correct_header = false;
else if (pheader.base.version >= 0x1353u && !(pheader.base.flag & REPLAY_UNIFORM))
correct_header = false;
if (!correct_header) {
std::fclose(rfp);
return false;
}
if(pheader.flag & REPLAY_COMPRESSED) {
if (pheader.base.id == REPLAY_ID_YRP2 && std::fread(reinterpret_cast<unsigned char*>(&pheader) + sizeof pheader.base, sizeof pheader - sizeof pheader.base, 1, rfp) < 1) {
std::fclose(rfp);
return false;
}
if(pheader.base.flag & REPLAY_COMPRESSED) {
comp_size = std::fread(comp_data, 1, MAX_COMP_SIZE, rfp);
std::fclose(rfp);
if (pheader.datasize > MAX_REPLAY_SIZE)
if (pheader.base.datasize > MAX_REPLAY_SIZE)
return false;
replay_size = pheader.datasize;
if (LzmaUncompress(replay_data, &replay_size, comp_data, &comp_size, pheader.props, 5) != SZ_OK)
replay_size = pheader.base.datasize;
if (LzmaUncompress(replay_data, &replay_size, comp_data, &comp_size, pheader.base.props, 5) != SZ_OK)
return false;
if (replay_size != pheader.datasize) {
if (replay_size != pheader.base.datasize) {
replay_size = 0;
return false;
}
......@@ -187,17 +201,6 @@ bool Replay::OpenReplay(const wchar_t* name) {
data_position = 0;
return true;
}
bool Replay::CheckReplay(const wchar_t* name) {
wchar_t fname[256];
myswprintf(fname, L"./replay/%ls", name);
FILE* rfp = mywfopen(fname, "rb");
if(!rfp)
return false;
ReplayHeader rheader;
size_t count = std::fread(&rheader, sizeof rheader, 1, rfp);
std::fclose(rfp);
return count == 1 && rheader.id == 0x31707279 && rheader.version >= 0x12d0u && (rheader.version < 0x1353u || (rheader.flag & REPLAY_UNIFORM));
}
bool Replay::DeleteReplay(const wchar_t* name) {
wchar_t fname[256];
myswprintf(fname, L"./replay/%ls", name);
......@@ -236,7 +239,7 @@ bool Replay::ReadName(wchar_t* data) {
BufferIO::CopyWStr(buffer, data, 20);
return true;
}
void Replay::ReadHeader(ReplayHeader& header) {
void Replay::ReadHeader(ExtendedReplayHeader& header) {
header = pheader;
}
bool Replay::ReadData(void* data, size_t length) {
......@@ -258,6 +261,7 @@ void Replay::Rewind() {
data_position = 0;
can_read = true;
}
#endif // YGOPRO_SERVER_MODE
void Replay::Reset() {
is_recording = false;
is_replaying = false;
......@@ -271,6 +275,7 @@ void Replay::Reset() {
decks.clear();
script_name.clear();
}
#ifndef YGOPRO_SERVER_MODE
void Replay::SkipInfo(){
if (data_position == 0)
data_position += info_offset;
......@@ -279,7 +284,7 @@ bool Replay::IsReplaying() const {
return is_replaying;
}
bool Replay::ReadInfo() {
int player_count = (pheader.flag & REPLAY_TAG) ? 4 : 2;
int player_count = (pheader.base.flag & REPLAY_TAG) ? 4 : 2;
for (int i = 0; i < player_count; ++i) {
wchar_t name[20]{};
if (!ReadName(name))
......@@ -288,11 +293,11 @@ bool Replay::ReadInfo() {
}
if (!ReadData(&params, sizeof params))
return false;
bool is_tag1 = pheader.flag & REPLAY_TAG;
bool is_tag1 = pheader.base.flag & REPLAY_TAG;
bool is_tag2 = params.duel_flag & DUEL_TAG_MODE;
if (is_tag1 != is_tag2)
return false;
if (pheader.flag & REPLAY_SINGLE_MODE) {
if (pheader.base.flag & REPLAY_SINGLE_MODE) {
uint16_t slen = Read<uint16_t>();
char filename[256]{};
if (slen == 0 || slen > sizeof(filename) - 1)
......@@ -328,5 +333,6 @@ bool Replay::ReadInfo() {
}
return true;
}
#endif // YGOPRO_SERVER_MODE
}
......@@ -13,6 +13,9 @@ namespace ygo {
#define REPLAY_SINGLE_MODE 0x8
#define REPLAY_UNIFORM 0x10
#define REPLAY_ID_YRP1 0x31707279
#define REPLAY_ID_YRP2 0x32707279
// max size
constexpr int MAX_REPLAY_SIZE = 0x80000;
constexpr int MAX_COMP_SIZE = UINT16_MAX + 1;
......@@ -36,6 +39,15 @@ struct ReplayHeader {
uint8_t props[8]{};
};
struct ExtendedReplayHeader {
ReplayHeader base;
uint32_t seed_sequence[SEED_COUNT]{};
uint32_t header_version{ 1 };
uint32_t value1{};
uint32_t value2{};
uint32_t value3{};
};
struct DuelParameters {
int32_t start_lp{};
int32_t start_hand{};
......@@ -50,7 +62,7 @@ public:
// record
void BeginRecord();
void WriteHeader(ReplayHeader& header);
void WriteHeader(ExtendedReplayHeader& header);
void WriteData(const void* data, size_t length, bool flush = true);
template<typename T>
void Write(T data, bool flush = true) {
......@@ -62,7 +74,6 @@ public:
void SaveReplay(const wchar_t* name);
// play
static bool CheckReplay(const wchar_t* name);
static bool DeleteReplay(const wchar_t* name);
static bool RenameReplay(const wchar_t* oldname, const wchar_t* newname);
static size_t GetDeckPlayer(size_t deck_index) {
......@@ -75,10 +86,13 @@ public:
return deck_index;
}
}
#ifdef YGOPRO_SERVER_MODE
void Reset();
#else
bool OpenReplay(const wchar_t* name);
bool ReadNextResponse(unsigned char resp[]);
bool ReadName(wchar_t* data);
void ReadHeader(ReplayHeader& header);
void ReadHeader(ExtendedReplayHeader& header);
bool ReadData(void* data, size_t length);
template<typename T>
T Read() {
......@@ -91,13 +105,14 @@ public:
void Reset();
void SkipInfo();
bool IsReplaying() const;
#endif // YGOPRO_SERVER_MODE
FILE* fp{ nullptr };
#ifdef _WIN32
HANDLE recording_fp{ nullptr };
#endif
ReplayHeader pheader;
ExtendedReplayHeader pheader;
unsigned char* comp_data;
size_t comp_size{};
......@@ -108,7 +123,9 @@ public:
std::string script_name; // 2 bytes, script name (max: 256 bytes)
private:
#ifndef YGOPRO_SERVER_MODE
bool ReadInfo();
#endif
unsigned char* replay_data;
size_t replay_size{};
......
......@@ -2,7 +2,7 @@
#include "duelclient.h"
#include "game.h"
#include "data_manager.h"
#include "../ocgcore/mtrandom.h"
#include <random>
#include <thread>
namespace ygo {
......@@ -57,7 +57,7 @@ bool ReplayMode::ReadReplayResponse() {
return result;
}
int ReplayMode::ReplayThread() {
const ReplayHeader& rh = cur_replay.pheader;
const auto& rh = cur_replay.pheader.base;
mainGame->dInfo.Clear();
mainGame->dInfo.isFirst = true;
mainGame->dInfo.isTag = !!(rh.flag & REPLAY_TAG);
......@@ -155,9 +155,7 @@ int ReplayMode::ReplayThread() {
return 0;
}
bool ReplayMode::StartDuel() {
const ReplayHeader& rh = cur_replay.pheader;
unsigned int seed = rh.seed;
std::mt19937 rnd(seed);
const auto& rh = cur_replay.pheader.base;
cur_replay.SkipInfo();
if(rh.flag & REPLAY_TAG) {
BufferIO::CopyWideString(cur_replay.players[0].c_str(), mainGame->dInfo.hostname);
......@@ -168,7 +166,26 @@ bool ReplayMode::StartDuel() {
BufferIO::CopyWideString(cur_replay.players[0].c_str(), mainGame->dInfo.hostname);
BufferIO::CopyWideString(cur_replay.players[1].c_str(), mainGame->dInfo.clientname);
}
pduel = create_duel(rnd());
if(rh.id == REPLAY_ID_YRP1) {
std::mt19937 rnd(rh.seed);
pduel = create_duel(rnd());
} else {
pduel = create_duel_v2(cur_replay.pheader.seed_sequence);
}
mainGame->InjectEnvToRegistry(pduel);
set_registry_value(pduel, "duel_mode", rh.flag & REPLAY_TAG ? "tag" : "single");
set_registry_value(pduel, "start_lp", std::to_string(cur_replay.params.start_lp).c_str());
set_registry_value(pduel, "start_hand", std::to_string(cur_replay.params.start_hand).c_str());
set_registry_value(pduel, "draw_count", std::to_string(cur_replay.params.draw_count).c_str());
char player_name_buf_u[40];
char player_key_buf[23];
for(int i = 0; i < ((rh.flag & REPLAY_TAG) ? 4 : 2); ++i) {
BufferIO::EncodeUTF8(cur_replay.players[i].c_str(), player_name_buf_u);
std::snprintf(player_key_buf, sizeof(player_key_buf), "player_name_%d", i);
set_registry_value(pduel, player_key_buf, player_name_buf_u);
std::snprintf(player_key_buf, sizeof(player_key_buf), "player_type_%d", i);
set_registry_value(pduel, player_key_buf, std::to_string(i).c_str());
}
mainGame->dInfo.duel_rule = cur_replay.params.duel_flag >> 16;
set_player_info(pduel, 0, cur_replay.params.start_lp, cur_replay.params.start_hand, cur_replay.params.draw_count);
set_player_info(pduel, 1, cur_replay.params.start_lp, cur_replay.params.start_hand, cur_replay.params.draw_count);
......@@ -216,8 +233,6 @@ bool ReplayMode::StartDuel() {
return false;
}
}
if (!(rh.flag & REPLAY_UNIFORM))
cur_replay.params.duel_flag |= DUEL_OLD_REPLAY;
start_duel(pduel, cur_replay.params.duel_flag);
return true;
}
......@@ -417,7 +432,7 @@ bool ReplayMode::ReplayAnalyze(unsigned char* msg, unsigned int len) {
case MSG_SELECT_CHAIN: {
player = BufferIO::ReadUInt8(pbuf);
count = BufferIO::ReadUInt8(pbuf);
pbuf += 10 + count * 13;
pbuf += 9 + count * 14;
return ReadReplayResponse();
}
case MSG_SELECT_PLACE:
......@@ -470,6 +485,7 @@ bool ReplayMode::ReplayAnalyze(unsigned char* msg, unsigned int len) {
}
case MSG_CONFIRM_CARDS: {
player = BufferIO::ReadUInt8(pbuf);
pbuf += 1;
count = BufferIO::ReadUInt8(pbuf);
pbuf += count * 7;
DuelClient::ClientAnalyze(offset, pbuf - offset);
......
This diff is collapsed.
......@@ -70,10 +70,11 @@ protected:
unsigned char last_response{ 0 };
std::set<DuelPlayer*> observers;
#ifdef YGOPRO_SERVER_MODE
DuelPlayer* cache_recorder;
DuelPlayer* replay_recorder;
unsigned char turn_player;
unsigned short phase;
DuelPlayer* cache_recorder{};
DuelPlayer* replay_recorder{};
unsigned char turn_player{ 0 };
unsigned short phase{ 0 };
bool deck_reversed{ false };
#endif
Replay last_replay;
bool match_mode{ false };
......@@ -88,9 +89,9 @@ protected:
short time_limit[2]{};
short time_elapsed{ 0 };
#ifdef YGOPRO_SERVER_MODE
short time_compensator[2];
short time_backed[2];
unsigned char last_game_msg;
short time_compensator[2]{};
short time_backed[2]{};
unsigned char last_game_msg{ 0 };
#endif
};
......
......@@ -2,7 +2,7 @@
#include "duelclient.h"
#include "game.h"
#include "data_manager.h"
#include "../ocgcore/mtrandom.h"
#include <random>
#include <thread>
namespace ygo {
......@@ -36,12 +36,23 @@ int SingleMode::SinglePlayThread() {
mainGame->dInfo.Clear();
int opt = 0;
std::random_device rd;
unsigned int seed = rd();
mt19937 rnd((uint_fast32_t)seed);
ExtendedReplayHeader rh;
rh.base.id = REPLAY_ID_YRP2;
rh.base.version = PRO_VERSION;
rh.base.flag = REPLAY_UNIFORM | REPLAY_SINGLE_MODE;
rh.base.start_time = (uint32_t)std::time(nullptr);
for (auto& x : rh.seed_sequence)
x = rd();
std::seed_seq seed(rh.seed_sequence, rh.seed_sequence + SEED_COUNT);
std::mt19937 rnd(seed);
uint32_t duel_seed[SEED_COUNT]{};
for (auto& x : duel_seed)
x = rnd();
set_script_reader(DataManager::ScriptReaderEx);
set_card_reader(DataManager::CardReader);
set_message_handler(SingleMode::MessageHandler);
pduel = create_duel(rnd.rand());
pduel = create_duel_v2(duel_seed);
mainGame->InjectEnvToRegistry(pduel);
set_player_info(pduel, 0, start_lp, start_hand, draw_count);
set_player_info(pduel, 1, start_lp, start_hand, draw_count);
preload_script(pduel, "./script/special.lua");
......@@ -82,12 +93,6 @@ int SingleMode::SinglePlayThread() {
end_duel(pduel);
return 0;
}
ReplayHeader rh;
rh.id = 0x31707279;
rh.version = PRO_VERSION;
rh.flag = REPLAY_UNIFORM | REPLAY_SINGLE_MODE;
rh.seed = seed;
rh.start_time = (unsigned int)std::time(nullptr);
mainGame->gMutex.lock();
mainGame->HideElement(mainGame->wSinglePlay);
mainGame->ClearCardInfo();
......@@ -311,7 +316,7 @@ bool SingleMode::SinglePlayAnalyze(unsigned char* msg, unsigned int len) {
case MSG_SELECT_CHAIN: {
player = BufferIO::ReadUInt8(pbuf);
count = BufferIO::ReadUInt8(pbuf);
pbuf += 10 + count * 13;
pbuf += 9 + count * 14;
if(!DuelClient::ClientAnalyze(offset, pbuf - offset)) {
mainGame->singleSignal.Reset();
mainGame->singleSignal.Wait();
......@@ -388,6 +393,7 @@ bool SingleMode::SinglePlayAnalyze(unsigned char* msg, unsigned int len) {
}
case MSG_CONFIRM_CARDS: {
player = BufferIO::ReadUInt8(pbuf);
pbuf += 1;
count = BufferIO::ReadUInt8(pbuf);
pbuf += count * 7;
DuelClient::ClientAnalyze(offset, pbuf - offset);
......@@ -761,7 +767,7 @@ bool SingleMode::SinglePlayAnalyze(unsigned char* msg, unsigned int len) {
break;
}
case MSG_AI_NAME: {
char namebuf[128]{};
char namebuf[SIZE_AI_NAME]{};
wchar_t wname[20]{};
int name_len = buffer_read<uint16_t>(pbuf);
if (name_len + 1 <= (int)sizeof namebuf) {
......@@ -774,8 +780,8 @@ bool SingleMode::SinglePlayAnalyze(unsigned char* msg, unsigned int len) {
break;
}
case MSG_SHOW_HINT: {
char msgbuf[1024]{};
wchar_t msg[1024]{};
char msgbuf[SIZE_HINT_MSG]{};
wchar_t msg[SIZE_HINT_MSG]{};
int msg_len = buffer_read<uint16_t>(pbuf);
if (msg_len + 1 <= (int)sizeof msgbuf) {
std::memcpy(msgbuf, pbuf, msg_len);
......
......@@ -14,11 +14,8 @@ SoundManager soundManager;
bool SoundManager::Init() {
#ifdef YGOPRO_USE_AUDIO
bgm_scene = -1;
previous_bgm_scene = -1;
RefreshBGMList();
bgm_process = false;
rnd.reset((unsigned int)std::time(nullptr));
rnd.seed(std::random_device()());
#ifdef YGOPRO_USE_MINIAUDIO
engineConfig = ma_engine_config_init();
#ifdef YGOPRO_MINIAUDIO_SUPPORT_OPUS_VORBIS
......@@ -71,6 +68,7 @@ void SoundManager::RefreshBGMList() {
#endif
}
void SoundManager::RefershBGMDir(std::wstring path, int scene) {
#ifdef YGOPRO_USE_AUDIO
std::wstring search = L"./sound/BGM/" + path;
FileSystem::TraversalDir(search.c_str(), [this, &path, scene](const wchar_t* name, bool isdir) {
if(!isdir && (
......@@ -84,6 +82,7 @@ void SoundManager::RefershBGMDir(std::wstring path, int scene) {
BGMList[scene].push_back(filename);
}
});
#endif // YGOPRO_USE_AUDIO
}
void SoundManager::PlaySound(wchar_t* sound) {
#ifdef YGOPRO_USE_AUDIO
......@@ -254,8 +253,10 @@ void SoundManager::PlaySoundEffect(int sound) {
default:
break;
}
wchar_t soundNameW[32];
BufferIO::DecodeUTF8(soundName, soundNameW);
wchar_t soundPathW[40];
myswprintf(soundPathW, L"./sound/%s.wav", soundName);
myswprintf(soundPathW, L"./sound/%ls.wav", soundNameW);
PlaySound(soundPathW);
#endif // YGOPRO_USE_AUDIO
}
......@@ -335,7 +336,7 @@ void SoundManager::PlayBGM(int scene) {
if(count <= 0)
return;
bgm_scene = scene;
int bgm = rnd.get_random_integer(0, count -1);
int bgm = (count > 1) ? std::uniform_int_distribution<>(0, count - 1)(rnd) : 0;
auto name = BGMList[scene][bgm].c_str();
wchar_t BGMName[1024];
myswprintf(BGMName, L"./sound/BGM/%ls", name);
......
......@@ -2,7 +2,7 @@
#define SOUNDMANAGER_H
#include "game.h"
#include "../ocgcore/mtrandom.h"
#include <random>
#ifdef YGOPRO_USE_MINIAUDIO
#include <miniaudio.h>
#endif
......@@ -15,10 +15,10 @@ namespace ygo {
class SoundManager {
private:
std::vector<std::wstring> BGMList[9];
int bgm_scene{};
int previous_bgm_scene{};
bool bgm_process;
mt19937 rnd;
int bgm_scene{ -1 };
int previous_bgm_scene{ -1 };
bool bgm_process { false };
std::mt19937 rnd;
#ifdef YGOPRO_USE_MINIAUDIO
ma_engine_config engineConfig;
#ifdef YGOPRO_MINIAUDIO_SUPPORT_OPUS_VORBIS
......
This diff is collapsed.
This diff is collapsed.
Subproject commit ab7ef30f678a7356e0fca85b0091fcdf56581b35
Subproject commit 326c22e9bd5af2e0f295eb263c095584342db952
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Subproject commit 52007f6939ec3b27238ff705e5364d6e6984e033
Subproject commit 49961747bbe53583deb08f969451805851a96919
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment