forked from mirror/openmw-tes3mp
Compare commits
No commits in common. "0.7.0" and "move" have entirely different histories.
2006 changed files with 20360 additions and 121573 deletions
|
@ -1,16 +0,0 @@
|
||||||
root = true
|
|
||||||
|
|
||||||
[*.cpp]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 4
|
|
||||||
insert_final_newline = true
|
|
||||||
|
|
||||||
[*.hpp]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 4
|
|
||||||
insert_final_newline = true
|
|
||||||
|
|
||||||
[*.glsl]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 4
|
|
||||||
insert_final_newline = false
|
|
36
.gitignore
vendored
36
.gitignore
vendored
|
@ -8,12 +8,9 @@ makefile
|
||||||
build*
|
build*
|
||||||
prebuilt
|
prebuilt
|
||||||
|
|
||||||
##windows build process
|
|
||||||
/deps
|
|
||||||
/MSVC*
|
|
||||||
|
|
||||||
## doxygen
|
## doxygen
|
||||||
Doxygen
|
Doxygen
|
||||||
|
!docs/cs-manual/Makefile
|
||||||
|
|
||||||
## ides/editors
|
## ides/editors
|
||||||
*~
|
*~
|
||||||
|
@ -25,10 +22,6 @@ Doxygen
|
||||||
.project
|
.project
|
||||||
.settings
|
.settings
|
||||||
.directory
|
.directory
|
||||||
.idea
|
|
||||||
cmake-build-*
|
|
||||||
files/windows/*.aps
|
|
||||||
cmake-build-*
|
|
||||||
## qt-creator
|
## qt-creator
|
||||||
CMakeLists.txt.user*
|
CMakeLists.txt.user*
|
||||||
|
|
||||||
|
@ -41,36 +34,15 @@ resources
|
||||||
|
|
||||||
## binaries
|
## binaries
|
||||||
/esmtool
|
/esmtool
|
||||||
|
/mwiniimport
|
||||||
|
/omwlauncher
|
||||||
/openmw
|
/openmw
|
||||||
/opencs
|
/opencs
|
||||||
/niftest
|
/niftest
|
||||||
/bsatool
|
|
||||||
/openmw-cs
|
|
||||||
/openmw-essimporter
|
|
||||||
/openmw-iniimporter
|
|
||||||
/openmw-launcher
|
|
||||||
/openmw-wizard
|
|
||||||
|
|
||||||
## generated objects
|
## generated objects
|
||||||
apps/openmw/config.hpp
|
apps/openmw/config.hpp
|
||||||
apps/launcher/ui_contentselector.h
|
|
||||||
apps/launcher/ui_settingspage.h
|
|
||||||
apps/opencs/ui_contentselector.h
|
|
||||||
apps/opencs/ui_filedialog.h
|
|
||||||
apps/wizard/qrc_wizard.cxx
|
|
||||||
apps/wizard/ui_componentselectionpage.h
|
|
||||||
apps/wizard/ui_conclusionpage.h
|
|
||||||
apps/wizard/ui_existinginstallationpage.h
|
|
||||||
apps/wizard/ui_importpage.h
|
|
||||||
apps/wizard/ui_installationpage.h
|
|
||||||
apps/wizard/ui_installationtargetpage.h
|
|
||||||
apps/wizard/ui_intropage.h
|
|
||||||
apps/wizard/ui_languageselectionpage.h
|
|
||||||
apps/wizard/ui_methodselectionpage.h
|
|
||||||
components/ui_contentselector.h
|
|
||||||
docs/mainpage.hpp
|
docs/mainpage.hpp
|
||||||
docs/Doxyfile
|
|
||||||
docs/DoxyfilePages
|
|
||||||
moc_*.cxx
|
moc_*.cxx
|
||||||
*.cxx_parameters
|
*.cxx_parameters
|
||||||
*qrc_launcher.cxx
|
*qrc_launcher.cxx
|
||||||
|
@ -82,5 +54,3 @@ moc_*.cxx
|
||||||
*ui_playpage.h
|
*ui_playpage.h
|
||||||
*.[ao]
|
*.[ao]
|
||||||
*.so
|
*.so
|
||||||
openmw.appdata.xml
|
|
||||||
venv/
|
|
||||||
|
|
|
@ -1,69 +0,0 @@
|
||||||
stages:
|
|
||||||
- build
|
|
||||||
|
|
||||||
Debian:
|
|
||||||
tags:
|
|
||||||
- docker
|
|
||||||
- linux
|
|
||||||
image: gcc
|
|
||||||
cache:
|
|
||||||
key: apt-cache
|
|
||||||
paths:
|
|
||||||
- apt-cache/
|
|
||||||
before_script:
|
|
||||||
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
|
|
||||||
- apt-get update -yq
|
|
||||||
- apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt4-dev libopenal-dev libopenscenegraph-3.4-dev libunshield-dev libtinyxml-dev
|
|
||||||
# - apt-get install -y libmygui-dev libbullet-dev # to be updated to latest below because stretch is too old
|
|
||||||
- curl http://ftp.us.debian.org/debian/pool/main/b/bullet/libbullet-dev_2.87+dfsg-2_amd64.deb -o libbullet-dev_2.87+dfsg-2_amd64.deb
|
|
||||||
- curl http://ftp.us.debian.org/debian/pool/main/b/bullet/libbullet2.87_2.87+dfsg-2_amd64.deb -o libbullet2.87_2.87+dfsg-2_amd64.deb
|
|
||||||
- curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb -o libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb
|
|
||||||
- curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb -o libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb
|
|
||||||
- curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmygui-dev_3.2.2+dfsg-1_amd64.deb -o libmygui-dev_3.2.2+dfsg-1_amd64.deb
|
|
||||||
- dpkg --ignore-depends=libmygui.ogreplatform0debian1v5 -i *.deb
|
|
||||||
stage: build
|
|
||||||
script:
|
|
||||||
- cores_to_use=$((`nproc`-2)); if (( $cores_to_use < 1 )); then cores_to_use=1; fi
|
|
||||||
- mkdir build; cd build; cmake -DCMAKE_BUILD_TYPE=MinSizeRel ../
|
|
||||||
- make -j$cores_to_use
|
|
||||||
- DESTDIR=artifacts make install
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- build/artifacts/
|
|
||||||
MacOS:
|
|
||||||
tags:
|
|
||||||
- macos
|
|
||||||
- xcode
|
|
||||||
except:
|
|
||||||
- branches # because our CI VMs are not public, MRs can't use them and timeout
|
|
||||||
stage: build
|
|
||||||
allow_failure: true
|
|
||||||
script:
|
|
||||||
- rm -fr build/* # remove anything in the build directory
|
|
||||||
- CI/before_install.osx.sh
|
|
||||||
- CI/before_script.osx.sh
|
|
||||||
- cd build; make -j2 package
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- build/OpenMW-*.dmg
|
|
||||||
|
|
||||||
Windows:
|
|
||||||
tags:
|
|
||||||
- win10
|
|
||||||
- msvc2017
|
|
||||||
except:
|
|
||||||
- branches # because our CI VMs are not public, MRs can't use them and timeout
|
|
||||||
stage: build
|
|
||||||
allow_failure: true
|
|
||||||
script:
|
|
||||||
# - env # turn on for debugging
|
|
||||||
- sh %CI_PROJECT_DIR%/CI/before_script.msvc.sh -c Release -p x64 -v 2017 -V
|
|
||||||
- SET msBuildLocation="C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\msbuild.exe"
|
|
||||||
- call %msBuildLocation% MSVC2017_64\OpenMW.sln /t:Build /p:Configuration=Release /m:%NUMBER_OF_PROCESSORS%
|
|
||||||
- 7z a OpenMW_MSVC2017_64_%CI_BUILD_REF_NAME%_%CI_BUILD_ID%.zip %CI_PROJECT_DIR%\MSVC2017_64\Release\
|
|
||||||
cache:
|
|
||||||
paths:
|
|
||||||
- deps
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- "*.zip"
|
|
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,3 +0,0 @@
|
||||||
[submodule "extern/breakpad"]
|
|
||||||
path = extern/breakpad
|
|
||||||
url = https://chromium.googlesource.com/breakpad/breakpad
|
|
87
.travis.yml
87
.travis.yml
|
@ -1,92 +1,59 @@
|
||||||
os:
|
os:
|
||||||
- linux
|
- linux
|
||||||
# - osx
|
- osx
|
||||||
osx_image: xcode9.4
|
osx_image: xcode7.2
|
||||||
language: cpp
|
language: cpp
|
||||||
sudo: required
|
sudo: required
|
||||||
dist: xenial
|
dist: trusty
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
- coverity_scan
|
- coverity_scan
|
||||||
- /openmw-.*$/
|
- /openmw-.*$/
|
||||||
- /^[0-9]+\.[0-9]+\.[0-9]+.*$/
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
||||||
# via the "travis encrypt" command using the project repo's public key
|
# via the "travis encrypt" command using the project repo's public key
|
||||||
- secure: 1QK0yVyoOB+gf2I7XzvhXu9w/5lq4stBXIwJbVCTjz4Q4XVHCosURaW1MAgKzMrPnbFEwjyn5uQ8BwsvvfkuN1AZD0YXITgc7gyI+J1wQ/p/ljxRxglakU6WEgsTs2J5z9UmGac4YTXg+quK7YP3rv+zuGim2I2rhzImejyzp0Ym3kRCnNcy+SGBsiRaevRJMe00Ch8zGAbEhduQGeSoS6W0rcu02DNlQKiq5NktWsXR+TWWWVfIeIlQR/lbPsCd0pdxMaMv2QCY0rVbwrYxWJwr/Qe45dAdWp+8/C3PbXpeMSGxlLa33nJNX4Lf/djxbjm8KWk6edaXPajrjR/0iwcpwq0jg2Jt6XfEdnJt35F1gpXlc04sxStjG45uloOKCFYT0wdhIO1Lq+hDP54wypQl+JInd5qC001O7pwhVxO36EgKWqo8HD+BqGDBwsNj2engy9Qcp3wO6G0rLBPB3CrZsk9wrHVv5cSiQSLMhId3Xviu3ZI2qEDA+kgTvxrKrsnMj4bILVCyG5Ka2Mj22wIDW9e8oIab9oTdujax3DTN1GkD6QuOAGzwDsNwGASsgfoeZ+FUhgM75RlBWGMilgkmnF7EJ0oAXLEpjtABnEr2d4qHv+y08kOuTDBLB9ExzCIj024dYYYNLZrqPKx0ncHuCMG2QNj2aJAJEZtj1rQ=
|
- secure: "jybGzAdUbqt9vWR/GEnRd96BgAi/7Zd1+2HK68j/i/8+/1YH2XxLOy4Jv/DUBhBlJIkxs/Xv8dRcUlFOclZDHX1d/9Qnsqd3oUVkD7k1y7cTOWy9TBQaE/v/kZo3LpzA3xPwwthrb0BvqIbOfIELi5fS5s8ba85WFRg3AX70wWE="
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
apt:
|
|
||||||
sources:
|
|
||||||
- sourceline: 'ppa:openmw/openmw'
|
|
||||||
- sourceline: 'ppa:rakhimov/boost'
|
|
||||||
- ubuntu-toolchain-r-test
|
|
||||||
packages: [
|
|
||||||
# Dev
|
|
||||||
cmake, clang-6.0, libunshield-dev, libtinyxml-dev,
|
|
||||||
g++-8,
|
|
||||||
# Tests
|
|
||||||
libgtest-dev, google-mock,
|
|
||||||
# Boost
|
|
||||||
libboost-filesystem1.61-dev, libboost-program-options1.61-dev, libboost-system1.61-dev,
|
|
||||||
# FFmpeg
|
|
||||||
libavcodec-dev, libavformat-dev, libavutil-dev, libswscale-dev,
|
|
||||||
# Audio & Video
|
|
||||||
libsdl2-dev, qtbase5-dev, libopenal-dev,
|
|
||||||
# The other ones from OpenMW ppa
|
|
||||||
libbullet-dev, libswresample-dev, libopenscenegraph-3.4-dev, libmygui-dev,
|
|
||||||
# tes3mp stuff
|
|
||||||
libboost1.61-dev, libqt5opengl5-dev, libluajit-5.1-dev
|
|
||||||
]
|
|
||||||
|
|
||||||
coverity_scan:
|
coverity_scan:
|
||||||
project:
|
project:
|
||||||
name: "TES3MP/openmw-tes3mp"
|
name: "OpenMW/openmw"
|
||||||
description: "<Your project description here>"
|
description: "<Your project description here>"
|
||||||
notification_email: koncord@tes3mp.com
|
notification_email: scrawl@baseoftrash.de
|
||||||
build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE -DBUILD_OPENCS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_LAUNCHER=FALSE"
|
build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE"
|
||||||
build_command: "make -j3"
|
build_command: "make -j2"
|
||||||
branch_pattern: coverity_scan
|
branch_pattern: coverity_scan
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: linux
|
- os: linux
|
||||||
env:
|
env:
|
||||||
- ANALYZE="scan-build-6.0 --use-cc clang-6.0 --use-c++ clang++-6.0 "
|
ANALYZE="scan-build-3.6 --use-cc clang-3.6 --use-c++ clang++-3.6 "
|
||||||
- MATRIX_CC="CC=clang-6.0 && CXX=clang++-6.0"
|
|
||||||
compiler: clang
|
compiler: clang
|
||||||
- os: linux
|
|
||||||
env:
|
|
||||||
- MATRIX_CC="CC=gcc-8 && CXX=g++-8"
|
|
||||||
- os: linux
|
|
||||||
env:
|
|
||||||
- MATRIX_CC="CC=clang-6.0 && CXX=clang++-6.0"
|
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- env:
|
- env: ANALYZE="scan-build-3.6 --use-cc clang-3.6 --use-c++ clang++-3.6 "
|
||||||
- MATRIX_CC="CC=clang-6.0 && CXX=clang++-6.0"
|
|
||||||
- env:
|
|
||||||
- ANALYZE="scan-build-6.0 --use-cc clang-6.0 --use-c++ clang++-6.0 "
|
|
||||||
- MATRIX_CC="CC=clang-6.0 && CXX=clang++-6.0"
|
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- ./CI/before_install.${TRAVIS_OS_NAME}.sh
|
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./CI/before_install.linux.sh; fi
|
||||||
before_script: ./CI/before_script.${TRAVIS_OS_NAME}.sh
|
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ./CI/before_install.osx.sh; fi
|
||||||
|
before_script:
|
||||||
|
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./CI/before_script.linux.sh; fi
|
||||||
|
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then ./CI/before_script.osx.sh; fi
|
||||||
script:
|
script:
|
||||||
- cd ./build
|
- cd ./build
|
||||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then ${ANALYZE}make -j3; fi
|
- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then ${ANALYZE}make -j3; fi
|
||||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi
|
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi
|
||||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi
|
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi
|
||||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
|
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
|
||||||
#notifications:
|
notifications:
|
||||||
# email:
|
recipients:
|
||||||
# recipients:
|
- corrmage+travis-ci@gmail.com
|
||||||
# - corrmage+travis-ci@gmail.com
|
email:
|
||||||
# on_success: change
|
on_success: change
|
||||||
# on_failure: always
|
on_failure: always
|
||||||
# irc:
|
irc:
|
||||||
# channels:
|
channels:
|
||||||
# - "chat.freenode.net#openmw"
|
- "chat.freenode.net#openmw"
|
||||||
# on_success: change
|
on_success: change
|
||||||
# on_failure: always
|
on_failure: always
|
||||||
# use_notice: true
|
use_notice: true
|
||||||
|
|
76
AUTHORS.md
76
AUTHORS.md
|
@ -15,39 +15,23 @@ Programmers
|
||||||
Adam Hogan (aurix)
|
Adam Hogan (aurix)
|
||||||
Aesylwinn
|
Aesylwinn
|
||||||
aegis
|
aegis
|
||||||
AHSauge
|
|
||||||
Aleksandar Jovanov
|
Aleksandar Jovanov
|
||||||
Alex Haddad (rainChu)
|
Alex Haddad (rainChu)
|
||||||
Alex McKibben
|
Alex McKibben (WeirdSexy)
|
||||||
alexanderkjall
|
|
||||||
Alexander Nadeau (wareya)
|
Alexander Nadeau (wareya)
|
||||||
Alexander Olofsson (Ace)
|
Alexander Olofsson (Ace)
|
||||||
Alex S (docwest)
|
|
||||||
Allofich
|
|
||||||
Andrei Kortunov (akortunov)
|
|
||||||
AnyOldName3
|
|
||||||
Aussiemon
|
|
||||||
Austin Salgat (Salgat)
|
|
||||||
Artem Kotsynyak (greye)
|
Artem Kotsynyak (greye)
|
||||||
artemutin
|
artemutin
|
||||||
Arthur Moore (EmperorArthur)
|
Arthur Moore (EmperorArthur)
|
||||||
Assumeru
|
|
||||||
athile
|
athile
|
||||||
Ben Shealy (bentsherman)
|
|
||||||
Bret Curtis (psi29a)
|
Bret Curtis (psi29a)
|
||||||
Britt Mathis (galdor557)
|
Britt Mathis (galdor557)
|
||||||
Capostrophic
|
|
||||||
cc9cii
|
cc9cii
|
||||||
Cédric Mocquillon
|
|
||||||
Chris Boyce (slothlife)
|
Chris Boyce (slothlife)
|
||||||
Chris Robinson (KittyCat)
|
Chris Robinson (KittyCat)
|
||||||
Cory F. Cohen (cfcohen)
|
Cory F. Cohen (cfcohen)
|
||||||
Cris Mihalache (Mirceam)
|
Cris Mihalache (Mirceam)
|
||||||
crussell187
|
|
||||||
DanielVukelich
|
|
||||||
darkf
|
darkf
|
||||||
David Cernat (davidcernat)
|
|
||||||
devnexen
|
|
||||||
Dieho
|
Dieho
|
||||||
Dmitry Shkurskiy (endorph)
|
Dmitry Shkurskiy (endorph)
|
||||||
Douglas Diniz (Dgdiniz)
|
Douglas Diniz (Dgdiniz)
|
||||||
|
@ -57,23 +41,18 @@ Programmers
|
||||||
Edmondo Tommasina (edmondo)
|
Edmondo Tommasina (edmondo)
|
||||||
Eduard Cot (trombonecot)
|
Eduard Cot (trombonecot)
|
||||||
Eli2
|
Eli2
|
||||||
elsid
|
|
||||||
Emanuel Guével (potatoesmaster)
|
Emanuel Guével (potatoesmaster)
|
||||||
eroen
|
eroen
|
||||||
escondida
|
escondida
|
||||||
Evgeniy Mineev (sandstranger)
|
Evgeniy Mineev (sandstranger)
|
||||||
Federico Guerra (FedeWar)
|
|
||||||
Fil Krynicki (filkry)
|
Fil Krynicki (filkry)
|
||||||
Finbar Crago (finbar-crago)
|
|
||||||
Florian Weber (Florianjw)
|
|
||||||
Gašper Sedej
|
Gašper Sedej
|
||||||
gugus/gus
|
gugus/gus
|
||||||
Hallfaer Tuilinn
|
Hallfaer Tuilinn
|
||||||
Haoda Wang (h313)
|
|
||||||
hristoast
|
hristoast
|
||||||
Internecine
|
Internecine
|
||||||
Jacob Essex (Yacoby)
|
Jacob Essex (Yacoby)
|
||||||
Jake Westrip (16bitint)
|
Jannik Heller (scrawl)
|
||||||
Jason Hooks (jhooks)
|
Jason Hooks (jhooks)
|
||||||
jeaye
|
jeaye
|
||||||
Jeffrey Haines (Jyby)
|
Jeffrey Haines (Jyby)
|
||||||
|
@ -84,20 +63,13 @@ Programmers
|
||||||
John Blomberg (fstp)
|
John Blomberg (fstp)
|
||||||
Jordan Ayers
|
Jordan Ayers
|
||||||
Jordan Milne
|
Jordan Milne
|
||||||
Jules Blok (Armada651)
|
|
||||||
julianko
|
|
||||||
Julien Voisin (jvoisin/ap0)
|
Julien Voisin (jvoisin/ap0)
|
||||||
Karl-Felix Glatzer (k1ll)
|
Karl-Felix Glatzer (k1ll)
|
||||||
Kevin Poitra (PuppyKevin)
|
Kevin Poitra (PuppyKevin)
|
||||||
Koncord
|
Koncord
|
||||||
Kurnevsky Evgeny (kurnevsky)
|
|
||||||
Lars Söderberg (Lazaroth)
|
Lars Söderberg (Lazaroth)
|
||||||
lazydev
|
lazydev
|
||||||
Leon Krieg (lkrieg)
|
|
||||||
Leon Saunders (emoose)
|
Leon Saunders (emoose)
|
||||||
Łukasz Gołębiewski (lukago)
|
|
||||||
logzero
|
|
||||||
lohikaarme
|
|
||||||
Lukasz Gromanowski (lgro)
|
Lukasz Gromanowski (lgro)
|
||||||
Manuel Edelmann (vorenon)
|
Manuel Edelmann (vorenon)
|
||||||
Marc Bouvier (CramitDeFrog)
|
Marc Bouvier (CramitDeFrog)
|
||||||
|
@ -105,7 +77,6 @@ Programmers
|
||||||
Mark Siewert (mark76)
|
Mark Siewert (mark76)
|
||||||
Marco Melletti (mellotanica)
|
Marco Melletti (mellotanica)
|
||||||
Marco Schulze
|
Marco Schulze
|
||||||
Martin Otto (MAtahualpa)
|
|
||||||
Mateusz Kołaczek (PL_kolek)
|
Mateusz Kołaczek (PL_kolek)
|
||||||
Mateusz Malisz (malice)
|
Mateusz Malisz (malice)
|
||||||
megaton
|
megaton
|
||||||
|
@ -113,59 +84,40 @@ Programmers
|
||||||
Michael Mc Donnell
|
Michael Mc Donnell
|
||||||
Michael Papageorgiou (werdanith)
|
Michael Papageorgiou (werdanith)
|
||||||
Michał Bień (Glorf)
|
Michał Bień (Glorf)
|
||||||
Michał Moroz (dragonee)
|
|
||||||
Miloslav Číž (drummyfish)
|
|
||||||
Miroslav Puda (pakanek)
|
Miroslav Puda (pakanek)
|
||||||
MiroslavR
|
MiroslavR
|
||||||
Mitchell Schwitzer (schwitzerm)
|
|
||||||
naclander
|
naclander
|
||||||
Narmo
|
Narmo
|
||||||
Nathan Jeffords (blunted2night)
|
Nathan Jeffords (blunted2night)
|
||||||
NeveHanter
|
NeveHanter
|
||||||
Nialsy
|
|
||||||
Nikolay Kasyanov (corristo)
|
Nikolay Kasyanov (corristo)
|
||||||
nobrakal
|
nobrakal
|
||||||
Nolan Poe (nopoe)
|
Nolan Poe (nopoe)
|
||||||
Oleg Chkan (mrcheko)
|
|
||||||
Paul Cercueil (pcercuei)
|
Paul Cercueil (pcercuei)
|
||||||
Paul McElroy (Greendogo)
|
Paul McElroy (Greendogo)
|
||||||
Pi03k
|
Pi03k
|
||||||
Pieter van der Kloet (pvdk)
|
Pieter van der Kloet (pvdk)
|
||||||
pkubik
|
pkubik
|
||||||
PlutonicOverkill
|
|
||||||
Radu-Marius Popovici (rpopovici)
|
Radu-Marius Popovici (rpopovici)
|
||||||
Rafael Moura (dhustkoder)
|
rcutmore
|
||||||
rdimesio
|
rdimesio
|
||||||
rexelion
|
|
||||||
riothamus
|
riothamus
|
||||||
Rob Cutmore (rcutmore)
|
|
||||||
Robert MacGregor (Ragora)
|
Robert MacGregor (Ragora)
|
||||||
Rohit Nirmal
|
Rohit Nirmal
|
||||||
Roman Melnik (Kromgart)
|
Roman Melnik (Kromgart)
|
||||||
Roman Proskuryakov (kpp)
|
Roman Proskuryakov (humbug)
|
||||||
Roman Siromakha (elsid)
|
|
||||||
Sandy Carter (bwrsandman)
|
Sandy Carter (bwrsandman)
|
||||||
Scott Howard
|
Scott Howard
|
||||||
scrawl
|
|
||||||
Sebastian Wick (swick)
|
Sebastian Wick (swick)
|
||||||
Sergey Shambir
|
Sergey Shambir
|
||||||
ShadowRadiance
|
|
||||||
Siimacore
|
|
||||||
sir_herrbatka
|
sir_herrbatka
|
||||||
smbas
|
smbas
|
||||||
spycrab
|
|
||||||
Stefan Galowicz (bogglez)
|
Stefan Galowicz (bogglez)
|
||||||
Stanislav Bobrov (Jiub)
|
Stanislav Bobrov (Jiub)
|
||||||
stil-t
|
|
||||||
svaante
|
|
||||||
Sylvain Thesnieres (Garvek)
|
Sylvain Thesnieres (Garvek)
|
||||||
t6
|
t6
|
||||||
terrorfisch
|
terrorfisch
|
||||||
thegriglat
|
|
||||||
Thomas Luppi (Digmaster)
|
Thomas Luppi (Digmaster)
|
||||||
tri4ng1e
|
|
||||||
unelsson
|
|
||||||
Will Herrmann (Thunderforge)
|
|
||||||
Tom Mason (wheybags)
|
Tom Mason (wheybags)
|
||||||
Torben Leif Carrington (TorbenC)
|
Torben Leif Carrington (TorbenC)
|
||||||
viadanna
|
viadanna
|
||||||
|
@ -173,22 +125,19 @@ Programmers
|
||||||
vocollapse
|
vocollapse
|
||||||
zelurker
|
zelurker
|
||||||
|
|
||||||
Documentation
|
Manual
|
||||||
-------------
|
------
|
||||||
|
|
||||||
Adam Bowen (adamnbowen)
|
|
||||||
Alejandro Sanchez (HiPhish)
|
|
||||||
Bodillium
|
Bodillium
|
||||||
Bret Curtis (psi29a)
|
|
||||||
Cramal
|
Cramal
|
||||||
Ryan Tucker (Ravenwing)
|
Alejandro Sanchez (HiPhish)
|
||||||
sir_herrbatka
|
sir_herrbatka
|
||||||
|
|
||||||
Packagers
|
Packagers
|
||||||
---------
|
---------
|
||||||
|
|
||||||
Alexander Olofsson (Ace) - Windows
|
Alexander Olofsson (Ace) - Windows
|
||||||
Bret Curtis (psi29a) - Debian and Ubuntu Linux
|
Bret Curtis (psi29a) - Ubuntu Linux
|
||||||
Edmondo Tommasina (edmondo) - Gentoo Linux
|
Edmondo Tommasina (edmondo) - Gentoo Linux
|
||||||
Julian Ospald (hasufell) - Gentoo Linux
|
Julian Ospald (hasufell) - Gentoo Linux
|
||||||
Karl-Felix Glatzer (k1ll) - Linux Binaries
|
Karl-Felix Glatzer (k1ll) - Linux Binaries
|
||||||
|
@ -199,16 +148,16 @@ Packagers
|
||||||
Public Relations and Translations
|
Public Relations and Translations
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
|
Alex McKibben (WeirdSexy) - Podcaster
|
||||||
Artem Kotsynyak (greye) - Russian News Writer
|
Artem Kotsynyak (greye) - Russian News Writer
|
||||||
Dawid Lakomy (Vedyimyn) - Polish News Writer
|
|
||||||
Jim Clauwaert (Zedd) - Public Outreach
|
Jim Clauwaert (Zedd) - Public Outreach
|
||||||
Julien Voisin (jvoisin/ap0) - French News Writer
|
Julien Voisin (jvoisin/ap0) - French News Writer
|
||||||
|
Tom Koenderink (Okulo) - English News Writer
|
||||||
Lukasz Gromanowski (lgro) - English News Writer
|
Lukasz Gromanowski (lgro) - English News Writer
|
||||||
Martin Otto (Atahualpa) - Podcaster, Public Outreach, German Translator
|
|
||||||
Mickey Lyle (raevol) - Release Manager
|
Mickey Lyle (raevol) - Release Manager
|
||||||
Pithorn - Chinese News Writer
|
Pithorn - Chinese News Writer
|
||||||
sir_herrbatka - Polish News Writer
|
sir_herrbatka - Polish News Writer
|
||||||
Tom Koenderink (Okulo) - English News Writer
|
Dawid Lakomy (Vedyimyn) - Polish News Writer
|
||||||
|
|
||||||
Website
|
Website
|
||||||
-------
|
-------
|
||||||
|
@ -235,7 +184,7 @@ Artwork
|
||||||
|
|
||||||
Necrod - OpenMW Logo
|
Necrod - OpenMW Logo
|
||||||
Mickey Lyle (raevol) - Wordpress Theme
|
Mickey Lyle (raevol) - Wordpress Theme
|
||||||
Tom Koenderink (Okulo), SirHerrbatka, crysthala, Shnatsel, Lamoot - OpenMW Editor Icons
|
Tom Koenderink (Okulo), SirHerrbatka, crysthala, Shnatsel - OpenMW Editor Icons
|
||||||
|
|
||||||
Inactive Contributors
|
Inactive Contributors
|
||||||
---------------------
|
---------------------
|
||||||
|
@ -259,6 +208,7 @@ Inactive Contributors
|
||||||
Nekochan
|
Nekochan
|
||||||
pchan3
|
pchan3
|
||||||
penguinroad
|
penguinroad
|
||||||
|
psi29a
|
||||||
sergoz
|
sergoz
|
||||||
spyboot
|
spyboot
|
||||||
Star-Demon
|
Star-Demon
|
||||||
|
|
653
CHANGELOG.md
653
CHANGELOG.md
|
@ -1,656 +1,3 @@
|
||||||
0.45.0
|
|
||||||
------
|
|
||||||
|
|
||||||
Bug #1990: Sunrise/sunset not set correct
|
|
||||||
Bug #2131: Lustidrike's spell misses the player every time
|
|
||||||
Bug #2222: Fatigue's effect on selling price is backwards
|
|
||||||
Bug #2326: After a bound item expires the last equipped item of that type is not automatically re-equipped
|
|
||||||
Bug #2455: Creatures attacks degrade armor
|
|
||||||
Bug #2562: Forcing AI to activate a teleport door sometimes causes a crash
|
|
||||||
Bug #2626: Resurrecting the player does not resume the game
|
|
||||||
Bug #2772: Non-existing class or faction freezes the game
|
|
||||||
Bug #2835: Player able to slowly move when overencumbered
|
|
||||||
Bug #2852: No murder bounty when a player follower commits murder
|
|
||||||
Bug #2862: [macOS] Can't quit launcher using Command-Q or OpenMW->Quit
|
|
||||||
Bug #2872: Tab completion in console doesn't work with explicit reference
|
|
||||||
Bug #2971: Compiler did not reject lines with naked expressions beginning with x.y
|
|
||||||
Bug #3249: Fixed revert function not updating views properly
|
|
||||||
Bug #3374: Touch spells not hitting kwama foragers
|
|
||||||
Bug #3486: [Mod] NPC Commands does not work
|
|
||||||
Bug #3591: Angled hit distance too low
|
|
||||||
Bug #3629: DB assassin attack never triggers creature spawning
|
|
||||||
Bug #3876: Landscape texture painting is misaligned
|
|
||||||
Bug #3897: Have Goodbye give all choices the effects of Goodbye
|
|
||||||
Bug #3911: [macOS] Typing in the "Content List name" dialog box produces double characters
|
|
||||||
Bug #3950: FLATTEN_STATIC_TRANSFORMS optimization breaks animated collision shapes
|
|
||||||
Bug #3993: Terrain texture blending map is not upscaled
|
|
||||||
Bug #3997: Almalexia doesn't pace
|
|
||||||
Bug #4036: Weird behaviour of AI packages if package target has non-unique ID
|
|
||||||
Bug #4047: OpenMW not reporting its version number in MacOS; OpenMW-CS not doing it fully
|
|
||||||
Bug #4110: Fixed undo / redo menu text losing the assigned shortcuts
|
|
||||||
Bug #4125: OpenMW logo cropped on bugtracker
|
|
||||||
Bug #4215: OpenMW shows book text after last EOL tag
|
|
||||||
Bug #4221: Characters get stuck in V-shaped terrain
|
|
||||||
Bug #4230: AiTravel package issues break some Tribunal quests
|
|
||||||
Bug #4251: Stationary NPCs do not return to their position after combat
|
|
||||||
Bug #4274: Pre-0.43 death animations are not forward-compatible with 0.43+
|
|
||||||
Bug #4286: Scripted animations can be interrupted
|
|
||||||
Bug #4291: Non-persistent actors that started the game as dead do not play death animations
|
|
||||||
Bug #4293: Faction members are not aware of faction ownerships in barter
|
|
||||||
Bug #4307: World cleanup should remove dead bodies only if death animation is finished
|
|
||||||
Bug #4311: OpenMW does not handle RootCollisionNode correctly
|
|
||||||
Bug #4327: Missing animations during spell/weapon stance switching
|
|
||||||
Bug #4358: Running animation is interrupted when magic mode is toggled
|
|
||||||
Bug #4368: Settings window ok button doesn't have key focus by default
|
|
||||||
Bug #4378: On-self absorb spells restore stats
|
|
||||||
Bug #4393: NPCs walk back to where they were after using ResetActors
|
|
||||||
Bug #4416: Handle exception if we try to play non-music file
|
|
||||||
Bug #4419: MRK NiStringExtraData is handled incorrectly
|
|
||||||
Bug #4426: RotateWorld behavior is incorrect
|
|
||||||
Bug #4429: [Windows] Error on build INSTALL.vcxproj project (debug) with cmake 3.7.2
|
|
||||||
Bug #4431: "Lock 0" console command is a no-op
|
|
||||||
Bug #4432: Guards behaviour is incorrect if they do not have AI packages
|
|
||||||
Bug #4433: Guard behaviour is incorrect with Alarm = 0
|
|
||||||
Bug #4451: Script fails to compile when using "Begin, [ScriptName]" syntax
|
|
||||||
Bug #4452: Default terrain texture bleeds through texture transitions
|
|
||||||
Bug #4453: Quick keys behaviour is invalid for equipment
|
|
||||||
Bug #4454: AI opens doors too slow
|
|
||||||
Bug #4457: Item without CanCarry flag prevents shield autoequipping in dark areas
|
|
||||||
Bug #4458: AiWander console command handles idle chances incorrectly
|
|
||||||
Bug #4459: NotCell dialogue condition doesn't support partial matches
|
|
||||||
Bug #4460: Script function "Equip" doesn't bypass beast restrictions
|
|
||||||
Bug #4461: "Open" spell from non-player caster isn't a crime
|
|
||||||
Bug #4464: OpenMW keeps AiState cached storages even after we cancel AI packages
|
|
||||||
Bug #4469: Abot Silt Striders – Model turn 90 degrees on horizontal
|
|
||||||
Bug #4474: No fallback when getVampireHead fails
|
|
||||||
Bug #4475: Scripted animations should not cause movement
|
|
||||||
Bug #4479: "Game" category on Advanced page is getting too long
|
|
||||||
Bug #4480: Segfault in QuickKeysMenu when item no longer in inventory
|
|
||||||
Bug #4489: Goodbye doesn't block dialogue hyperlinks
|
|
||||||
Bug #4490: PositionCell on player gives "Error: tried to add local script twice"
|
|
||||||
Bug #4494: Training cap based off Base Skill instead of Modified Skill
|
|
||||||
Bug #4495: Crossbow animations blending is buggy
|
|
||||||
Bug #4496: SpellTurnLeft and SpellTurnRight animation groups are unused
|
|
||||||
Bug #4497: File names starting with x or X are not classified as animation
|
|
||||||
Bug #4503: Cast and ExplodeSpell commands increase alteration skill
|
|
||||||
Bug #4510: Division by zero in MWMechanics::CreatureStats::setAttribute
|
|
||||||
Bug #4519: Knockdown does not discard movement in the 1st-person mode
|
|
||||||
Bug #4539: Paper Doll is affected by GUI scaling
|
|
||||||
Bug #4545: Creatures flee from werewolves
|
|
||||||
Bug #4551: Replace 0 sound range with default range separately
|
|
||||||
Bug #4553: Forcegreeting on non-actor opens a dialogue window which cannot be closed
|
|
||||||
Bug #4557: Topics with reserved names are handled differently from vanilla
|
|
||||||
Bug #4558: Mesh optimizer: check for reserved node name is case-sensitive
|
|
||||||
Bug #4563: Fast travel price logic checks destination cell instead of service actor cell
|
|
||||||
Bug #4565: Underwater view distance should be limited
|
|
||||||
Bug #4573: Player uses headtracking in the 1st-person mode
|
|
||||||
Bug #4574: Player turning animations are twitchy
|
|
||||||
Bug #4575: Weird result of attack animation blending with movement animations
|
|
||||||
Bug #4576: Reset of idle animations when attack can not be started
|
|
||||||
Feature #2606: Editor: Implemented (optional) case sensitive global search
|
|
||||||
Feature #3083: Play animation when NPC is casting spell via script
|
|
||||||
Feature #3103: Provide option for disposition to get increased by successful trade
|
|
||||||
Feature #3276: Editor: Search - Show number of (remaining) search results and indicate a search without any results
|
|
||||||
Feature #3641: Editor: Limit FPS in 3d preview window
|
|
||||||
Feature #3703: Ranged sneak attack criticals
|
|
||||||
Feature #4012: Editor: Write a log file if OpenCS crashes
|
|
||||||
Feature #4222: 360° screenshots
|
|
||||||
Feature #4256: Implement ToggleBorders (TB) console command
|
|
||||||
Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts
|
|
||||||
Feature #4345: Add equivalents for the command line commands to Launcher
|
|
||||||
Feature #4404: Editor: All EnumDelegate fields should have their items sorted alphabetically
|
|
||||||
Feature #4444: Per-group KF-animation files support
|
|
||||||
Feature #4466: Editor: Add option to ignore "Base" records when running verifier
|
|
||||||
Feature #4488: Make water shader rougher during rain
|
|
||||||
Feature #4509: Show count of enchanted items in stack in the spells list
|
|
||||||
Feature #4512: Editor: Use markers for lights and creatures levelled lists
|
|
||||||
Feature #4548: Weapon priority: use the actual chance to hit the target instead of weapon skill
|
|
||||||
Feature #4549: Weapon priority: use the actual damage in weapon rating calculations
|
|
||||||
Feature #4550: Weapon priority: make ranged weapon bonus more sensible
|
|
||||||
Task #2490: Don't open command prompt window on Release-mode builds automatically
|
|
||||||
Task #4545: Enable is_pod string test
|
|
||||||
|
|
||||||
0.44.0
|
|
||||||
------
|
|
||||||
|
|
||||||
Bug #1428: Daedra summoning scripts aren't executed when the item is taken through the inventory
|
|
||||||
Bug #1987: Some glyphs are not supported
|
|
||||||
Bug #2254: Magic related visual effects are not rendered when loading a saved game
|
|
||||||
Bug #2485: Journal alphabetical index doesn't match "Morrowind content language" setting
|
|
||||||
Bug #2703: OnPCHitMe is not handled correctly
|
|
||||||
Bug #2829: Incorrect order for content list consisting of a game file and an esp without dependencies
|
|
||||||
Bug #2841: "Total eclipse" happens if weather settings are not defined.
|
|
||||||
Bug #2897: Editor: Rename "Original creature" field
|
|
||||||
Bug #3278: Editor: Unchecking "Auto Calc" flag changes certain values
|
|
||||||
Bug #3343: Editor: ID sorting is case-sensitive in certain tables
|
|
||||||
Bug #3557: Resource priority confusion when using the local data path as installation root
|
|
||||||
Bug #3587: Pathgrid and Flying Creatures wrong behaviour – abotWhereAreAllBirdsGoing
|
|
||||||
Bug #3603: SetPos should not skip weather transitions
|
|
||||||
Bug #3618: Myar Aranath total conversion can't be started due to capital-case extension of the master file
|
|
||||||
Bug #3638: Fast forwarding can move NPC inside objects
|
|
||||||
Bug #3664: Combat music does not start in dialogue
|
|
||||||
Bug #3696: Newlines are accompanied by empty rectangle glyph in dialogs
|
|
||||||
Bug #3708: Controllers broken on macOS
|
|
||||||
Bug #3726: Items with suppressed activation can be picked up via the inventory menu
|
|
||||||
Bug #3783: [Mod] Abot's Silt Striders 1.16 - silt strider "falls" to ground and glides on floor during travel
|
|
||||||
Bug #3863: Can be forced to not resist arrest if you cast Calm Humanoid on aggroed death warrant guards
|
|
||||||
Bug #3884: Incorrect enemy behavior when exhausted
|
|
||||||
Bug #3926: Installation Wizard places Morrowind.esm after Tribunal/Bloodmoon if it has a later file creation date
|
|
||||||
Bug #4061: Scripts error on special token included in name
|
|
||||||
Bug #4111: Crash when mouse over soulgem with a now-missing soul
|
|
||||||
Bug #4122: Swim animation should not be interrupted during underwater attack
|
|
||||||
Bug #4134: Battle music behaves different than vanilla
|
|
||||||
Bug #4135: Reflecting an absorb spell different from vanilla
|
|
||||||
Bug #4136: Enchanted weapons without "ignore normal weapons" flag don't bypass creature "ignore normal weapons" effect
|
|
||||||
Bug #4143: Antialiasing produces graphical artifacts when used with shader lighting
|
|
||||||
Bug #4159: NPCs' base skeleton files should not be optimized
|
|
||||||
Bug #4177: Jumping/landing animation interference/flickering
|
|
||||||
Bug #4179: NPCs do not face target
|
|
||||||
Bug #4180: Weapon switch sound playing even though no weapon is switched
|
|
||||||
Bug #4184: Guards can initiate dialogue even though you are far above them
|
|
||||||
Bug #4190: Enchanted clothes changes visibility with Chameleon on equip/unequip
|
|
||||||
Bug #4191: "screenshot saved" message also appears in the screenshot image
|
|
||||||
Bug #4192: Archers in OpenMW have shorter attack range than archers in Morrowind
|
|
||||||
Bug #4210: Some dialogue topics are not highlighted on first encounter
|
|
||||||
Bug #4211: FPS drops after minimizing the game during rainy weather
|
|
||||||
Bug #4216: Thrown weapon projectile doesn't rotate
|
|
||||||
Bug #4223: Displayed spell casting chance must be 0 if player doesn't have enough magicka to cast it
|
|
||||||
Bug #4225: Double "Activate" key presses with Mouse and Gamepad.
|
|
||||||
Bug #4226: The current player's class should be default value in the class select menu
|
|
||||||
Bug #4229: Tribunal/Bloodmoon summoned creatures fight other summons
|
|
||||||
Bug #4233: W and A keys override S and D Keys
|
|
||||||
Bug #4235: Wireframe mode affects local map
|
|
||||||
Bug #4239: Quick load from container screen causes crash
|
|
||||||
Bug #4242: Crime greetings display in Journal
|
|
||||||
Bug #4245: Merchant NPCs sell ingredients growing on potted plants they own
|
|
||||||
Bug #4246: Take armor condition into account when calcuting armor rating
|
|
||||||
Bug #4250: Jumping is not as fluid as it was pre-0.43.0
|
|
||||||
Bug #4252: "Error in frame: FFmpeg exception: Failed to allocate input stream" message spam if OpenMW encounter non-music file in the Music folder
|
|
||||||
Bug #4261: Magic effects from eaten ingredients always have 1 sec duration
|
|
||||||
Bug #4263: Arrow position is incorrect in 3rd person view during attack for beast races
|
|
||||||
Bug #4264: Player in god mode can be affected by some negative spell effects
|
|
||||||
Bug #4269: Crash when hovering the faction section and the 'sAnd' GMST is missing (as in MW 1.0)
|
|
||||||
Bug #4272: Root note transformations are discarded again
|
|
||||||
Bug #4279: Sometimes cells are not marked as explored on the map
|
|
||||||
Bug #4298: Problem with MessageBox and chargen menu interaction order
|
|
||||||
Bug #4301: Optimizer breaks LOD nodes
|
|
||||||
Bug #4308: PlaceAtMe doesn't inherit scale of calling object
|
|
||||||
Bug #4309: Only harmful effects with resistance effect set are resistable
|
|
||||||
Bug #4313: Non-humanoid creatures are capable of opening doors
|
|
||||||
Bug #4314: Rainy weather slows down the game when changing from indoors/outdoors
|
|
||||||
Bug #4319: Collisions for certain meshes are incorrectly ignored
|
|
||||||
Bug #4320: Using mouse 1 to move forward causes selection dialogues to jump selections forward.
|
|
||||||
Bug #4322: NPC disposition: negative faction reaction modifier doesn't take PC rank into account
|
|
||||||
Bug #4328: Ownership by dead actors is not cleared from picked items
|
|
||||||
Bug #4334: Torch and shield usage inconsistent with original game
|
|
||||||
Bug #4336: Wizard: Incorrect Morrowind assets path autodetection
|
|
||||||
Bug #4343: Error message for coc and starting cell shouldn't imply that it only works for interior cells
|
|
||||||
Bug #4346: Count formatting does not work well with very high numbers
|
|
||||||
Bug #4351: Using AddSoulgem fills all soul gems of the specified type
|
|
||||||
Bug #4391: No visual indication is provided when an unavailable spell fails to be chosen via a quick key
|
|
||||||
Bug #4392: Inventory filter breaks after loading a game
|
|
||||||
Bug #4405: No default terrain in empty cells when distant terrain is enabled
|
|
||||||
Bug #4410: [Mod] Arktwend: OpenMW does not use default marker definitions
|
|
||||||
Bug #4412: openmw-iniimporter ignores data paths from config
|
|
||||||
Bug #4413: Moving with 0 strength uses all of your fatigue
|
|
||||||
Bug #4420: Camera flickering when I open up and close menus while sneaking
|
|
||||||
Bug #4424: [macOS] Cursor is either empty or garbage when compiled against macOS 10.13 SDK
|
|
||||||
Bug #4435: Item health is considered a signed integer
|
|
||||||
Bug #4441: Adding items to currently disabled weapon-wielding creatures crashes the game
|
|
||||||
Feature #1786: Round up encumbrance value in the encumbrance bar
|
|
||||||
Feature #2694: Editor: rename "model" column to make its purpose clear
|
|
||||||
Feature #3870: Editor: Terrain Texture Brush Button
|
|
||||||
Feature #3872: Editor: Edit functions in terrain texture editing mode
|
|
||||||
Feature #4054: Launcher: Create menu for settings.cfg options
|
|
||||||
Feature #4064: Option for fast travel services to charge for the first companion
|
|
||||||
Feature #4142: Implement fWereWolfHealth GMST
|
|
||||||
Feature #4174: Multiple quicksaves
|
|
||||||
Feature #4407: Support NiLookAtController
|
|
||||||
Feature #4423: Rebalance soul gem values
|
|
||||||
Task #4015: Use AppVeyor build artifact features to make continuous builds available
|
|
||||||
Editor: New (and more complete) icon set
|
|
||||||
|
|
||||||
0.43.0
|
|
||||||
------
|
|
||||||
|
|
||||||
Bug #815: Different settings cause inconsistent underwater visibility
|
|
||||||
Bug #1452: autosave is not executed when waiting
|
|
||||||
Bug #1555: Closing containers with spacebar doesn't work after touching an item
|
|
||||||
Bug #1692: Can't close container when item is "held"
|
|
||||||
Bug #2405: Maximum distance for guards attacking hostile creatures is incorrect
|
|
||||||
Bug #2445: Spellcasting can be interrupted
|
|
||||||
Bug #2489: Keeping map open not persisted between saves
|
|
||||||
Bug #2594: 1st person view uses wrong body texture with Better bodies
|
|
||||||
Bug #2628: enablestatreviewmenu command doen't read race, class and sign values from current game
|
|
||||||
Bug #2639: Attacking flag isn't reset upon reloading
|
|
||||||
Bug #2698: Snow and rain VFX move with the player
|
|
||||||
Bug #2704: Some creature swim animations not being used
|
|
||||||
Bug #2789: Potential risk of misunderstanding using the colored "owned" crosshair feature
|
|
||||||
Bug #3045: Settings containing '#' cannot be loaded
|
|
||||||
Bug #3097: Drop() doesn't work when an item is held (with the mouse)
|
|
||||||
Bug #3110: GetDetected doesn't work without a reference
|
|
||||||
Bug #3126: Framerate nosedives when adjusting dialogue window size
|
|
||||||
Bug #3243: Ampersand in configuration files isn't escaped automatically
|
|
||||||
Bug #3365: Wrong water reflection along banks
|
|
||||||
Bug #3441: Golden saint always dispelling soul trap / spell priority issue
|
|
||||||
Bug #3528: Disposing of corpses breaks quests
|
|
||||||
Bug #3531: No FPS limit when playing bink videos even though "framerate limit" is set in settings.cfg
|
|
||||||
Bug #3647: Multi-effect spells play audio louder than in Vanilla
|
|
||||||
Bug #3656: NPCs forget where their place in the world is
|
|
||||||
Bug #3665: Music transitions are too abrupt
|
|
||||||
Bug #3679: Spell cast effect should disappear after using rest command
|
|
||||||
Bug #3684: Merchants do not restock empty soul gems if they acquire filled ones.
|
|
||||||
Bug #3694: Wrong magicka bonus applied on character creation
|
|
||||||
Bug #3706: Guards don't try to arrest the player if attacked
|
|
||||||
Bug #3709: Editor: Camera is not positioned correctly on mode switches related to orbital mode
|
|
||||||
Bug #3720: Death counter not cleaned of non-existing IDs when loading a game
|
|
||||||
Bug #3744: "Greater/lesser or equal" operators are not parsed when their signs are swapped
|
|
||||||
Bug #3749: Yagrum Bagarn moves to different position on encountering
|
|
||||||
Bug #3766: DisableLevitation does not remove visuals of preexisting effect
|
|
||||||
Bug #3787: Script commands in result box for voiced dialogue are ignored
|
|
||||||
Bug #3793: OpenMW tries to animate animated references even when they are disabled
|
|
||||||
Bug #3794: Default sound buffer size is too small for mods
|
|
||||||
Bug #3796: Mod 'Undress for me' doesn't work: NPCs re-equip everything
|
|
||||||
Bug #3798: tgm command behaviour differs from vanilla
|
|
||||||
Bug #3804: [Mod] Animated Morrowind: some animations do not loop correctly
|
|
||||||
Bug #3805: Slight enchant miscalculation
|
|
||||||
Bug #3826: Rendering problems with an image in a letter
|
|
||||||
Bug #3833: [Mod] Windows Glow: windows textures are much darker than in original game
|
|
||||||
Bug #3835: Bodyparts with multiple NiTriShapes are not handled correctly
|
|
||||||
Bug #3839: InventoryStore::purgeEffect() removes only first effect with argument ID
|
|
||||||
Bug #3843: Wrong jumping fatigue loss calculations
|
|
||||||
Bug #3850: Boethiah's voice is distorted underwater
|
|
||||||
Bug #3851: NPCs and player say things while underwater
|
|
||||||
Bug #3864: Crash when exiting to Khartag point from Ilunibi
|
|
||||||
Bug #3878: Swapping soul gems while enchanting allows constant effect enchantments using any soul gem
|
|
||||||
Bug #3879: Dialogue option: Go to jail, persists beyond quickload
|
|
||||||
Bug #3891: Journal displays empty entries
|
|
||||||
Bug #3892: Empty space before dialogue entry display
|
|
||||||
Bug #3898: (mod) PositionCell in dialogue results closes dialogue window
|
|
||||||
Bug #3906: "Could not find Data Files location" dialog can appear multiple times
|
|
||||||
Bug #3908: [Wizard] User gets stuck if they cancel out of installing from a CD
|
|
||||||
Bug #3909: Morrowind Content Language dropdown is the only element on the right half of the Settings window
|
|
||||||
Bug #3910: Launcher window can be resized so that it cuts off the scroll
|
|
||||||
Bug #3915: NC text key on nifs doesn't work
|
|
||||||
Bug #3919: Closing inventory while cursor hovers over spell (or other magic menu item) produces left click sound
|
|
||||||
Bug #3922: Combat AI should avoid enemy hits when casts Self-ranged spells
|
|
||||||
Bug #3934: [macOS] Copy/Paste from system clipboard uses Control key instead of Command key
|
|
||||||
Bug #3935: Incorrect attack strength for AI actors
|
|
||||||
Bug #3937: Combat AI: enchanted weapons have too high rating
|
|
||||||
Bug #3942: UI sounds are distorted underwater
|
|
||||||
Bug #3943: CPU/GPU usage should stop when the game is minimised
|
|
||||||
Bug #3944: Attempting to sell stolen items back to their owner does not remove them from your inventory
|
|
||||||
Bug #3955: Player's avatar rendering issues
|
|
||||||
Bug #3956: EditEffectDialog: Cancel button does not update a Range button and an Area slider properly
|
|
||||||
Bug #3957: Weird bodypart rendering if a node has reserved name
|
|
||||||
Bug #3960: Clothes with high cost (> 32768) are not handled properly
|
|
||||||
Bug #3963: When on edge of being burdened the condition doesn't lower as you run.
|
|
||||||
Bug #3971: Editor: Incorrect colour field in cell table
|
|
||||||
Bug #3974: Journal page turning doesn't produce sounds
|
|
||||||
Bug #3978: Instant opening and closing happens when using a Controller with Menus/Containers
|
|
||||||
Bug #3981: Lagging when spells are cast, especially noticeable on new landmasses such as Tamriel Rebuilt
|
|
||||||
Bug #3982: Down sounds instead of Up ones are played when trading
|
|
||||||
Bug #3987: NPCs attack after some taunting with no "Goodbye"
|
|
||||||
Bug #3991: Journal can still be opened at main menu
|
|
||||||
Bug #3995: Dispel cancels every temporary magic effect
|
|
||||||
Bug #4002: Build broken on OpenBSD with clang
|
|
||||||
Bug #4003: Reduce Render Area of Inventory Doll to Fit Within Border
|
|
||||||
Bug #4004: Manis Virmaulese attacks without saying anything
|
|
||||||
Bug #4010: AiWander: "return to the spawn position" feature does not work properly
|
|
||||||
Bug #4016: Closing menus with spacebar will still send certain assigned actions through afterwards
|
|
||||||
Bug #4017: GetPCRunning and GetPCSneaking should check that the PC is actually moving
|
|
||||||
Bug #4024: Poor music track distribution
|
|
||||||
Bug #4025: Custom spell with copy-pasted name always sorts to top of spell list
|
|
||||||
Bug #4027: Editor: OpenMW-CS misreports its own name as "OpenCS", under Mac OS
|
|
||||||
Bug #4033: Archers don't attack if the arrows have run out and there is no other weapon
|
|
||||||
Bug #4037: Editor: New greetings do not work in-game.
|
|
||||||
Bug #4049: Reloading a saved game while falling prevents damage
|
|
||||||
Bug #4056: Draw animation should not be played when player equips a new weapon
|
|
||||||
Bug #4074: Editor: Merging of LAND/LTEX records
|
|
||||||
Bug #4076: Disposition bar is not updated when "goodbye" selected in dialogue
|
|
||||||
Bug #4079: Alchemy skill increases do not take effect until next batch
|
|
||||||
Bug #4093: GetResistFire, getResistFrost and getResistShock doesn't work as in vanilla
|
|
||||||
Bug #4094: Level-up messages for levels past 20 are hardcoded not to be used
|
|
||||||
Bug #4095: Error in framelistener when take all items from a dead corpse
|
|
||||||
Bug #4096: Messagebox with the "%0.f" format should use 0 digit precision
|
|
||||||
Bug #4104: Cycling through weapons does not skip broken ones
|
|
||||||
Bug #4105: birthsign generation menu does not show full details
|
|
||||||
Bug #4107: Editor: Left pane in Preferences window is too narrow
|
|
||||||
Bug #4112: Inventory sort order is inconsistent
|
|
||||||
Bug #4113: 'Resolution not supported in fullscreen' message is inconvenient
|
|
||||||
Bug #4131: Pickpocketing behaviour is different from vanilla
|
|
||||||
Bug #4155: NPCs don't equip a second ring in some cases
|
|
||||||
Bug #4156: Snow doesn't create water ripples
|
|
||||||
Bug #4165: NPCs autoequip new clothing with the same price
|
|
||||||
Feature #452: Rain-induced water ripples
|
|
||||||
Feature #824: Fading for doors and teleport commands
|
|
||||||
Feature #933: Editor: LTEX record table
|
|
||||||
Feature #936: Editor: LAND record table
|
|
||||||
Feature #1374: AI: Resurface to breathe
|
|
||||||
Feature #2320: ess-Importer: convert projectiles
|
|
||||||
Feature #2509: Editor: highlighting occurrences of a word in a script
|
|
||||||
Feature #2748: Editor: Should use one resource manager per document
|
|
||||||
Feature #2834: Have openMW's UI remember what menu items were 'pinned' across boots.
|
|
||||||
Feature #2923: Option to show the damage of the arrows through tooltip.
|
|
||||||
Feature #3099: Disabling inventory while dragging an item forces you to drop it
|
|
||||||
Feature #3274: Editor: Script Editor - Shortcuts and context menu options for commenting code out and uncommenting code respectively
|
|
||||||
Feature #3275: Editor: User Settings- Add an option to reset settings to their default status (per category / all)
|
|
||||||
Feature #3400: Add keyboard shortcuts for menus
|
|
||||||
Feature #3492: Show success rate while enchanting
|
|
||||||
Feature #3530: Editor: Reload data files
|
|
||||||
Feature #3682: Editor: Default key binding reset
|
|
||||||
Feature #3921: Combat AI: aggro priorities
|
|
||||||
Feature #3941: Allow starting at an unnamed exterior cell with --start
|
|
||||||
Feature #3952: Add Visual Studio 2017 support
|
|
||||||
Feature #3953: Combat AI: use "WhenUsed" enchantments
|
|
||||||
Feature #4082: Leave the stack of ingredients or potions grabbed after using an ingredient/potion
|
|
||||||
Task #2258: Windows installer: launch OpenMW tickbox
|
|
||||||
Task #4152: The Windows CI script is moving files around that CMake should be dealing with
|
|
||||||
|
|
||||||
0.42.0
|
|
||||||
------
|
|
||||||
|
|
||||||
Bug #1956: Duplicate objects after loading the game, when a mod was edited
|
|
||||||
Bug #2100: Falling leaves in Vurt's Leafy West Gash II not rendered correctly
|
|
||||||
Bug #2116: Cant fit through some doorways pressed against staircases
|
|
||||||
Bug #2289: Some modal dialogs are not centered on the screen when the window resizes
|
|
||||||
Bug #2409: Softlock when pressing weapon/magic switch keys during chargen, afterwards switches weapons even though a text field is selected
|
|
||||||
Bug #2483: Previous/Next Weapon hotkeys triggered while typing the name of game save
|
|
||||||
Bug #2629: centeroncell, coc causes death / fall damage time to time when teleporting from high
|
|
||||||
Bug #2645: Cycling weapons is possible while console/pause menu is open
|
|
||||||
Bug #2678: Combat with water creatures do not end upon exiting water
|
|
||||||
Bug #2759: Light Problems in Therana's Chamber in Tel Branora
|
|
||||||
Bug #2771: unhandled sdl event of type 0x302
|
|
||||||
Bug #2777: (constant/on cast) disintegrate armor/weapon on self is seemingly not working
|
|
||||||
Bug #2838: Editor: '.' in a record name should be allowed
|
|
||||||
Bug #2909: NPCs appear floating when standing on a slope
|
|
||||||
Bug #3093: Controller movement cannot be used while mouse is moving
|
|
||||||
Bug #3134: Crash possible when using console with open container
|
|
||||||
Bug #3254: AI enemies hit between them.
|
|
||||||
Bug #3344: Editor: Verification results sorting by Type is not alphabetical.
|
|
||||||
Bug #3345: Editor: Cloned and added pathgrids are lost after reopen of saved omwgame file
|
|
||||||
Bug #3355: [MGSO] Physics maxing out in south cornerclub Balmora
|
|
||||||
Bug #3484: Editor: camera position is not set when changing cell via drag&drop
|
|
||||||
Bug #3508: Slowfall kills Jump momentum
|
|
||||||
Bug #3580: Crash: Error ElementBufferObject::remove BufferData<0> out of range
|
|
||||||
Bug #3581: NPCs wander too much
|
|
||||||
Bug #3601: Menu Titles not centered vertically
|
|
||||||
Bug #3607: [Mac OS] Beginning of NPC speech cut off (same issue as closed bug #3453)
|
|
||||||
Bug #3613: Can not map "next weapon" or "next spell" to controller
|
|
||||||
Bug #3617: Enchanted arrows don't explode when hitting the ground
|
|
||||||
Bug #3645: Unable to use steps in Vivec, Palace of Vivec
|
|
||||||
Bug #3650: Tamriel Rebuilt 16.09.1 – Hist Cuirass GND nif is rendered inside a Pink Box
|
|
||||||
Bug #3652: Item icon shadows get stuck in the alchemy GUI
|
|
||||||
Bug #3653: Incorrect swish sounds
|
|
||||||
Bug #3666: NPC collision should not be disabled until death animation has finished
|
|
||||||
Bug #3669: Editor: Text field was missing from book object editing dialogue
|
|
||||||
Bug #3670: Unhandled SDL event of type 0x304
|
|
||||||
Bug #3671: Incorrect local variable value after picking up bittercup
|
|
||||||
Bug #3686: Travelling followers doesn't increase travel fee
|
|
||||||
Bug #3689: Problematic greetings from Antares Big Mod that override the appropriate ones.
|
|
||||||
Bug #3690: Certain summoned creatures do not engage in combat with underwater creatures
|
|
||||||
Bug #3691: Enemies do not initiate combat with player followers on sight
|
|
||||||
Bug #3695: [Regression] Dispel does not always dispel spell effects in 0.41
|
|
||||||
Bug #3699: Crash on MWWorld::ProjectileManager::moveMagicBolts
|
|
||||||
Bug #3700: Climbing on rocks and mountains
|
|
||||||
Bug #3704: Creatures don't auto-equip their shields on creation
|
|
||||||
Bug #3705: AI combat engagement logic differs from vanilla
|
|
||||||
Bug #3707: Animation playing does some very odd things if pc comes in contact with the animated mesh
|
|
||||||
Bug #3712: [Mod] Freeze upon entering Adanumuran with mod Adanumuran Reclaimed
|
|
||||||
Bug #3713: [Regression] Cancelling dialogue or using travel with creatures throws a (possibly game-breaking) exception
|
|
||||||
Bug #3719: Dropped identification papers can't be picked up again
|
|
||||||
Bug #3722: Command spell doesn't bring enemies out of combat
|
|
||||||
Bug #3727: Using "Activate" mid-script-execution invalidates interpreter context
|
|
||||||
Bug #3746: Editor: Book records show attribute IDs instead of skill IDs for teached skills entry.
|
|
||||||
Bug #3755: Followers stop following after loading from savegame
|
|
||||||
Bug #3772: ModStat lowers attribute to 100 if it was greater
|
|
||||||
Bug #3781: Guns in Clean Hunter Rifles mod use crossbow sounds
|
|
||||||
Bug #3797: NPC and creature names don't show up in combat when RMB windows are displayed
|
|
||||||
Bug #3800: Wrong tooltip maximum width
|
|
||||||
Bug #3801: Drowning widget is bugged
|
|
||||||
Bug #3802: BarterOffer shouldn't limit pcMercantile
|
|
||||||
Bug #3813: Some fatal error
|
|
||||||
Bug #3816: Expression parser thinks the -> token is unexpected when a given explicit refID clashes with a journal ID
|
|
||||||
Bug #3822: Custom added creatures are not animated
|
|
||||||
Feature #451: Water sounds
|
|
||||||
Feature #2691: Light particles sometimes not shown in inventory character preview
|
|
||||||
Feature #3523: Light source on magic projectiles
|
|
||||||
Feature #3644: Nif NiSphericalCollider Unknown Record Type
|
|
||||||
Feature #3675: ess-Importer: convert mark location
|
|
||||||
Feature #3693: ess-Importer: convert last known exterior cell
|
|
||||||
Feature #3748: Editor: Replace "Scroll" check box in Book records with "Book Type" combo box.
|
|
||||||
Feature #3751: Editor: Replace "Xyz Blood" check boxes in NPC and Creature records with "Blood Type" combo box
|
|
||||||
Feature #3752: Editor: Replace emitter check boxes in Light records with "Emitter Type" combo box
|
|
||||||
Feature #3756: Editor: Replace "Female" check box in NPC records with "Gender" combo box
|
|
||||||
Feature #3757: Editor: Replace "Female" check box in BodyPart records with "Gender" combo box
|
|
||||||
Task #3092: const version of ContainerStoreIterator
|
|
||||||
Task #3795: /deps folder not in .gitignore
|
|
||||||
|
|
||||||
0.41.0
|
|
||||||
------
|
|
||||||
|
|
||||||
Bug #1138: Casting water walking doesn't move the player out of the water
|
|
||||||
Bug #1931: Rocks from blocked passage in Bamz-Amschend, Radacs Forge can reset and cant be removed again.
|
|
||||||
Bug #2048: Almvisi and Divine Intervention display wrong spell effect
|
|
||||||
Bug #2054: Show effect-indicator for "instant effect" spells and potions
|
|
||||||
Bug #2150: Clockwork City door animation problem
|
|
||||||
Bug #2288: Playback of weapon idle animation not correct
|
|
||||||
Bug #2410: Stat-review window doesn't display starting spells, powers, or abilities
|
|
||||||
Bug #2493: Repairing occasionally very slow
|
|
||||||
Bug #2716: [OSG] Water surface is too transparent from some angles
|
|
||||||
Bug #2859: [MAC OS X] Cannot exit fullscreen once enabled
|
|
||||||
Bug #3091: Editor: will not save addon if global variable value type is null
|
|
||||||
Bug #3277: Editor: Non-functional nested tables in subviews need to be hidden instead of being disabled
|
|
||||||
Bug #3348: Disabled map markers show on minimap
|
|
||||||
Bug #3350: Extending selection to instances with same object results in duplicates.
|
|
||||||
Bug #3353: [Mod] Romance version 3.7 script failed
|
|
||||||
Bug #3376: [Mod] Vampire Embrace script fails to execute
|
|
||||||
Bug #3385: Banners don't animate in stormy weather as they do in the original game
|
|
||||||
Bug #3393: Akulakhan re-enabled after main quest
|
|
||||||
Bug #3427: Editor: OpenMW-CS instances won´t get deleted
|
|
||||||
Bug #3451: Feril Salmyn corpse isn't where it is supposed to be
|
|
||||||
Bug #3497: Zero-weight armor is displayed as "heavy" in inventory tooltip
|
|
||||||
Bug #3499: Idle animations don't always loop
|
|
||||||
Bug #3500: Spark showers at Sotha Sil do not appear until you look at the ceiling
|
|
||||||
Bug #3515: Editor: Moved objects in interior cells are teleported to exterior cells.
|
|
||||||
Bug #3520: Editor: OpenMW-CS cannot find project file when launching the game
|
|
||||||
Bug #3521: Armed NPCs don't use correct melee attacks
|
|
||||||
Bug #3535: Changing cell immediately after dying causes character to freeze.
|
|
||||||
Bug #3542: Unable to rest if unalerted slaughterfish are in the cell with you
|
|
||||||
Bug #3549: Blood effects occur even when a hit is resisted
|
|
||||||
Bug #3551: NPC Todwendy in german version can't interact
|
|
||||||
Bug #3552: Opening the journal when fonts are missing results in a crash
|
|
||||||
Bug #3555: SetInvisible command should not apply graphic effect
|
|
||||||
Bug #3561: Editor: changes from omwaddon are not loaded in [New Addon] mode
|
|
||||||
Bug #3562: Non-hostile NPCs can be disarmed by stealing their weapons via sneaking
|
|
||||||
Bug #3564: Editor: openmw-cs verification results
|
|
||||||
Bug #3568: Items that should be invisible are shown in the inventory
|
|
||||||
Bug #3574: Alchemy: Alembics and retorts are used in reverse
|
|
||||||
Bug #3575: Diaglog choices don't work in mw 0.40
|
|
||||||
Bug #3576: Minor differences in AI reaction to hostile spell effects
|
|
||||||
Bug #3577: not local nolore dialog test
|
|
||||||
Bug #3578: Animation Replacer hangs after one cicle/step
|
|
||||||
Bug #3579: Bound Armor skillups and sounds
|
|
||||||
Bug #3583: Targetted GetCurrentAiPackage returns 0
|
|
||||||
Bug #3584: Persuasion bug
|
|
||||||
Bug #3590: Vendor, Ilen Faveran, auto equips items from stock
|
|
||||||
Bug #3594: Weather doesn't seem to update correctly in Mournhold
|
|
||||||
Bug #3598: Saving doesn't save status of objects
|
|
||||||
Bug #3600: Screen goes black when trying to travel to Sadrith Mora
|
|
||||||
Bug #3608: Water ripples aren't created when walking on water
|
|
||||||
Bug #3626: Argonian NPCs swim like khajiits
|
|
||||||
Bug #3627: Cannot delete "Blessed touch" spell from spellbook
|
|
||||||
Bug #3634: An enchanted throwing weapon consumes charges from the stack in your inventory. (0.40.0)
|
|
||||||
Bug #3635: Levelled items in merchants are "re-rolled" (not bug 2952, see inside)
|
|
||||||
Feature #1118: AI combat: flee
|
|
||||||
Feature #1596: Editor: Render water
|
|
||||||
Feature #2042: Adding a non-portable Light to the inventory should cause the player to glow
|
|
||||||
Feature #3166: Editor: Instance editing mode - rotate sub mode
|
|
||||||
Feature #3167: Editor: Instance editing mode - scale sub mode
|
|
||||||
Feature #3420: ess-Importer: player control flags
|
|
||||||
Feature #3489: You shouldn't be be able to re-cast a bound equipment spell
|
|
||||||
Feature #3496: Zero-weight boots should play light boot footsteps
|
|
||||||
Feature #3516: Water Walking should give a "can't cast" message and fail when you are too deep
|
|
||||||
Feature #3519: Play audio and visual effects for all effects in a spell
|
|
||||||
Feature #3527: Double spell explosion scaling
|
|
||||||
Feature #3534: Play particle textures for spell effects
|
|
||||||
Feature #3539: Make NPCs use opponent's weapon range to decide whether to dodge
|
|
||||||
Feature #3540: Allow dodging for creatures with "biped" flag
|
|
||||||
Feature #3545: Drop shadow for items in menu
|
|
||||||
Feature #3558: Implement same spell range for "on touch" spells as original engine
|
|
||||||
Feature #3560: Allow using telekinesis with touch spells on objects
|
|
||||||
Task #3585: Some objects added by Morrowind Rebirth do not display properly their texture
|
|
||||||
|
|
||||||
0.40.0
|
|
||||||
------
|
|
||||||
|
|
||||||
Bug #1320: AiWander - Creatures in cells without pathgrids do not wander
|
|
||||||
Bug #1873: Death events are triggered at the beginning of the death animation
|
|
||||||
Bug #1996: Resting interrupts magic effects
|
|
||||||
Bug #2399: Vampires can rest in broad daylight and survive the experience
|
|
||||||
Bug #2604: Incorrect magicka recalculation
|
|
||||||
Bug #2721: Telekinesis extends interaction range where it shouldn't
|
|
||||||
Bug #2981: When waiting, NPCs can go where they wouldn't go normally.
|
|
||||||
Bug #3045: Esp files containing the letter '#' in the file name cannot be loaded on startup
|
|
||||||
Bug #3071: Slowfall does not stop momentum when jumping
|
|
||||||
Bug #3085: Plugins can not replace parent cell references with a cell reference of different type
|
|
||||||
Bug #3145: Bug with AI Cliff Racer. He will not attack you, unless you put in front of him.
|
|
||||||
Bug #3149: Editor: Weather tables were missing from regions
|
|
||||||
Bug #3201: Netch shoots over your head
|
|
||||||
Bug #3269: If you deselect a mod and try to load a save made inside a cell added by it, you end bellow the terrain in the grid 0/0
|
|
||||||
Bug #3286: Editor: Script editor tab width
|
|
||||||
Bug #3329: Teleportation spells cause crash to desktop after build update from 0.37 to 0.38.0
|
|
||||||
Bug #3331: Editor: Start Scripts table: Adding a script doesn't refresh the list of Start Scripts and allows to add a single script multiple times
|
|
||||||
Bug #3332: Editor: Scene view: Tool tips only occur when holding the left mouse button
|
|
||||||
Bug #3340: ESS-Importer does not separate item stacks
|
|
||||||
Bug #3342: Editor: Creation of pathgrids did not check if the pathgrid already existed
|
|
||||||
Bug #3346: "Talked to PC" is always 0 for "Hello" dialogue
|
|
||||||
Bug #3349: AITravel doesn't repeat
|
|
||||||
Bug #3370: NPCs wandering to invalid locations after training
|
|
||||||
Bug #3378: "StopCombat" command does not function in vanilla quest
|
|
||||||
Bug #3384: Battle at Nchurdamz - Larienna Macrina does not stop combat after killing Hrelvesuu
|
|
||||||
Bug #3388: Monster Respawn tied to Quicksave
|
|
||||||
Bug #3390: Strange visual effect in Dagoth Ur's chamber
|
|
||||||
Bug #3391: Inappropriate Blight weather behavior at end of main quest
|
|
||||||
Bug #3394: Replaced dialogue inherits some of its old data
|
|
||||||
Bug #3397: Actors that start the game dead always have the same death pose
|
|
||||||
Bug #3401: Sirollus Saccus sells not glass arrows
|
|
||||||
Bug #3402: Editor: Weapon data not being properly set
|
|
||||||
Bug #3405: Mulvisic Othril will not use her chitin throwing stars
|
|
||||||
Bug #3407: Tanisie Verethi will immediately detect the player
|
|
||||||
Bug #3408: Improper behavior of ashmire particles
|
|
||||||
Bug #3412: Ai Wander start time resets when saving/loading the game
|
|
||||||
Bug #3416: 1st person and 3rd person camera isn't converted from .ess correctly
|
|
||||||
Bug #3421: Idling long enough while paralyzed sometimes causes character to get stuck
|
|
||||||
Bug #3423: Sleep interruption inside dungeons too agressive
|
|
||||||
Bug #3424: Pickpocketing sometimes won't work
|
|
||||||
Bug #3432: AiFollow / AiEscort durations handled incorrectly
|
|
||||||
Bug #3434: Dead NPC's and Creatures still contribute to sneak skill increases
|
|
||||||
Bug #3437: Weather-conditioned dialogue should not play in interiors
|
|
||||||
Bug #3439: Effects cast by summon stick around after their death
|
|
||||||
Bug #3440: Parallax maps looks weird
|
|
||||||
Bug #3443: Class graphic for custom class should be Acrobat
|
|
||||||
Bug #3446: OpenMW segfaults when using Atrayonis's "Anthology Solstheim: Tomb of the Snow Prince" mod
|
|
||||||
Bug #3448: After dispelled, invisibility icon is still displayed
|
|
||||||
Bug #3453: First couple of seconds of NPC speech is muted
|
|
||||||
Bug #3455: Portable house mods lock player and npc movement up exiting house.
|
|
||||||
Bug #3456: Equipping an item will undo dispel of constant effect invisibility
|
|
||||||
Bug #3458: Constant effect restore health doesn't work during Wait
|
|
||||||
Bug #3466: It is possible to stack multiple scroll effects of the same type
|
|
||||||
Bug #3471: When two mods delete the same references, many references are not disabled by the engine.
|
|
||||||
Bug #3473: 3rd person camera can be glitched
|
|
||||||
Feature #1424: NPC "Face" function
|
|
||||||
Feature #2974: Editor: Multiple Deletion of Subrecords
|
|
||||||
Feature #3044: Editor: Render path grid v2
|
|
||||||
Feature #3362: Editor: Configurable key bindings
|
|
||||||
Feature #3375: Make sun / moon reflections weather dependent
|
|
||||||
Feature #3386: Editor: Edit pathgrid
|
|
||||||
|
|
||||||
0.39.0
|
|
||||||
------
|
|
||||||
|
|
||||||
Bug #1384: Dark Brotherhood Assassin (and other scripted NPCs?) spawns beneath/inside solid objects
|
|
||||||
Bug #1544: "Drop" drops equipped item in a separate stack
|
|
||||||
Bug #1587: Collision detection glitches
|
|
||||||
Bug #1629: Container UI locks up in Vivec at Jeanne's
|
|
||||||
Bug #1771: Dark Brotherhood Assassin oddity in Eight Plates
|
|
||||||
Bug #1827: Unhandled NiTextureEffect in ex_dwrv_ruin30.nif
|
|
||||||
Bug #2089: When saving while swimming in water in an interior cell, you will be spawned under water on loading
|
|
||||||
Bug #2295: Internal texture not showing, nipixeldata
|
|
||||||
Bug #2363: Corpses don't disappear
|
|
||||||
Bug #2369: Respawns should be timed individually
|
|
||||||
Bug #2393: Сharacter is stuck in the tree
|
|
||||||
Bug #2444: [Mod] NPCs from Animated Morrowind appears not using proper animations
|
|
||||||
Bug #2467: Creatures do not respawn
|
|
||||||
Bug #2515: Ghosts in Ibar-Dad spawn stuck in walls
|
|
||||||
Bug #2610: FixMe script still needs to be implemented
|
|
||||||
Bug #2689: Riekling raider pig constantly screams while running
|
|
||||||
Bug #2719: Vivec don't put their hands on the knees with this replacer (Psymoniser Vivec God Replacement NPC Edition v1.0
|
|
||||||
Bug #2737: Camera shaking when side stepping around object
|
|
||||||
Bug #2760: AI Combat Priority Problem - Use of restoration spell instead of attacking
|
|
||||||
Bug #2806: Stack overflow in LocalScripts::getNext
|
|
||||||
Bug #2807: Collision detection allows player to become stuck inside objects
|
|
||||||
Bug #2814: Stairs to Marandus have improper collision
|
|
||||||
Bug #2925: Ranes Ienith will not appear, breaking the Morag Tong and Thieves Guid questlines
|
|
||||||
Bug #3024: Editor: Creator bar in startscript subview does not accept script ID drops
|
|
||||||
Bug #3046: Sleep creature: Velk is spawned half-underground in the Thirr River Valley
|
|
||||||
Bug #3080: Calling aifollow without operant in local script every frame causes mechanics to overheat + log
|
|
||||||
Bug #3101: Regression: White guar does not move
|
|
||||||
Bug #3108: Game Freeze after Killing Diseased Rat in Foreign Quarter Tomb
|
|
||||||
Bug #3124: Bloodmoon Quest - Rite of the Wolf Giver (BM_WolfGiver) – Innocent victim won't turn werewolf
|
|
||||||
Bug #3125: Improper dialogue window behavior when talking to creatures
|
|
||||||
Bug #3130: Some wandering NPCs disappearing, cannot finish quests
|
|
||||||
Bug #3132: Editor: GMST ID named sMake Enchantment is instead named sMake when making new game from scratch
|
|
||||||
Bug #3133: OpenMW and the OpenCS are writting warnings about scripts that use the function GetDisabled.
|
|
||||||
Bug #3135: Journal entry for The Pigrim's Path missing name
|
|
||||||
Bug #3136: Dropped bow is displaced
|
|
||||||
Bug #3140: Editor: OpenMW-CS fails to open newly converted and saved omwaddon file.
|
|
||||||
Bug #3142: Duplicate Resist Magic message
|
|
||||||
Bug #3143: Azura missing her head
|
|
||||||
Bug #3146: Potion effect showing when ingredient effects are not known
|
|
||||||
Bug #3155: When executing chop attack with a spear, hands turn partly invisible
|
|
||||||
Bug #3161: Fast travel from Silt Strider or Boat Ride will break save files made afterwards
|
|
||||||
Bug #3163: Editor: Objects dropped to scene do not always save
|
|
||||||
Bug #3173: Game Crashes After Casting Recall Spell
|
|
||||||
Bug #3174: Constant effect enchantments play spell animation on dead bodies
|
|
||||||
Bug #3175: Spell effects do not wear down when caster dies
|
|
||||||
Bug #3176: NPCs appearing randomly far away from towns
|
|
||||||
Bug #3177: Submerged corpse floats ontop of water when it shouldn't (Widow Vabdas' Deed quest)
|
|
||||||
Bug #3184: Bacola Closcius in Balmora, South Wall Cornerclub spams magic effects if attacked
|
|
||||||
Bug #3207: Editor: New objects do not render
|
|
||||||
Bug #3212: Arrow of Ranged Silence
|
|
||||||
Bug #3213: Looking at Floor After Magical Transport
|
|
||||||
Bug #3220: The number of remaining ingredients in the alchemy window doesn't go down when failing to brew a potion
|
|
||||||
Bug #3222: Falling through the water in Vivec
|
|
||||||
Bug #3223: Crash at the beginning with MOD (The Symphony)
|
|
||||||
Bug #3228: Purple screen when leveling up.
|
|
||||||
Bug #3233: Infinite disposition via MWDialogue::Filter::testDisposition() glitch
|
|
||||||
Bug #3234: Armor mesh stuck on body in inventory menu
|
|
||||||
Bug #3235: Unlike vanilla, OpenMW don't allow statics and activators cast effects on the player.
|
|
||||||
Bug #3238: Not loading cells when using Poorly Placed Object Fix.esm
|
|
||||||
Bug #3248: Editor: Using the "Next Script" and "Previous Script" buttons changes the record status to "Modified"
|
|
||||||
Bug #3258: Woman biped skeleton
|
|
||||||
Bug #3259: No alternating punches
|
|
||||||
Bug #3262: Crash in class selection menu
|
|
||||||
Bug #3279: Load menu: Deleting a savegame makes scroll bar jump to the top
|
|
||||||
Bug #3326: Starting a new game, getting to class selection, then starting another new game temporarily assigns Acrobat class
|
|
||||||
Bug #3327: Stuck in table after loading when character was sneaking when quicksave
|
|
||||||
Feature #652: Editor: GMST verifier
|
|
||||||
Feature #929: Editor: Info record verifier
|
|
||||||
Feature #1279: Editor: Render cell border markers
|
|
||||||
Feature #2482: Background cell loading and caching of loaded cells
|
|
||||||
Feature #2484: Editor: point lighting
|
|
||||||
Feature #2801: Support NIF bump map textures in osg
|
|
||||||
Feature #2926: Editor: Optional line wrap in script editor wrap lines
|
|
||||||
Feature #3000: Editor: Reimplement 3D scene camera system
|
|
||||||
Feature #3035: Editor: Make scenes a drop target for referenceables
|
|
||||||
Feature #3043: Editor: Render cell markers v2
|
|
||||||
Feature #3164: Editor: Instance Selection Menu
|
|
||||||
Feature #3165: Editor: Instance editing mode - move sub mode
|
|
||||||
Feature #3244: Allow changing water Level of Interiors behaving like exteriors
|
|
||||||
Feature #3250: Editor: Use "Enter" key instead of clicking "Create" button to confirm ID input in Creator Bar
|
|
||||||
Support #3179: Fatal error on startup
|
|
||||||
|
|
||||||
0.38.0
|
0.38.0
|
||||||
------
|
------
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,21 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-
|
|
||||||
|
|
||||||
# Set up compilers
|
if [ "${ANALYZE}" ]; then
|
||||||
if [ ! -z "${MATRIX_CC}" ]; then
|
echo "yes" | sudo add-apt-repository "deb http://llvm.org/apt/`lsb_release -sc`/ llvm-toolchain-`lsb_release -sc`-3.6 main"
|
||||||
eval "${MATRIX_CC}"
|
wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key|sudo apt-key add -
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# build libgtest & libgtest_main
|
echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse"
|
||||||
|
echo "yes" | sudo apt-add-repository ppa:openmw/openmw
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -qq libgtest-dev google-mock
|
||||||
|
sudo apt-get install -qq libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-thread-dev
|
||||||
|
sudo apt-get install -qq libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev
|
||||||
|
sudo apt-get install -qq libbullet-dev libopenscenegraph-dev libmygui-dev libsdl2-dev libunshield-dev libtinyxml-dev libopenal-dev libqt4-dev
|
||||||
|
if [ "${ANALYZE}" ]; then sudo apt-get install -qq clang-3.6; fi
|
||||||
sudo mkdir /usr/src/gtest/build
|
sudo mkdir /usr/src/gtest/build
|
||||||
cd /usr/src/gtest/build
|
cd /usr/src/gtest/build
|
||||||
sudo cmake .. -DBUILD_SHARED_LIBS=1
|
sudo cmake .. -DBUILD_SHARED_LIBS=1
|
||||||
sudo make -j4
|
sudo make -j4
|
||||||
sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so
|
sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so
|
||||||
sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so
|
sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so
|
||||||
|
|
||||||
cd ~/
|
|
||||||
git clone https://github.com/TES3MP/CrabNet
|
|
||||||
cd CrabNet
|
|
||||||
cmake . -DCRABNET_ENABLE_DLL=OFF -DCRABNET_ENABLE_SAMPLES=OFF -DCMAKE_BUILD_TYPE=Release
|
|
||||||
make -j3
|
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
brew update
|
brew update
|
||||||
|
brew rm cmake || true
|
||||||
|
brew rm pkgconfig || true
|
||||||
|
brew rm qt5 || true
|
||||||
|
brew install cmake pkgconfig qt5
|
||||||
|
|
||||||
brew outdated cmake || brew upgrade cmake
|
curl http://downloads.openmw.org/osx/dependencies/openmw-deps-263d4a8.zip -o ~/openmw-deps.zip
|
||||||
brew outdated pkgconfig || brew upgrade pkgconfig
|
unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null
|
||||||
brew install qt
|
|
||||||
|
|
||||||
curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-100d2e0.zip -o ~/openmw-deps.zip
|
|
||||||
unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null
|
|
||||||
|
|
|
@ -3,37 +3,6 @@
|
||||||
free -m
|
free -m
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
|
export CODE_COVERAGE=1
|
||||||
# Set up compilers
|
if [ "${CC}" = "clang" ]; then export CODE_COVERAGE=0; fi
|
||||||
if [ ! -z "${MATRIX_CC}" ]; then
|
${ANALYZE}cmake .. -DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} -DBUILD_UNITTESTS=1 -DCMAKE_INSTALL_PREFIX=/usr -DBINDIR=/usr/games -DCMAKE_BUILD_TYPE="None" -DUSE_SYSTEM_TINYXML=TRUE
|
||||||
eval "${MATRIX_CC}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
export RAKNET_ROOT=~/CrabNet
|
|
||||||
|
|
||||||
export CODE_COVERAGE=0
|
|
||||||
if [ ! -z "${ANALYZE}" ]; then
|
|
||||||
CODE_COVERAGE=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
${ANALYZE}cmake .. \
|
|
||||||
-DDESIRED_QT_VERSION=5 \
|
|
||||||
-DBUILD_OPENMW_MP=ON \
|
|
||||||
-DBUILD_BROWSER=ON \
|
|
||||||
-DBUILD_MASTER=ON \
|
|
||||||
-DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} \
|
|
||||||
-DBUILD_BSATOOL=OFF \
|
|
||||||
-DBUILD_ESMTOOL=OFF \
|
|
||||||
-DBUILD_ESSIMPORTER=OFF \
|
|
||||||
-DBUILD_LAUNCHER=OFF \
|
|
||||||
-DBUILD_MWINIIMPORTER=OFF \
|
|
||||||
-DBUILD_MYGUI_PLUGIN=OFF \
|
|
||||||
-DBUILD_OPENCS=OFF \
|
|
||||||
-DBUILD_WIZARD=OFF \
|
|
||||||
-DBUILD_UNITTESTS=1 \
|
|
||||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
|
||||||
-DBINDIR=/usr/games \
|
|
||||||
-DCMAKE_BUILD_TYPE="None" \
|
|
||||||
-DUSE_SYSTEM_TINYXML=TRUE \
|
|
||||||
-DRakNet_LIBRARY_RELEASE=~/CrabNet/lib/libRakNetLibStatic.a \
|
|
||||||
-DRakNet_LIBRARY_DEBUG=~/CrabNet/lib/libRakNetLibStatic.a
|
|
||||||
|
|
572
CI/before_script.msvc.sh
Normal file → Executable file
572
CI/before_script.msvc.sh
Normal file → Executable file
|
@ -1,38 +1,4 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# set -x # turn-on for debugging
|
|
||||||
|
|
||||||
MISSINGTOOLS=0
|
|
||||||
|
|
||||||
command -v 7z >/dev/null 2>&1 || { echo "Error: 7z (7zip) is not on the path."; MISSINGTOOLS=1; }
|
|
||||||
command -v cmake >/dev/null 2>&1 || { echo "Error: cmake (CMake) is not on the path."; MISSINGTOOLS=1; }
|
|
||||||
|
|
||||||
if [ $MISSINGTOOLS -ne 0 ]; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
WORKINGDIR="$(pwd)"
|
|
||||||
case "$WORKINGDIR" in
|
|
||||||
*[[:space:]]*)
|
|
||||||
echo "Error: Working directory contains spaces."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
APPVEYOR=${APPVEYOR:-}
|
|
||||||
CI=${CI:-}
|
|
||||||
STEP=${STEP:-}
|
|
||||||
|
|
||||||
VERBOSE=""
|
|
||||||
STRIP=""
|
|
||||||
SKIP_DOWNLOAD=""
|
|
||||||
SKIP_EXTRACT=""
|
|
||||||
KEEP=""
|
|
||||||
UNITY_BUILD=""
|
|
||||||
VS_VERSION=""
|
|
||||||
PLATFORM=""
|
|
||||||
CONFIGURATION=""
|
|
||||||
|
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
ARGSTR=$1
|
ARGSTR=$1
|
||||||
|
@ -50,6 +16,10 @@ while [ $# -gt 0 ]; do
|
||||||
V )
|
V )
|
||||||
VERBOSE=true ;;
|
VERBOSE=true ;;
|
||||||
|
|
||||||
|
v )
|
||||||
|
VS_VERSION=$1
|
||||||
|
shift ;;
|
||||||
|
|
||||||
d )
|
d )
|
||||||
SKIP_DOWNLOAD=true ;;
|
SKIP_DOWNLOAD=true ;;
|
||||||
|
|
||||||
|
@ -62,10 +32,6 @@ while [ $# -gt 0 ]; do
|
||||||
u )
|
u )
|
||||||
UNITY_BUILD=true ;;
|
UNITY_BUILD=true ;;
|
||||||
|
|
||||||
v )
|
|
||||||
VS_VERSION=$1
|
|
||||||
shift ;;
|
|
||||||
|
|
||||||
p )
|
p )
|
||||||
PLATFORM=$1
|
PLATFORM=$1
|
||||||
shift ;;
|
shift ;;
|
||||||
|
@ -77,6 +43,7 @@ while [ $# -gt 0 ]; do
|
||||||
h )
|
h )
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
Usage: $0 [-cdehkpuvV]
|
Usage: $0 [-cdehkpuvV]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-c <Release/Debug>
|
-c <Release/Debug>
|
||||||
Set the configuration, can also be set with environment variable CONFIGURATION.
|
Set the configuration, can also be set with environment variable CONFIGURATION.
|
||||||
|
@ -92,7 +59,7 @@ Options:
|
||||||
Set the build platform, can also be set with environment variable PLATFORM.
|
Set the build platform, can also be set with environment variable PLATFORM.
|
||||||
-u
|
-u
|
||||||
Configure for unity builds.
|
Configure for unity builds.
|
||||||
-v <2013/2015/2017>
|
-v <2013/2015>
|
||||||
Choose the Visual Studio version to use.
|
Choose the Visual Studio version to use.
|
||||||
-V
|
-V
|
||||||
Run verbosely
|
Run verbosely
|
||||||
|
@ -111,6 +78,9 @@ done
|
||||||
if [ -z $VERBOSE ]; then
|
if [ -z $VERBOSE ]; then
|
||||||
STRIP="> /dev/null 2>&1"
|
STRIP="> /dev/null 2>&1"
|
||||||
fi
|
fi
|
||||||
|
if [ -z $VS_VERSION ]; then
|
||||||
|
VS_VERSION="2013"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -z $APPVEYOR ]; then
|
if [ -z $APPVEYOR ]; then
|
||||||
echo "Running prebuild outside of Appveyor."
|
echo "Running prebuild outside of Appveyor."
|
||||||
|
@ -120,7 +90,9 @@ if [ -z $APPVEYOR ]; then
|
||||||
else
|
else
|
||||||
echo "Running prebuild in Appveyor."
|
echo "Running prebuild in Appveyor."
|
||||||
|
|
||||||
cd "$APPVEYOR_BUILD_FOLDER"
|
cd $APPVEYOR_BUILD_FOLDER
|
||||||
|
VERSION="$(cat README.md | grep Version: | awk '{ print $3; }')-$(git rev-parse --short HEAD)"
|
||||||
|
appveyor UpdateBuild -Version "$VERSION" > /dev/null &
|
||||||
fi
|
fi
|
||||||
|
|
||||||
run_cmd() {
|
run_cmd() {
|
||||||
|
@ -133,7 +105,7 @@ run_cmd() {
|
||||||
|
|
||||||
if [ $RET -ne 0 ]; then
|
if [ $RET -ne 0 ]; then
|
||||||
if [ -z $APPVEYOR ]; then
|
if [ -z $APPVEYOR ]; then
|
||||||
echo "Command $CMD failed, output can be found in $(real_pwd)/output.log"
|
echo "Command $CMD failed, output can be found in `real_pwd`/output.log"
|
||||||
else
|
else
|
||||||
echo
|
echo
|
||||||
echo "Command $CMD failed;"
|
echo "Command $CMD failed;"
|
||||||
|
@ -212,69 +184,44 @@ add_osg_dlls() {
|
||||||
OSG_PLUGINS="$OSG_PLUGINS $@"
|
OSG_PLUGINS="$OSG_PLUGINS $@"
|
||||||
}
|
}
|
||||||
|
|
||||||
QT_PLATFORMS=""
|
|
||||||
add_qt_platform_dlls() {
|
|
||||||
QT_PLATFORMS="$QT_PLATFORMS $@"
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ -z $PLATFORM ]; then
|
if [ -z $PLATFORM ]; then
|
||||||
PLATFORM="$(uname -m)"
|
PLATFORM=`uname -m`
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z $CONFIGURATION ]; then
|
if [ -z $CONFIGURATION ]; then
|
||||||
CONFIGURATION="Debug"
|
CONFIGURATION="Debug"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z $VS_VERSION ]; then
|
|
||||||
VS_VERSION="2013"
|
|
||||||
fi
|
|
||||||
|
|
||||||
case $VS_VERSION in
|
case $VS_VERSION in
|
||||||
15|15.0|2017 )
|
14|2015 )
|
||||||
GENERATOR="Visual Studio 15 2017"
|
|
||||||
TOOLSET="vc141"
|
|
||||||
MSVC_REAL_VER="15"
|
|
||||||
MSVC_VER="14.1"
|
|
||||||
MSVC_YEAR="2015"
|
|
||||||
MSVC_DISPLAY_YEAR="2017"
|
|
||||||
;;
|
|
||||||
|
|
||||||
14|14.0|2015 )
|
|
||||||
GENERATOR="Visual Studio 14 2015"
|
GENERATOR="Visual Studio 14 2015"
|
||||||
TOOLSET="vc140"
|
XP_TOOLSET="v140_xp"
|
||||||
MSVC_REAL_VER="14"
|
|
||||||
MSVC_VER="14.0"
|
|
||||||
MSVC_YEAR="2015"
|
|
||||||
MSVC_DISPLAY_YEAR="2015"
|
|
||||||
;;
|
;;
|
||||||
|
|
||||||
12|12.0|2013 )
|
# 12|2013|
|
||||||
|
* )
|
||||||
GENERATOR="Visual Studio 12 2013"
|
GENERATOR="Visual Studio 12 2013"
|
||||||
TOOLSET="vc120"
|
XP_TOOLSET="v120_xp"
|
||||||
MSVC_REAL_VER="12"
|
|
||||||
MSVC_VER="12.0"
|
|
||||||
MSVC_YEAR="2013"
|
|
||||||
MSVC_DISPLAY_YEAR="2013"
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
case $PLATFORM in
|
case $PLATFORM in
|
||||||
x64|x86_64|x86-64|win64|Win64 )
|
x64|x86_64|x86-64|win64|Win64 )
|
||||||
ARCHNAME="x86-64"
|
ARCHNAME=x86-64
|
||||||
ARCHSUFFIX="64"
|
ARCHSUFFIX=64
|
||||||
BITS="64"
|
BITS=64
|
||||||
|
|
||||||
BASE_OPTS="-G\"$GENERATOR Win64\""
|
BASE_OPTS="-G\"$GENERATOR Win64\""
|
||||||
add_cmake_opts "-G\"$GENERATOR Win64\""
|
add_cmake_opts "-G\"$GENERATOR Win64\""
|
||||||
;;
|
;;
|
||||||
|
|
||||||
x32|x86|i686|i386|win32|Win32 )
|
x32|x86|i686|i386|win32|Win32 )
|
||||||
ARCHNAME="x86"
|
ARCHNAME=x86
|
||||||
ARCHSUFFIX="86"
|
ARCHSUFFIX=86
|
||||||
BITS="32"
|
BITS=32
|
||||||
|
|
||||||
BASE_OPTS="-G\"$GENERATOR\""
|
BASE_OPTS="-G\"$GENERATOR\" -T$XP_TOOLSET"
|
||||||
add_cmake_opts "-G\"$GENERATOR\""
|
add_cmake_opts "-G\"$GENERATOR\"" -T$XP_TOOLSET
|
||||||
;;
|
;;
|
||||||
|
|
||||||
* )
|
* )
|
||||||
|
@ -283,38 +230,35 @@ case $PLATFORM in
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
case $CONFIGURATION in
|
|
||||||
debug|Debug|DEBUG )
|
|
||||||
CONFIGURATION=Debug
|
|
||||||
BUILD_CONFIG=Debug
|
|
||||||
;;
|
|
||||||
|
|
||||||
release|Release|RELEASE )
|
|
||||||
CONFIGURATION=Release
|
|
||||||
BUILD_CONFIG=Release
|
|
||||||
;;
|
|
||||||
|
|
||||||
relwithdebinfo|RelWithDebInfo|RELWITHDEBINFO )
|
|
||||||
CONFIGURATION=Release
|
|
||||||
BUILD_CONFIG=RelWithDebInfo
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if ! [ -z $UNITY_BUILD ]; then
|
if ! [ -z $UNITY_BUILD ]; then
|
||||||
add_cmake_opts "-DOPENMW_UNITY_BUILD=True"
|
add_cmake_opts "-DOPENMW_UNITY_BUILD=True"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
case $CONFIGURATION in
|
||||||
|
debug|Debug|DEBUG )
|
||||||
|
CONFIGURATION=Debug
|
||||||
|
;;
|
||||||
|
|
||||||
|
release|Release|RELEASE )
|
||||||
|
CONFIGURATION=Release
|
||||||
|
;;
|
||||||
|
|
||||||
|
relwithdebinfo|RelWithDebInfo|RELWITHDEBINFO )
|
||||||
|
CONFIGURATION=RelWithDebInfo
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "==================================="
|
echo "=========================="
|
||||||
echo "Starting prebuild on MSVC${MSVC_DISPLAY_YEAR} WIN${BITS}"
|
echo "Starting prebuild on win$BITS"
|
||||||
echo "==================================="
|
echo "=========================="
|
||||||
echo
|
echo
|
||||||
|
|
||||||
# cd OpenMW/AppVeyor-test
|
# cd OpenMW/AppVeyor-test
|
||||||
mkdir -p deps
|
mkdir -p deps
|
||||||
cd deps
|
cd deps
|
||||||
|
|
||||||
DEPS="$(pwd)"
|
DEPS="`pwd`"
|
||||||
|
|
||||||
if [ -z $SKIP_DOWNLOAD ]; then
|
if [ -z $SKIP_DOWNLOAD ]; then
|
||||||
echo "Downloading dependency packages."
|
echo "Downloading dependency packages."
|
||||||
|
@ -322,186 +266,176 @@ if [ -z $SKIP_DOWNLOAD ]; then
|
||||||
|
|
||||||
# Boost
|
# Boost
|
||||||
if [ -z $APPVEYOR ]; then
|
if [ -z $APPVEYOR ]; then
|
||||||
download "Boost 1.67.0" \
|
download "Boost 1.58.0" \
|
||||||
"https://sourceforge.net/projects/boost/files/boost-binaries/1.67.0/boost_1_67_0-msvc-${MSVC_VER}-${BITS}.exe" \
|
http://sourceforge.net/projects/boost/files/boost-binaries/1.58.0/boost_1_58_0-msvc-12.0-$BITS.exe \
|
||||||
"boost-1.67.0-msvc${MSVC_YEAR}-win${BITS}.exe"
|
boost-1.58.0-win$BITS.exe
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Bullet
|
# Bullet
|
||||||
download "Bullet 2.86" \
|
download "Bullet 2.83.5" \
|
||||||
"https://www.lysator.liu.se/~ace/OpenMW/deps/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" \
|
http://www.lysator.liu.se/~ace/OpenMW/deps/Bullet-2.83.5-win$BITS.7z \
|
||||||
"Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z"
|
Bullet-2.83.5-win$BITS.7z
|
||||||
|
|
||||||
# FFmpeg
|
# FFmpeg
|
||||||
download "FFmpeg 3.2.4" \
|
download "FFmpeg 2.5.2" \
|
||||||
"https://ffmpeg.zeranoe.com/builds/win${BITS}/shared/ffmpeg-3.2.4-win${BITS}-shared.zip" \
|
http://ffmpeg.zeranoe.com/builds/win$BITS/shared/ffmpeg-2.5.2-win$BITS-shared.7z \
|
||||||
"ffmpeg-3.2.4-win${BITS}.zip" \
|
ffmpeg$BITS-2.5.2.7z \
|
||||||
"https://ffmpeg.zeranoe.com/builds/win${BITS}/dev/ffmpeg-3.2.4-win${BITS}-dev.zip" \
|
http://ffmpeg.zeranoe.com/builds/win$BITS/dev/ffmpeg-2.5.2-win$BITS-dev.7z \
|
||||||
"ffmpeg-3.2.4-dev-win${BITS}.zip"
|
ffmpeg$BITS-2.5.2-dev.7z
|
||||||
|
|
||||||
# MyGUI
|
# MyGUI
|
||||||
download "MyGUI 3.2.2" \
|
download "MyGUI 3.2.2" \
|
||||||
"https://www.lysator.liu.se/~ace/OpenMW/deps/MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z" \
|
http://www.lysator.liu.se/~ace/OpenMW/deps/MyGUI-3.2.2-win$BITS.7z \
|
||||||
"MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z"
|
MyGUI-3.2.2-win$BITS.7z
|
||||||
|
|
||||||
# OpenAL
|
# OpenAL
|
||||||
download "OpenAL-Soft 1.17.2" \
|
download "OpenAL-Soft 1.16.0" \
|
||||||
"http://kcat.strangesoft.net/openal-binaries/openal-soft-1.17.2-bin.zip" \
|
http://kcat.strangesoft.net/openal-binaries/openal-soft-1.16.0-bin.zip \
|
||||||
"OpenAL-Soft-1.17.2.zip"
|
OpenAL-Soft-1.16.0.zip
|
||||||
|
|
||||||
# OSG
|
# OSG
|
||||||
download "OpenSceneGraph 3.4.1-scrawl" \
|
download "OpenSceneGraph 3.3.8" \
|
||||||
"https://www.lysator.liu.se/~ace/OpenMW/deps/OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" \
|
http://www.lysator.liu.se/~ace/OpenMW/deps/OSG-3.3.8-win$BITS.7z \
|
||||||
"OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z"
|
OSG-3.3.8-win$BITS.7z
|
||||||
|
|
||||||
# Qt
|
# Qt
|
||||||
if [ -z $APPVEYOR ]; then
|
if [ -z $APPVEYOR ]; then
|
||||||
if [ $BITS == "64" ]; then
|
download "Qt 4.8.6" \
|
||||||
QT_SUFFIX="_64"
|
http://sourceforge.net/projects/qt64ng/files/qt/$ARCHNAME/4.8.6/msvc2013/qt-4.8.6-x$ARCHSUFFIX-msvc2013.7z \
|
||||||
else
|
qt$BITS-4.8.6.7z
|
||||||
QT_SUFFIX=""
|
|
||||||
fi
|
|
||||||
|
|
||||||
download "Qt 5.7.0" \
|
|
||||||
"https://download.qt.io/archive/qt/5.7/5.7.0/qt-opensource-windows-x86-msvc${MSVC_YEAR}${QT_SUFFIX}-5.7.0.exe" \
|
|
||||||
"qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" \
|
|
||||||
"https://www.lysator.liu.se/~ace/OpenMW/deps/qt-5-install.qs" \
|
|
||||||
"qt-5-install.qs"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# SDL2
|
# SDL2
|
||||||
download "SDL 2.0.7" \
|
download "SDL 2.0.3" \
|
||||||
"https://www.libsdl.org/release/SDL2-devel-2.0.7-VC.zip" \
|
https://www.libsdl.org/release/SDL2-devel-2.0.3-VC.zip \
|
||||||
"SDL2-2.0.7.zip"
|
SDL2-2.0.3.zip
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd .. #/..
|
cd .. #/..
|
||||||
|
|
||||||
# Set up dependencies
|
# Set up dependencies
|
||||||
BUILD_DIR="MSVC${MSVC_DISPLAY_YEAR}_${BITS}"
|
|
||||||
if [ -z $KEEP ]; then
|
if [ -z $KEEP ]; then
|
||||||
echo
|
echo
|
||||||
echo "(Re)Creating build directory."
|
printf "Preparing build directory... "
|
||||||
|
|
||||||
rm -rf "$BUILD_DIR"
|
rm -rf Build_$BITS
|
||||||
|
mkdir -p Build_$BITS/deps
|
||||||
|
|
||||||
|
echo Done.
|
||||||
fi
|
fi
|
||||||
|
mkdir -p Build_$BITS/deps
|
||||||
|
cd Build_$BITS/deps
|
||||||
|
|
||||||
mkdir -p "${BUILD_DIR}/deps"
|
DEPS_INSTALL=`pwd`
|
||||||
cd "${BUILD_DIR}/deps"
|
|
||||||
|
|
||||||
DEPS_INSTALL="$(pwd)"
|
|
||||||
cd $DEPS
|
cd $DEPS
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "Extracting dependencies, this might take a while..."
|
echo "Extracting dependencies..."
|
||||||
echo "---------------------------------------------------"
|
|
||||||
echo
|
|
||||||
|
|
||||||
|
|
||||||
# Boost
|
# Boost
|
||||||
if [ -z $APPVEYOR ]; then
|
printf "Boost 1.58.0... "
|
||||||
printf "Boost 1.67.0... "
|
|
||||||
else
|
|
||||||
if [ $MSVC_VER -eq 12.0 ]; then
|
|
||||||
printf "Boost 1.58.0 AppVeyor... "
|
|
||||||
else
|
|
||||||
printf "Boost 1.67.0 AppVeyor... "
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
{
|
{
|
||||||
if [ -z $APPVEYOR ]; then
|
if [ -z $APPVEYOR ]; then
|
||||||
cd $DEPS_INSTALL
|
cd $DEPS_INSTALL
|
||||||
|
|
||||||
BOOST_SDK="$(real_pwd)/Boost"
|
BOOST_SDK="`real_pwd`/Boost"
|
||||||
|
|
||||||
# Boost's installer is still based on ms-dos API that doesn't support larger than 260 char path names
|
if [ -d Boost ] && grep "BOOST_VERSION 105800" Boost/boost/version.hpp > /dev/null; then
|
||||||
# We work around this by installing to root of the current working drive and then move it to our deps
|
|
||||||
# get the current working drive's root, we'll install to that temporarily
|
|
||||||
CWD_DRIVE_ROOT="$(powershell -command '(get-location).Drive.Root')Boost_temp"
|
|
||||||
CWD_DRIVE_ROOT_BASH=$(echo "$CWD_DRIVE_ROOT" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
|
|
||||||
if [ -d CWD_DRIVE_ROOT_BASH ]; then
|
|
||||||
printf "Cannot continue, ${CWD_DRIVE_ROOT_BASH} aka ${CWD_DRIVE_ROOT} already exists. Please remove before re-running. ";
|
|
||||||
exit 1;
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -d ${BOOST_SDK} ] && grep "BOOST_VERSION 106700" Boost/boost/version.hpp > /dev/null; then
|
|
||||||
printf "Exists. "
|
printf "Exists. "
|
||||||
elif [ -z $SKIP_EXTRACT ]; then
|
elif [ -z $SKIP_EXTRACT ]; then
|
||||||
rm -rf Boost
|
rm -rf Boost
|
||||||
CI_EXTRA_INNO_OPTIONS=""
|
$DEPS/boost-1.58.0-win$BITS.exe //dir="$(echo $BOOST_SDK | sed s,/,\\\\,g)" //verysilent
|
||||||
[ -n "$CI" ] && CI_EXTRA_INNO_OPTIONS="//SUPPRESSMSGBOXES //LOG='boost_install.log'"
|
|
||||||
"${DEPS}/boost-1.67.0-msvc${MSVC_YEAR}-win${BITS}.exe" //DIR="${CWD_DRIVE_ROOT}" //VERYSILENT //NORESTART ${CI_EXTRA_INNO_OPTIONS}
|
|
||||||
mv "${CWD_DRIVE_ROOT_BASH}" "${BOOST_SDK}"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
|
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
|
||||||
-DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}"
|
-DBOOST_LIBRARYDIR="$BOOST_SDK/lib$BITS-msvc-12.0"
|
||||||
add_cmake_opts -DBoost_COMPILER="-${TOOLSET}"
|
|
||||||
echo Done.
|
echo Done.
|
||||||
else
|
else
|
||||||
# Appveyor unstable has all the boost we need already
|
# Appveyor unstable has all the boost we need already
|
||||||
if [ $MSVC_REAL_VER -eq 12 ]; then
|
BOOST_SDK="c:/Libraries/boost"
|
||||||
BOOST_SDK="c:/Libraries/boost_1_58_0"
|
|
||||||
else
|
|
||||||
BOOST_SDK="c:/Libraries/boost_1_67_0"
|
|
||||||
fi
|
|
||||||
if [ $MSVC_REAL_VER -eq 15 ]; then
|
|
||||||
LIB_SUFFIX="1"
|
|
||||||
else
|
|
||||||
LIB_SUFFIX="0"
|
|
||||||
fi
|
|
||||||
|
|
||||||
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
|
add_cmake_opts -DBOOST_ROOT="$BOOST_SDK" \
|
||||||
-DBOOST_LIBRARYDIR="${BOOST_SDK}/lib${BITS}-msvc-${MSVC_VER}.${LIB_SUFFIX}"
|
-DBOOST_LIBRARYDIR="$BOOST_SDK/lib$BITS-msvc-12.0"
|
||||||
add_cmake_opts -DBoost_COMPILER="-${TOOLSET}"
|
|
||||||
|
|
||||||
echo Done.
|
echo AppVeyor.
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
cd $DEPS
|
cd $DEPS
|
||||||
echo
|
|
||||||
# Bullet
|
# Bullet
|
||||||
printf "Bullet 2.86... "
|
printf "Bullet 2.83.5... "
|
||||||
{
|
{
|
||||||
cd $DEPS_INSTALL
|
cd $DEPS_INSTALL
|
||||||
|
|
||||||
if [ -d Bullet ]; then
|
if [ -d Bullet ]; then
|
||||||
printf -- "Exists. (No version checking) "
|
printf "Exists. (No version checking) "
|
||||||
elif [ -z $SKIP_EXTRACT ]; then
|
elif [ -z $SKIP_EXTRACT ]; then
|
||||||
rm -rf Bullet
|
rm -rf Bullet
|
||||||
eval 7z x -y "${DEPS}/Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
|
eval 7z x -y $DEPS/Bullet-2.83.5-win$BITS.7z $STRIP
|
||||||
mv "Bullet-2.86-msvc${MSVC_YEAR}-win${BITS}" Bullet
|
mv Bullet-2.83.5-win$BITS Bullet
|
||||||
fi
|
fi
|
||||||
export BULLET_ROOT="$(real_pwd)/Bullet"
|
|
||||||
|
BULLET_SDK="`real_pwd`/Bullet"
|
||||||
|
add_cmake_opts -DBULLET_INCLUDE_DIR="$BULLET_SDK/include/bullet" \
|
||||||
|
-DBULLET_COLLISION_LIBRARY="$BULLET_SDK/lib/BulletCollision.lib" \
|
||||||
|
-DBULLET_COLLISION_LIBRARY_DEBUG="$BULLET_SDK/lib/BulletCollision_Debug.lib" \
|
||||||
|
-DBULLET_MATH_LIBRARY="$BULLET_SDK/lib/LinearMath.lib" \
|
||||||
|
-DBULLET_MATH_LIBRARY_DEBUG="$BULLET_SDK/lib/LinearMath_Debug.lib"
|
||||||
|
|
||||||
echo Done.
|
echo Done.
|
||||||
}
|
}
|
||||||
cd $DEPS
|
cd $DEPS
|
||||||
echo
|
|
||||||
# FFmpeg
|
# FFmpeg
|
||||||
printf "FFmpeg 3.2.4... "
|
printf "FFmpeg 2.5.2... "
|
||||||
{
|
{
|
||||||
cd $DEPS_INSTALL
|
cd $DEPS_INSTALL
|
||||||
if [ -d FFmpeg ] && grep "FFmpeg version: 3.2.4" FFmpeg/README.txt > /dev/null; then
|
|
||||||
|
if [ -d FFmpeg ] && grep "FFmpeg version: 2.5.2" FFmpeg/README.txt > /dev/null; then
|
||||||
printf "Exists. "
|
printf "Exists. "
|
||||||
elif [ -z $SKIP_EXTRACT ]; then
|
elif [ -z $SKIP_EXTRACT ]; then
|
||||||
rm -rf FFmpeg
|
rm -rf FFmpeg
|
||||||
eval 7z x -y "${DEPS}/ffmpeg-3.2.4-win${BITS}.zip" $STRIP
|
|
||||||
eval 7z x -y "${DEPS}/ffmpeg-3.2.4-dev-win${BITS}.zip" $STRIP
|
eval 7z x -y $DEPS/ffmpeg$BITS-2.5.2.7z $STRIP
|
||||||
mv "ffmpeg-3.2.4-win${BITS}-shared" FFmpeg
|
eval 7z x -y $DEPS/ffmpeg$BITS-2.5.2-dev.7z $STRIP
|
||||||
cp -r "ffmpeg-3.2.4-win${BITS}-dev/"* FFmpeg/
|
|
||||||
rm -rf "ffmpeg-3.2.4-win${BITS}-dev"
|
mv ffmpeg-2.5.2-win$BITS-shared FFmpeg
|
||||||
|
cp -r ffmpeg-2.5.2-win$BITS-dev/* FFmpeg/
|
||||||
|
rm -rf ffmpeg-2.5.2-win$BITS-dev
|
||||||
fi
|
fi
|
||||||
export FFMPEG_HOME="$(real_pwd)/FFmpeg"
|
|
||||||
add_runtime_dlls "$(pwd)/FFmpeg/bin/"{avcodec-57,avformat-57,avutil-55,swresample-2,swscale-4}.dll
|
FFMPEG_SDK="`real_pwd`/FFmpeg"
|
||||||
|
add_cmake_opts -DAVCODEC_INCLUDE_DIRS="$FFMPEG_SDK/include" \
|
||||||
|
-DAVCODEC_LIBRARIES="$FFMPEG_SDK/lib/avcodec.lib" \
|
||||||
|
-DAVDEVICE_INCLUDE_DIRS="$FFMPEG_SDK/include" \
|
||||||
|
-DAVDEVICE_LIBRARIES="$FFMPEG_SDK/lib/avdevice.lib" \
|
||||||
|
-DAVFORMAT_INCLUDE_DIRS="$FFMPEG_SDK/include" \
|
||||||
|
-DAVFORMAT_LIBRARIES="$FFMPEG_SDK/lib/avformat.lib" \
|
||||||
|
-DAVUTIL_INCLUDE_DIRS="$FFMPEG_SDK/include" \
|
||||||
|
-DAVUTIL_LIBRARIES="$FFMPEG_SDK/lib/avutil.lib" \
|
||||||
|
-DPOSTPROC_INCLUDE_DIRS="$FFMPEG_SDK/include" \
|
||||||
|
-DPOSTPROC_LIBRARIES="$FFMPEG_SDK/lib/postproc.lib" \
|
||||||
|
-DSWRESAMPLE_INCLUDE_DIRS="$FFMPEG_SDK/include" \
|
||||||
|
-DSWRESAMPLE_LIBRARIES="$FFMPEG_SDK/lib/swresample.lib" \
|
||||||
|
-DSWSCALE_INCLUDE_DIRS="$FFMPEG_SDK/include" \
|
||||||
|
-DSWSCALE_LIBRARIES="$FFMPEG_SDK/lib/swscale.lib"
|
||||||
|
|
||||||
|
add_runtime_dlls `pwd`/FFmpeg/bin/{avcodec-56,avformat-56,avutil-54,swresample-1,swscale-3}.dll
|
||||||
|
|
||||||
if [ $BITS -eq 32 ]; then
|
if [ $BITS -eq 32 ]; then
|
||||||
add_cmake_opts "-DCMAKE_EXE_LINKER_FLAGS=\"/machine:X86 /safeseh:no\""
|
add_cmake_opts "-DCMAKE_EXE_LINKER_FLAGS=\"/machine:X86 /safeseh:no\""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo Done.
|
echo Done.
|
||||||
}
|
}
|
||||||
cd $DEPS
|
cd $DEPS
|
||||||
echo
|
|
||||||
# MyGUI
|
# MyGUI
|
||||||
printf "MyGUI 3.2.2... "
|
printf "MyGUI 3.2.2... "
|
||||||
{
|
{
|
||||||
cd $DEPS_INSTALL
|
cd $DEPS_INSTALL
|
||||||
|
|
||||||
if [ -d MyGUI ] && \
|
if [ -d MyGUI ] && \
|
||||||
grep "MYGUI_VERSION_MAJOR 3" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
|
grep "MYGUI_VERSION_MAJOR 3" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
|
||||||
grep "MYGUI_VERSION_MINOR 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
|
grep "MYGUI_VERSION_MINOR 2" MyGUI/include/MYGUI/MyGUI_Prerequest.h > /dev/null && \
|
||||||
|
@ -510,151 +444,170 @@ printf "MyGUI 3.2.2... "
|
||||||
printf "Exists. "
|
printf "Exists. "
|
||||||
elif [ -z $SKIP_EXTRACT ]; then
|
elif [ -z $SKIP_EXTRACT ]; then
|
||||||
rm -rf MyGUI
|
rm -rf MyGUI
|
||||||
eval 7z x -y "${DEPS}/MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
|
eval 7z x -y $DEPS/MyGUI-3.2.2-win$BITS.7z $STRIP
|
||||||
mv "MyGUI-3.2.2-msvc${MSVC_YEAR}-win${BITS}" MyGUI
|
mv MyGUI-3.2.2-win$BITS MyGUI
|
||||||
fi
|
fi
|
||||||
export MYGUI_HOME="$(real_pwd)/MyGUI"
|
|
||||||
|
MYGUI_SDK="`real_pwd`/MyGUI"
|
||||||
|
|
||||||
|
add_cmake_opts -DMYGUISDK="$MYGUI_SDK" \
|
||||||
|
-DMYGUI_INCLUDE_DIRS="$MYGUI_SDK/include/MYGUI" \
|
||||||
|
-DMYGUI_PREQUEST_FILE="$MYGUI_SDK/include/MYGUI/MyGUI_Prerequest.h"
|
||||||
|
|
||||||
if [ $CONFIGURATION == "Debug" ]; then
|
if [ $CONFIGURATION == "Debug" ]; then
|
||||||
SUFFIX="_d"
|
SUFFIX="_d"
|
||||||
else
|
else
|
||||||
SUFFIX=""
|
SUFFIX=""
|
||||||
fi
|
fi
|
||||||
add_runtime_dlls "$(pwd)/MyGUI/bin/${CONFIGURATION}/MyGUIEngine${SUFFIX}.dll"
|
add_runtime_dlls `pwd`/MyGUI/bin/$CONFIGURATION/MyGUIEngine$SUFFIX.dll
|
||||||
|
|
||||||
echo Done.
|
echo Done.
|
||||||
}
|
}
|
||||||
cd $DEPS
|
cd $DEPS
|
||||||
echo
|
|
||||||
# OpenAL
|
# OpenAL
|
||||||
printf "OpenAL-Soft 1.17.2... "
|
printf "OpenAL-Soft 1.16.0... "
|
||||||
{
|
{
|
||||||
if [ -d openal-soft-1.17.2-bin ]; then
|
if [ -d openal-soft-1.16.0-bin ]; then
|
||||||
printf "Exists. "
|
printf "Exists. "
|
||||||
elif [ -z $SKIP_EXTRACT ]; then
|
elif [ -z $SKIP_EXTRACT ]; then
|
||||||
rm -rf openal-soft-1.17.2-bin
|
rm -rf openal-soft-1.16.0-bin
|
||||||
eval 7z x -y OpenAL-Soft-1.17.2.zip $STRIP
|
eval 7z x -y OpenAL-Soft-1.16.0.zip $STRIP
|
||||||
fi
|
fi
|
||||||
OPENAL_SDK="$(real_pwd)/openal-soft-1.17.2-bin"
|
|
||||||
add_cmake_opts -DOPENAL_INCLUDE_DIR="${OPENAL_SDK}/include/AL" \
|
OPENAL_SDK="`real_pwd`/openal-soft-1.16.0-bin"
|
||||||
-DOPENAL_LIBRARY="${OPENAL_SDK}/libs/Win${BITS}/OpenAL32.lib"
|
|
||||||
add_runtime_dlls "$(pwd)/openal-soft-1.17.2-bin/bin/WIN${BITS}/soft_oal.dll:OpenAL32.dll"
|
add_cmake_opts -DOPENAL_INCLUDE_DIR="$OPENAL_SDK/include/AL" \
|
||||||
|
-DOPENAL_LIBRARY="$OPENAL_SDK/libs/Win$BITS/OpenAL32.lib"
|
||||||
|
|
||||||
echo Done.
|
echo Done.
|
||||||
}
|
}
|
||||||
cd $DEPS
|
cd $DEPS
|
||||||
echo
|
|
||||||
# OSG
|
# OSG
|
||||||
printf "OSG 3.4.1-scrawl... "
|
printf "OSG 3.3.8... "
|
||||||
{
|
{
|
||||||
cd $DEPS_INSTALL
|
cd $DEPS_INSTALL
|
||||||
|
|
||||||
if [ -d OSG ] && \
|
if [ -d OSG ] && \
|
||||||
grep "OPENSCENEGRAPH_MAJOR_VERSION 3" OSG/include/osg/Version > /dev/null && \
|
grep "OPENSCENEGRAPH_MAJOR_VERSION 3" OSG/include/osg/Version > /dev/null && \
|
||||||
grep "OPENSCENEGRAPH_MINOR_VERSION 4" OSG/include/osg/Version > /dev/null && \
|
grep "OPENSCENEGRAPH_MINOR_VERSION 3" OSG/include/osg/Version > /dev/null && \
|
||||||
grep "OPENSCENEGRAPH_PATCH_VERSION 1" OSG/include/osg/Version > /dev/null
|
grep "OPENSCENEGRAPH_PATCH_VERSION 8" OSG/include/osg/Version > /dev/null
|
||||||
then
|
then
|
||||||
printf "Exists. "
|
printf "Exists. "
|
||||||
elif [ -z $SKIP_EXTRACT ]; then
|
elif [ -z $SKIP_EXTRACT ]; then
|
||||||
rm -rf OSG
|
rm -rf OSG
|
||||||
eval 7z x -y "${DEPS}/OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}.7z" $STRIP
|
eval 7z x -y $DEPS/OSG-3.3.8-win$BITS.7z $STRIP
|
||||||
mv "OSG-3.4.1-scrawl-msvc${MSVC_YEAR}-win${BITS}" OSG
|
mv OSG-3.3.8-win$BITS OSG
|
||||||
fi
|
fi
|
||||||
OSG_SDK="$(real_pwd)/OSG"
|
|
||||||
|
OSG_SDK="`real_pwd`/OSG"
|
||||||
|
|
||||||
add_cmake_opts -DOSG_DIR="$OSG_SDK"
|
add_cmake_opts -DOSG_DIR="$OSG_SDK"
|
||||||
|
|
||||||
if [ $CONFIGURATION == "Debug" ]; then
|
if [ $CONFIGURATION == "Debug" ]; then
|
||||||
SUFFIX="d"
|
SUFFIX="d"
|
||||||
else
|
else
|
||||||
SUFFIX=""
|
SUFFIX=""
|
||||||
fi
|
fi
|
||||||
add_runtime_dlls "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng*}${SUFFIX}.dll \
|
|
||||||
"$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer}${SUFFIX}.dll
|
add_runtime_dlls `pwd`/OSG/bin/{OpenThreads,zlib}$SUFFIX.dll \
|
||||||
add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_"{bmp,dds,jpeg,osg,png,tga}${SUFFIX}.dll
|
`pwd`/OSG/bin/osg{,Animation,DB,FX,GA,Particle,Qt,Text,Util,Viewer}$SUFFIX.dll
|
||||||
add_osg_dlls "$(pwd)/OSG/bin/osgPlugins-3.4.1/osgdb_serializers_osg"{,animation,fx,ga,particle,text,util,viewer}${SUFFIX}.dll
|
|
||||||
|
add_osg_dlls `pwd`/OSG/bin/osgPlugins-3.3.8/osgdb_{bmp,dds,gif,jpeg,png,tga}$SUFFIX.dll
|
||||||
|
|
||||||
echo Done.
|
echo Done.
|
||||||
}
|
}
|
||||||
cd $DEPS
|
cd $DEPS
|
||||||
echo
|
|
||||||
# Qt
|
# Qt
|
||||||
if [ -z $APPVEYOR ]; then
|
if [ -z $APPVEYOR ]; then
|
||||||
printf "Qt 5.7.0... "
|
printf "Qt 4.8.6... "
|
||||||
else
|
else
|
||||||
printf "Qt 5.10 AppVeyor... "
|
printf "Qt 5.4... "
|
||||||
fi
|
fi
|
||||||
{
|
{
|
||||||
if [ $BITS -eq 64 ]; then
|
|
||||||
SUFFIX="_64"
|
|
||||||
else
|
|
||||||
SUFFIX=""
|
|
||||||
fi
|
|
||||||
if [ -z $APPVEYOR ]; then
|
if [ -z $APPVEYOR ]; then
|
||||||
cd $DEPS_INSTALL
|
cd $DEPS_INSTALL
|
||||||
QT_SDK="$(real_pwd)/Qt/5.7/msvc${MSVC_YEAR}${SUFFIX}"
|
QT_SDK="`real_pwd`/Qt"
|
||||||
if [ -d Qt ] && head -n2 Qt/InstallationLog.txt | grep "5.7.0" > /dev/null; then
|
|
||||||
|
if [ -d Qt ] && head -n2 Qt/BUILDINFO.txt | grep "4.8.6" > /dev/null; then
|
||||||
printf "Exists. "
|
printf "Exists. "
|
||||||
elif [ -z $SKIP_EXTRACT ]; then
|
elif [ -z $SKIP_EXTRACT ]; then
|
||||||
rm -rf Qt
|
rm -rf Qt
|
||||||
cp "${DEPS}/qt-5-install.qs" qt-install.qs
|
eval 7z x -y $DEPS/qt$BITS-4.8.6.7z $STRIP
|
||||||
sed -i "s|INSTALL_DIR|$(real_pwd)/Qt|" qt-install.qs
|
mv qt-4.8.6-* Qt
|
||||||
sed -i "s/qt.VERSION.winBITS_msvcYEAR/qt.57.win${BITS}_msvc${MSVC_YEAR}${SUFFIX}/" qt-install.qs
|
cd Qt
|
||||||
printf -- "(Installation might take a while) "
|
eval ./qtbinpatcher.exe $STRIP
|
||||||
"${DEPS}/qt-5.7.0-msvc${MSVC_YEAR}-win${BITS}.exe" --script qt-install.qs --silent
|
|
||||||
mv qt-install.qs Qt/
|
|
||||||
echo Done.
|
|
||||||
printf " Cleaning up extraneous data... "
|
|
||||||
rm -r "$(real_pwd)/Qt/"{dist,Docs,Examples,Tools,vcredist,components.xml,MaintenanceTool.dat,MaintenanceTool.exe,MaintenanceTool.ini,network.xml,qt-install.qs}
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd $QT_SDK
|
cd $QT_SDK
|
||||||
add_cmake_opts -DDESIRED_QT_VERSION=5 \
|
|
||||||
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
|
add_cmake_opts -DDESIRED_QT_VERSION=4 \
|
||||||
-DCMAKE_PREFIX_PATH="$QT_SDK"
|
-DQT_QMAKE_EXECUTABLE="$QT_SDK/bin/qmake.exe"
|
||||||
|
|
||||||
if [ $CONFIGURATION == "Debug" ]; then
|
if [ $CONFIGURATION == "Debug" ]; then
|
||||||
SUFFIX="d"
|
SUFFIX="d4"
|
||||||
else
|
else
|
||||||
SUFFIX=""
|
SUFFIX="4"
|
||||||
fi
|
fi
|
||||||
add_runtime_dlls "$(pwd)/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll
|
|
||||||
add_qt_platform_dlls "$(pwd)/plugins/platforms/qwindows${SUFFIX}.dll"
|
add_runtime_dlls `pwd`/bin/Qt{Core,Gui,Network,OpenGL}$SUFFIX.dll
|
||||||
|
|
||||||
echo Done.
|
echo Done.
|
||||||
else
|
else
|
||||||
QT_SDK="C:/Qt/5.10/msvc${MSVC_DISPLAY_YEAR}${SUFFIX}"
|
if [ $BITS -eq 32 ]; then
|
||||||
add_cmake_opts -DDESIRED_QT_VERSION=5 \
|
QT_SDK="C:/Qt/5.4/msvc2013_opengl"
|
||||||
-DQT_QMAKE_EXECUTABLE="${QT_SDK}/bin/qmake.exe" \
|
|
||||||
-DCMAKE_PREFIX_PATH="$QT_SDK"
|
|
||||||
if [ $CONFIGURATION == "Debug" ]; then
|
|
||||||
SUFFIX="d"
|
|
||||||
else
|
else
|
||||||
SUFFIX=""
|
QT_SDK="C:/Qt/5.4/msvc2013_64_opengl"
|
||||||
fi
|
fi
|
||||||
DIR=$(echo "${QT_SDK}" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
|
|
||||||
add_runtime_dlls "${DIR}/bin/Qt5"{Core,Gui,Network,OpenGL,Widgets}${SUFFIX}.dll
|
add_cmake_opts -DDESIRED_QT_VERSION=5 \
|
||||||
add_qt_platform_dlls "${DIR}/plugins/platforms/qwindows${SUFFIX}.dll"
|
-DQT_QMAKE_EXECUTABLE="$QT_SDK/bin/qmake.exe" \
|
||||||
echo Done.
|
-DCMAKE_PREFIX_PATH="$QT_SDK"
|
||||||
|
|
||||||
|
echo AppVeyor.
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
cd $DEPS
|
cd $DEPS
|
||||||
echo
|
|
||||||
# SDL2
|
# SDL2
|
||||||
printf "SDL 2.0.7... "
|
printf "SDL 2.0.3... "
|
||||||
{
|
{
|
||||||
if [ -d SDL2-2.0.7 ]; then
|
if [ -d SDL2-2.0.3 ]; then
|
||||||
printf "Exists. "
|
printf "Exists. "
|
||||||
elif [ -z $SKIP_EXTRACT ]; then
|
elif [ -z $SKIP_EXTRACT ]; then
|
||||||
rm -rf SDL2-2.0.7
|
rm -rf SDL2-2.0.3
|
||||||
eval 7z x -y SDL2-2.0.7.zip $STRIP
|
eval 7z x -y SDL2-2.0.3.zip $STRIP
|
||||||
fi
|
fi
|
||||||
export SDL2DIR="$(real_pwd)/SDL2-2.0.7"
|
|
||||||
add_runtime_dlls "$(pwd)/SDL2-2.0.7/lib/x${ARCHSUFFIX}/SDL2.dll"
|
SDL_SDK="`real_pwd`/SDL2-2.0.3"
|
||||||
|
add_cmake_opts -DSDL2_INCLUDE_DIR="$SDL_SDK/include" \
|
||||||
|
-DSDL2MAIN_LIBRARY="$SDL_SDK/lib/x$ARCHSUFFIX/SDL2main.lib" \
|
||||||
|
-DSDL2_LIBRARY_PATH="$SDL_SDK/lib/x$ARCHSUFFIX/SDL2.lib"
|
||||||
|
|
||||||
|
add_runtime_dlls `pwd`/SDL2-2.0.3/lib/x$ARCHSUFFIX/SDL2.dll
|
||||||
|
|
||||||
echo Done.
|
echo Done.
|
||||||
}
|
}
|
||||||
echo
|
|
||||||
|
|
||||||
cd $DEPS_INSTALL/..
|
cd $DEPS_INSTALL/..
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "Setting up OpenMW build..."
|
echo "Setting up OpenMW build..."
|
||||||
|
|
||||||
add_cmake_opts -DBUILD_BSATOOL=no \
|
add_cmake_opts -DBUILD_BSATOOL=no \
|
||||||
-DBUILD_ESMTOOL=no \
|
-DBUILD_ESMTOOL=no \
|
||||||
-DBUILD_MYGUI_PLUGIN=no \
|
-DBUILD_MYGUI_PLUGIN=no \
|
||||||
-DOPENMW_MP_BUILD=on
|
-DOPENMW_MP_BUILD=on
|
||||||
if [ ! -z $CI ]; then
|
|
||||||
|
if [ -z $CI ]; then
|
||||||
|
echo " (Outside of CI, doing full build.)"
|
||||||
|
else
|
||||||
case $STEP in
|
case $STEP in
|
||||||
components )
|
components )
|
||||||
echo " Building subproject: Components."
|
echo " Subproject: Components."
|
||||||
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
||||||
-DBUILD_LAUNCHER=no \
|
-DBUILD_LAUNCHER=no \
|
||||||
-DBUILD_MWINIIMPORTER=no \
|
-DBUILD_MWINIIMPORTER=no \
|
||||||
|
@ -663,7 +616,7 @@ if [ ! -z $CI ]; then
|
||||||
-DBUILD_WIZARD=no
|
-DBUILD_WIZARD=no
|
||||||
;;
|
;;
|
||||||
openmw )
|
openmw )
|
||||||
echo " Building subproject: OpenMW."
|
echo " Subproject: OpenMW."
|
||||||
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
||||||
-DBUILD_LAUNCHER=no \
|
-DBUILD_LAUNCHER=no \
|
||||||
-DBUILD_MWINIIMPORTER=no \
|
-DBUILD_MWINIIMPORTER=no \
|
||||||
|
@ -671,7 +624,7 @@ if [ ! -z $CI ]; then
|
||||||
-DBUILD_WIZARD=no
|
-DBUILD_WIZARD=no
|
||||||
;;
|
;;
|
||||||
opencs )
|
opencs )
|
||||||
echo " Building subproject: OpenCS."
|
echo " Subproject: OpenCS."
|
||||||
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
||||||
-DBUILD_LAUNCHER=no \
|
-DBUILD_LAUNCHER=no \
|
||||||
-DBUILD_MWINIIMPORTER=no \
|
-DBUILD_MWINIIMPORTER=no \
|
||||||
|
@ -679,54 +632,47 @@ if [ ! -z $CI ]; then
|
||||||
-DBUILD_WIZARD=no
|
-DBUILD_WIZARD=no
|
||||||
;;
|
;;
|
||||||
misc )
|
misc )
|
||||||
echo " Building subprojects: Misc."
|
echo " Subproject: Misc."
|
||||||
add_cmake_opts -DBUILD_OPENCS=no \
|
add_cmake_opts -DBUILD_OPENCS=no \
|
||||||
-DBUILD_OPENMW=no
|
-DBUILD_OPENMW=no
|
||||||
;;
|
;;
|
||||||
|
* )
|
||||||
|
echo " Building everything."
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
# NOTE: Disable this when/if we want to run test cases
|
|
||||||
#if [ -z $CI ]; then
|
|
||||||
echo "- Copying Runtime DLLs..."
|
|
||||||
mkdir -p $BUILD_CONFIG
|
|
||||||
for DLL in $RUNTIME_DLLS; do
|
|
||||||
TARGET="$(basename "$DLL")"
|
|
||||||
if [[ "$DLL" == *":"* ]]; then
|
|
||||||
IFS=':'; SPLIT=( ${DLL} ); unset IFS
|
|
||||||
DLL=${SPLIT[0]}
|
|
||||||
TARGET=${SPLIT[1]}
|
|
||||||
fi
|
|
||||||
echo " ${TARGET}."
|
|
||||||
cp "$DLL" "$BUILD_CONFIG/$TARGET"
|
|
||||||
done
|
|
||||||
echo
|
|
||||||
echo "- OSG Plugin DLLs..."
|
|
||||||
mkdir -p $BUILD_CONFIG/osgPlugins-3.4.1
|
|
||||||
for DLL in $OSG_PLUGINS; do
|
|
||||||
echo " $(basename $DLL)."
|
|
||||||
cp "$DLL" $BUILD_CONFIG/osgPlugins-3.4.1
|
|
||||||
done
|
|
||||||
echo
|
|
||||||
echo "- Qt Platform DLLs..."
|
|
||||||
mkdir -p ${BUILD_CONFIG}/platforms
|
|
||||||
for DLL in $QT_PLATFORMS; do
|
|
||||||
echo " $(basename $DLL)"
|
|
||||||
cp "$DLL" "${BUILD_CONFIG}/platforms"
|
|
||||||
done
|
|
||||||
echo
|
|
||||||
#fi
|
|
||||||
if [ -z $VERBOSE ]; then
|
if [ -z $VERBOSE ]; then
|
||||||
printf -- "- Configuring... "
|
printf " Configuring... "
|
||||||
else
|
else
|
||||||
echo "- cmake .. $CMAKE_OPTS"
|
echo " cmake .. $CMAKE_OPTS"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
run_cmd cmake .. $CMAKE_OPTS
|
run_cmd cmake .. $CMAKE_OPTS
|
||||||
RET=$?
|
RET=$?
|
||||||
|
|
||||||
if [ -z $VERBOSE ]; then
|
if [ -z $VERBOSE ]; then
|
||||||
if [ $RET -eq 0 ]; then
|
if [ $RET -eq 0 ]; then echo Done.
|
||||||
echo Done.
|
else echo Failed.; fi
|
||||||
else
|
|
||||||
echo Failed.
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
|
||||||
|
# NOTE: Disable this when/if we want to run test cases
|
||||||
|
if [ -z $CI ]; then
|
||||||
|
echo "Copying Runtime DLLs..."
|
||||||
|
mkdir -p $CONFIGURATION
|
||||||
|
for DLL in $RUNTIME_DLLS; do
|
||||||
|
echo " `basename $DLL`."
|
||||||
|
cp "$DLL" $CONFIGURATION/
|
||||||
|
done
|
||||||
|
echo "OSG Plugin DLLs..."
|
||||||
|
mkdir -p $CONFIGURATION/osgPlugins-3.3.8
|
||||||
|
for DLL in $OSG_PLUGINS; do
|
||||||
|
echo " `basename $DLL`."
|
||||||
|
cp "$DLL" $CONFIGURATION/osgPlugins-3.3.8
|
||||||
|
done
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
|
||||||
exit $RET
|
exit $RET
|
||||||
|
|
|
@ -4,17 +4,21 @@ export CXX=clang++
|
||||||
export CC=clang
|
export CC=clang
|
||||||
|
|
||||||
DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps"
|
DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps"
|
||||||
QT_PATH=`brew --prefix qt`
|
QT_PATH="/usr/local/opt/qt5"
|
||||||
|
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
|
|
||||||
cmake \
|
cmake \
|
||||||
|
-D PKG_CONFIG_USE_CMAKE_PREFIX_PATH=ON \
|
||||||
|
-D CMAKE_EXE_LINKER_FLAGS="-lz" \
|
||||||
-D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \
|
-D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \
|
||||||
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.9" \
|
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.8" \
|
||||||
-D CMAKE_OSX_SYSROOT="macosx10.13" \
|
-D CMAKE_OSX_SYSROOT="macosx10.11" \
|
||||||
-D CMAKE_BUILD_TYPE=Release \
|
-D CMAKE_BUILD_TYPE=Debug \
|
||||||
-D OPENMW_OSX_DEPLOYMENT=TRUE \
|
-D OPENMW_OSX_DEPLOYMENT=TRUE \
|
||||||
-D DESIRED_QT_VERSION=5 \
|
-D DESIRED_QT_VERSION=5 \
|
||||||
|
-D OSG_PLUGIN_LIB_SEARCH_PATH="$DEPENDENCIES_ROOT/lib/osgPlugins-3.4.0" \
|
||||||
-D BUILD_ESMTOOL=FALSE \
|
-D BUILD_ESMTOOL=FALSE \
|
||||||
-D BUILD_MYGUI_PLUGIN=FALSE \
|
-D BUILD_MYGUI_PLUGIN=FALSE \
|
||||||
-G"Unix Makefiles" \
|
-G"Unix Makefiles" \
|
||||||
|
|
|
@ -1,13 +1,5 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
APPVEYOR=""
|
|
||||||
CI=""
|
|
||||||
|
|
||||||
PACKAGE=""
|
|
||||||
PLATFORM=""
|
|
||||||
CONFIGURATION=""
|
|
||||||
VS_VERSION=""
|
|
||||||
|
|
||||||
if [ -z $PLATFORM ]; then
|
if [ -z $PLATFORM ]; then
|
||||||
PLATFORM=`uname -m`
|
PLATFORM=`uname -m`
|
||||||
fi
|
fi
|
||||||
|
@ -16,70 +8,46 @@ if [ -z $CONFIGURATION ]; then
|
||||||
CONFIGURATION="Debug"
|
CONFIGURATION="Debug"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
case $VS_VERSION in
|
|
||||||
14|14.0|2015 )
|
|
||||||
GENERATOR="Visual Studio 14 2015"
|
|
||||||
MSVC_YEAR="2015"
|
|
||||||
MSVC_VER="14.0"
|
|
||||||
;;
|
|
||||||
|
|
||||||
# 12|2013|
|
|
||||||
* )
|
|
||||||
GENERATOR="Visual Studio 12 2013"
|
|
||||||
MSVC_YEAR="2013"
|
|
||||||
MVSC_VER="12.0"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
case $PLATFORM in
|
case $PLATFORM in
|
||||||
x64|x86_64|x86-64|win64|Win64 )
|
|
||||||
BITS=64
|
|
||||||
;;
|
|
||||||
|
|
||||||
x32|x86|i686|i386|win32|Win32 )
|
x32|x86|i686|i386|win32|Win32 )
|
||||||
BITS=32
|
BITS=32
|
||||||
;;
|
PLATFORM=Win32
|
||||||
esac
|
|
||||||
|
|
||||||
case $CONFIGURATION in
|
|
||||||
debug|Debug|DEBUG )
|
|
||||||
CONFIGURATION=Debug
|
|
||||||
;;
|
;;
|
||||||
|
|
||||||
release|Release|RELEASE )
|
x64|x86_64|x86-64|win64|Win64 )
|
||||||
CONFIGURATION=Release
|
BITS=64
|
||||||
|
PLATFORM=x64
|
||||||
;;
|
;;
|
||||||
|
|
||||||
relwithdebinfo|RelWithDebInfo|RELWITHDEBINFO )
|
* )
|
||||||
CONFIGURATION=RelWithDebInfo
|
echo "Unknown platform $PLATFORM."
|
||||||
;;
|
exit 1 ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
if [ -z $APPVEYOR ]; then
|
if [ -z $APPVEYOR ]; then
|
||||||
echo "Running ${BITS}-bit MSVC${MSVC_YEAR} ${CONFIGURATION} build outside of Appveyor."
|
echo "Running $BITS-bit $CONFIGURATION build outside of Appveyor."
|
||||||
|
|
||||||
DIR=$(echo "$0" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
|
DIR=$(echo "$0" | sed "s,\\\\,/,g" | sed "s,\(.\):,/\\1,")
|
||||||
cd $(dirname "$DIR")/..
|
cd $(dirname "$DIR")/..
|
||||||
else
|
else
|
||||||
echo "Running ${BITS}-bit MSVC${MSVC_YEAR} ${CONFIGURATION} build in Appveyor."
|
echo "Running $BITS-bit $CONFIGURATION build in Appveyor."
|
||||||
|
|
||||||
cd $APPVEYOR_BUILD_FOLDER
|
cd $APPVEYOR_BUILD_FOLDER
|
||||||
fi
|
fi
|
||||||
|
|
||||||
BUILD_DIR="MSVC${MSVC_YEAR}_${BITS}"
|
cd build_$BITS
|
||||||
cd ${BUILD_DIR}
|
|
||||||
|
|
||||||
which msbuild > /dev/null
|
which msbuild > /dev/null
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
msbuild() {
|
msbuild() {
|
||||||
/c/Program\ Files\ \(x86\)/MSBuild/${MSVC_VER}/Bin/MSBuild.exe "$@"
|
/c/Program\ Files\ \(x86\)/MSBuild/12.0/Bin/MSBuild.exe "$@"
|
||||||
}
|
}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z $APPVEYOR ]; then
|
if [ -z $APPVEYOR ]; then
|
||||||
msbuild OpenMW.sln //t:Build //p:Configuration=${CONFIGURATION} //m:8
|
msbuild OpenMW.sln //t:Build //m:8
|
||||||
else
|
else
|
||||||
msbuild OpenMW.sln //t:Build //p:Configuration=${CONFIGURATION} //m:8 //logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
msbuild OpenMW.sln //t:Build //m:8 //logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
RET=$?
|
RET=$?
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
cd build
|
|
||||||
|
|
||||||
DATE=`date +'%d%m%Y'`
|
|
||||||
SHORT_COMMIT=`git rev-parse --short ${TRAVIS_COMMIT}`
|
|
||||||
TARGET_FILENAME="OpenMW-${DATE}-${SHORT_COMMIT}.dmg"
|
|
||||||
|
|
||||||
if ! curl --ssl -u $OSX_FTP_USER:$OSX_FTP_PASSWORD "${OSX_FTP_URL}" --silent | grep $SHORT_COMMIT > /dev/null; then
|
|
||||||
curl --ssl --ftp-create-dirs -T *.dmg -u $OSX_FTP_USER:$OSX_FTP_PASSWORD "${OSX_FTP_URL}${TARGET_FILENAME}"
|
|
||||||
fi
|
|
554
CMakeLists.txt
554
CMakeLists.txt
|
@ -1,35 +1,3 @@
|
||||||
# Apps and tools
|
|
||||||
option(BUILD_OPENMW "build OpenMW" ON)
|
|
||||||
option(BUILD_OPENMW_MP "build OpenMW-MP" ON)
|
|
||||||
option(BUILD_MASTER "build tes3mp master server" OFF)
|
|
||||||
option(BUILD_BSATOOL "build BSA extractor" ON)
|
|
||||||
option(BUILD_ESMTOOL "build ESM inspector" ON)
|
|
||||||
option(BUILD_LAUNCHER "build Launcher" ON)
|
|
||||||
option(BUILD_BROWSER "build tes3mp Server Browser" ON)
|
|
||||||
option(BUILD_MWINIIMPORTER "build MWiniImporter" ON)
|
|
||||||
option(BUILD_ESSIMPORTER "build ESS (Morrowind save game) importer" ON)
|
|
||||||
option(BUILD_OPENCS "build OpenMW Construction Set" ON)
|
|
||||||
option(BUILD_WIZARD "build Installation Wizard" ON)
|
|
||||||
option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
|
|
||||||
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF)
|
|
||||||
option(BUILD_NIFTEST "build nif file tester" OFF)
|
|
||||||
option(BUILD_MYGUI_PLUGIN "build MyGUI plugin for OpenMW resources, to use with MyGUI tools" ON)
|
|
||||||
option(BUILD_DOCS "build documentation." OFF )
|
|
||||||
|
|
||||||
if (NOT BUILD_LAUNCHER AND NOT BUILD_BROWSER AND NOT BUILD_OPENCS AND NOT BUILD_WIZARD)
|
|
||||||
set(USE_QT FALSE)
|
|
||||||
else()
|
|
||||||
set(USE_QT TRUE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (USE_QT)
|
|
||||||
set(DESIRED_QT_VERSION 4 CACHE STRING "The QT version OpenMW should use (4 or 5)")
|
|
||||||
set_property(CACHE DESIRED_QT_VERSION PROPERTY STRINGS 4 5)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# set the minimum required version across the board
|
|
||||||
cmake_minimum_required(VERSION 3.1.0)
|
|
||||||
|
|
||||||
project(OpenMW)
|
project(OpenMW)
|
||||||
|
|
||||||
# If the user doesn't supply a CMAKE_BUILD_TYPE via command line, choose one for them.
|
# If the user doesn't supply a CMAKE_BUILD_TYPE via command line, choose one for them.
|
||||||
|
@ -57,7 +25,7 @@ endif()
|
||||||
message(STATUS "Configuring OpenMW...")
|
message(STATUS "Configuring OpenMW...")
|
||||||
|
|
||||||
set(OPENMW_VERSION_MAJOR 0)
|
set(OPENMW_VERSION_MAJOR 0)
|
||||||
set(OPENMW_VERSION_MINOR 44)
|
set(OPENMW_VERSION_MINOR 38)
|
||||||
set(OPENMW_VERSION_RELEASE 0)
|
set(OPENMW_VERSION_RELEASE 0)
|
||||||
|
|
||||||
set(OPENMW_VERSION_COMMITHASH "")
|
set(OPENMW_VERSION_COMMITHASH "")
|
||||||
|
@ -67,13 +35,17 @@ set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VER
|
||||||
|
|
||||||
set(GIT_CHECKOUT FALSE)
|
set(GIT_CHECKOUT FALSE)
|
||||||
if(EXISTS ${PROJECT_SOURCE_DIR}/.git)
|
if(EXISTS ${PROJECT_SOURCE_DIR}/.git)
|
||||||
find_package(Git)
|
if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow)
|
||||||
|
find_package(Git)
|
||||||
|
|
||||||
if(GIT_FOUND)
|
if(GIT_FOUND)
|
||||||
set(GIT_CHECKOUT TRUE)
|
set(GIT_CHECKOUT TRUE)
|
||||||
else(GIT_FOUND)
|
else(GIT_FOUND)
|
||||||
message(WARNING "Git executable not found")
|
message(WARNING "Git executable not found")
|
||||||
endif(GIT_FOUND)
|
endif(GIT_FOUND)
|
||||||
|
else(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow)
|
||||||
|
message(STATUS "Shallow Git clone detected, not attempting to retrieve version info")
|
||||||
|
endif(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow)
|
||||||
endif(EXISTS ${PROJECT_SOURCE_DIR}/.git)
|
endif(EXISTS ${PROJECT_SOURCE_DIR}/.git)
|
||||||
|
|
||||||
# Macros
|
# Macros
|
||||||
|
@ -91,12 +63,19 @@ option(QT_STATIC "Link static build of QT into the binaries" FALSE)
|
||||||
|
|
||||||
option(OPENMW_UNITY_BUILD "Use fewer compilation units to speed up compile time" FALSE)
|
option(OPENMW_UNITY_BUILD "Use fewer compilation units to speed up compile time" FALSE)
|
||||||
|
|
||||||
# what is necessary to build documentation
|
# Apps and tools
|
||||||
IF( BUILD_DOCS )
|
option(BUILD_OPENMW "build OpenMW" ON)
|
||||||
# Builds the documentation.
|
option(BUILD_BSATOOL "build BSA extractor" ON)
|
||||||
FIND_PACKAGE( Sphinx REQUIRED )
|
option(BUILD_ESMTOOL "build ESM inspector" ON)
|
||||||
FIND_PACKAGE( Doxygen REQUIRED )
|
option(BUILD_LAUNCHER "build Launcher" ON)
|
||||||
ENDIF()
|
option(BUILD_MWINIIMPORTER "build MWiniImporter" ON)
|
||||||
|
option(BUILD_ESSIMPORTER "build ESS (Morrowind save game) importer" ON)
|
||||||
|
option(BUILD_OPENCS "build OpenMW Construction Set" ON)
|
||||||
|
option(BUILD_WIZARD "build Installation Wizard" ON)
|
||||||
|
option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
|
||||||
|
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF)
|
||||||
|
option(BUILD_NIFTEST "build nif file tester" OFF)
|
||||||
|
option(BUILD_MYGUI_PLUGIN "build MyGUI plugin for OpenMW resources, to use with MyGUI tools" ON)
|
||||||
|
|
||||||
# OS X deployment
|
# OS X deployment
|
||||||
option(OPENMW_OSX_DEPLOYMENT OFF)
|
option(OPENMW_OSX_DEPLOYMENT OFF)
|
||||||
|
@ -109,7 +88,7 @@ endif()
|
||||||
# Set up common paths
|
# Set up common paths
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files")
|
set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files")
|
||||||
set(OPENMW_RESOURCE_FILES "../Resources/resources" CACHE PATH "location of OpenMW resources files")
|
set(OPENMW_RESOURCE_FILES "./resources" CACHE PATH "location of OpenMW resources files")
|
||||||
elseif(UNIX)
|
elseif(UNIX)
|
||||||
# Paths
|
# Paths
|
||||||
SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries")
|
SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries")
|
||||||
|
@ -137,13 +116,16 @@ if (WIN32)
|
||||||
option(USE_DEBUG_CONSOLE "whether a debug console should be enabled for debug builds, if false debug output is redirected to Visual Studio output" ON)
|
option(USE_DEBUG_CONSOLE "whether a debug console should be enabled for debug builds, if false debug output is redirected to Visual Studio output" ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(RakNet REQUIRED)
|
if (NOT BUILD_LAUNCHER AND NOT BUILD_OPENCS AND NOT BUILD_WIZARD)
|
||||||
include_directories(${RakNet_INCLUDES})
|
set(USE_QT FALSE)
|
||||||
|
else()
|
||||||
|
set(USE_QT TRUE)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
find_package(OpenGL REQUIRED)
|
|
||||||
|
|
||||||
if (USE_QT)
|
if (USE_QT)
|
||||||
|
set(DESIRED_QT_VERSION 4 CACHE STRING "The QT version OpenMW should use (4 or 5)")
|
||||||
|
set_property(CACHE DESIRED_QT_VERSION PROPERTY STRINGS 4 5)
|
||||||
message(STATUS "Using Qt${DESIRED_QT_VERSION}")
|
message(STATUS "Using Qt${DESIRED_QT_VERSION}")
|
||||||
|
|
||||||
if (DESIRED_QT_VERSION MATCHES 4)
|
if (DESIRED_QT_VERSION MATCHES 4)
|
||||||
|
@ -158,20 +140,41 @@ if (USE_QT)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
IF(BUILD_OPENMW OR BUILD_OPENCS)
|
if (USE_QT AND DESIRED_QT_VERSION MATCHES 5)
|
||||||
# Sound setup
|
# 2.8.11+ is required to make Qt5 happy and allow linking QtMain on Windows.
|
||||||
find_package(FFmpeg REQUIRED COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE SWRESAMPLE)
|
cmake_minimum_required(VERSION 2.8.11)
|
||||||
# Required for building the FFmpeg headers
|
else()
|
||||||
add_definitions(-D__STDC_CONSTANT_MACROS)
|
# We probably support older versions than this.
|
||||||
|
cmake_minimum_required(VERSION 2.6)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Sound setup
|
||||||
|
unset(FFMPEG_LIBRARIES CACHE)
|
||||||
|
|
||||||
|
find_package(FFmpeg REQUIRED)
|
||||||
|
|
||||||
|
set (FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${SWSCALE_LIBRARY} ${SWRESAMPLE_LIBRARIES})
|
||||||
|
|
||||||
|
if ( NOT AVCODEC_FOUND OR NOT AVFORMAT_FOUND OR NOT AVUTIL_FOUND OR NOT SWSCALE_FOUND OR NOT SWRESAMPLE_FOUND)
|
||||||
|
message(FATAL_ERROR "FFmpeg component required, but not found!")
|
||||||
|
endif()
|
||||||
|
# Required for building the FFmpeg headers
|
||||||
|
add_definitions(-D__STDC_CONSTANT_MACROS)
|
||||||
|
|
||||||
# TinyXML
|
# TinyXML
|
||||||
option(USE_SYSTEM_TINYXML "Use system TinyXML library instead of internal." OFF)
|
option(USE_SYSTEM_TINYXML "Use system TinyXML library instead of internal." OFF)
|
||||||
if (USE_SYSTEM_TINYXML)
|
if(USE_SYSTEM_TINYXML)
|
||||||
find_package(TinyXML REQUIRED)
|
find_library(TINYXML_LIBRARIES tinyxml)
|
||||||
|
find_path(TINYXML_INCLUDE_DIR tinyxml.h)
|
||||||
|
message(STATUS "Found TinyXML: ${TINYXML_LIBRARIES} ${TINYXML_INCLUDE_DIR}")
|
||||||
add_definitions (-DTIXML_USE_STL)
|
add_definitions (-DTIXML_USE_STL)
|
||||||
include_directories(SYSTEM ${TinyXML_INCLUDE_DIRS})
|
if(TINYXML_LIBRARIES AND TINYXML_INCLUDE_DIR)
|
||||||
|
include_directories(${TINYXML_INCLUDE_DIR})
|
||||||
|
message(STATUS "Using system TinyXML library.")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Detection of system TinyXML incomplete.")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
ENDIF(BUILD_OPENMW OR BUILD_OPENCS)
|
|
||||||
|
|
||||||
# Platform specific
|
# Platform specific
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
|
@ -187,10 +190,11 @@ if (WIN32)
|
||||||
add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN)
|
add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (NOT WIN32 AND BUILD_WIZARD) # windows users can just run the morrowind installer
|
option(OPENGL_ES "enable opengl es support" FALSE )
|
||||||
find_package(LIBUNSHIELD REQUIRED) # required only for non win32 when building openmw-wizard
|
|
||||||
set(OPENMW_USE_UNSHIELD TRUE)
|
if (OPENGL_ES)
|
||||||
endif()
|
add_definitions(-DOPENGL_ES)
|
||||||
|
endif(OPENGL_ES)
|
||||||
|
|
||||||
# Fix for not visible pthreads functions for linker with glibc 2.15
|
# Fix for not visible pthreads functions for linker with glibc 2.15
|
||||||
if (UNIX AND NOT APPLE)
|
if (UNIX AND NOT APPLE)
|
||||||
|
@ -205,57 +209,14 @@ if(NOT HAVE_STDINT_H)
|
||||||
message(FATAL_ERROR "stdint.h was not found" )
|
message(FATAL_ERROR "stdint.h was not found" )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
include (CheckIncludeFileCXX)
|
||||||
IF(BUILD_OPENMW OR BUILD_OPENCS)
|
check_include_file_cxx(unordered_map HAVE_UNORDERED_MAP)
|
||||||
|
if (HAVE_UNORDERED_MAP)
|
||||||
find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgParticle osgUtil osgFX)
|
add_definitions(-DHAVE_UNORDERED_MAP)
|
||||||
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
|
endif ()
|
||||||
|
|
||||||
set(USED_OSG_PLUGINS
|
|
||||||
osgdb_bmp
|
|
||||||
osgdb_dds
|
|
||||||
osgdb_jpeg
|
|
||||||
osgdb_osg
|
|
||||||
osgdb_png
|
|
||||||
osgdb_serializers_osg
|
|
||||||
osgdb_tga
|
|
||||||
)
|
|
||||||
|
|
||||||
get_filename_component(OSG_LIB_DIR ${OSGDB_LIBRARY} DIRECTORY)
|
|
||||||
set(OSGPlugins_LIB_DIR "${OSG_LIB_DIR}/osgPlugins-${OPENSCENEGRAPH_VERSION}")
|
|
||||||
|
|
||||||
if(OSG_STATIC)
|
|
||||||
add_definitions(-DOSG_LIBRARY_STATIC)
|
|
||||||
|
|
||||||
find_package(OSGPlugins REQUIRED COMPONENTS ${USED_OSG_PLUGINS})
|
|
||||||
list(APPEND OPENSCENEGRAPH_LIBRARIES ${OSGPlugins_LIBRARIES})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(QT_STATIC)
|
|
||||||
if(WIN32)
|
|
||||||
if(DESIRED_QT_VERSION MATCHES 4)
|
|
||||||
# QtCore needs WSAAsyncSelect from Ws2_32.lib
|
|
||||||
set(QT_QTCORE_LIBRARY ${QT_QTCORE_LIBRARY} Ws2_32.lib)
|
|
||||||
message("QT_QTCORE_LIBRARY: ${QT_QTCORE_LIBRARY}")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(REQUIRED_BULLET_VERSION 286) # Bullet 286 required due to runtime bugfixes for btCapsuleShape
|
|
||||||
if (DEFINED ENV{TRAVIS_BRANCH} OR DEFINED ENV{APPVEYOR})
|
|
||||||
set(REQUIRED_BULLET_VERSION 283) # but for build testing, 283 is fine
|
|
||||||
endif()
|
|
||||||
|
|
||||||
find_package(MyGUI 3.2.1 REQUIRED)
|
|
||||||
find_package(SDL2 REQUIRED)
|
|
||||||
find_package(OpenAL REQUIRED)
|
|
||||||
find_package(Bullet ${REQUIRED_BULLET_VERSION} REQUIRED COMPONENTS BulletCollision LinearMath)
|
|
||||||
ELSE()
|
|
||||||
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}) # HACK: DO NOT MOVE THIS. Used for server only build, kept here to avoid merge conflicts above.
|
|
||||||
ENDIF(BUILD_OPENMW OR BUILD_OPENCS)
|
|
||||||
|
|
||||||
|
|
||||||
set(BOOST_COMPONENTS system filesystem program_options)
|
set(BOOST_COMPONENTS system filesystem program_options thread)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale)
|
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale)
|
||||||
endif(WIN32)
|
endif(WIN32)
|
||||||
|
@ -264,18 +225,91 @@ IF(BOOST_STATIC)
|
||||||
set(Boost_USE_STATIC_LIBS ON)
|
set(Boost_USE_STATIC_LIBS ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgAnimation osgParticle osgUtil osgFX)
|
||||||
|
|
||||||
|
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
if(OSG_STATIC)
|
||||||
|
macro(use_static_osg_plugin_library PLUGIN_NAME)
|
||||||
|
set(PLUGIN_NAME_DBG ${PLUGIN_NAME}d ${PLUGIN_NAME}D ${PLUGIN_NAME}_d ${PLUGIN_NAME}_D ${PLUGIN_NAME}_debug ${PLUGIN_NAME})
|
||||||
|
|
||||||
|
# For now, users wishing to do a static build will need to pass the path to where the plugins reside
|
||||||
|
# More clever logic would need to deduce the path, probably installed under <OpenSceneGraph>/lib/osgPlugins-<X.X.X>
|
||||||
|
find_library(${PLUGIN_NAME}_LIBRARY_REL NAMES ${PLUGIN_NAME} HINTS ${OSG_PLUGIN_LIB_SEARCH_PATH})
|
||||||
|
find_library(${PLUGIN_NAME}_LIBRARY_DBG NAMES ${PLUGIN_NAME_DBG} HINTS ${OSG_PLUGIN_LIB_SEARCH_PATH})
|
||||||
|
make_library_set(${PLUGIN_NAME}_LIBRARY)
|
||||||
|
|
||||||
|
if("${${PLUGIN_NAME}_LIBRARY}" STREQUAL "")
|
||||||
|
message(FATAL_ERROR "Unable to find static OpenSceneGraph plugin: ${PLUGIN_NAME}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(OPENSCENEGRAPH_LIBRARIES ${OPENSCENEGRAPH_LIBRARIES} ${${PLUGIN_NAME}_LIBRARY})
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
macro(use_static_osg_plugin_dep DEPENDENCY)
|
||||||
|
find_package(${DEPENDENCY} REQUIRED)
|
||||||
|
|
||||||
|
set(OPENSCENEGRAPH_LIBRARIES ${OPENSCENEGRAPH_LIBRARIES} ${${DEPENDENCY}_LIBRARIES})
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
add_definitions(-DOSG_LIBRARY_STATIC)
|
||||||
|
|
||||||
|
set(PLUGIN_LIST
|
||||||
|
osgdb_png # depends on libpng, zlib
|
||||||
|
osgdb_tga
|
||||||
|
osgdb_dds
|
||||||
|
osgdb_jpeg # depends on libjpeg
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach(PLUGIN ${PLUGIN_LIST})
|
||||||
|
use_static_osg_plugin_library(${PLUGIN})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# OSG static plugins need to linked against their respective dependencies
|
||||||
|
set(PLUGIN_DEPS_LIST
|
||||||
|
PNG # needed by osgdb_png
|
||||||
|
ZLIB # needed by osgdb_png
|
||||||
|
JPEG # needed by osgdb_jpeg
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach(DEPENDENCY ${PLUGIN_DEPS_LIST})
|
||||||
|
use_static_osg_plugin_dep(${DEPENDENCY})
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(QT_STATIC)
|
||||||
|
if(WIN32)
|
||||||
|
if(DESIRED_QT_VERSION MATCHES 4)
|
||||||
|
# QtCore needs WSAAsyncSelect from Ws2_32.lib
|
||||||
|
set(QT_QTCORE_LIBRARY ${QT_QTCORE_LIBRARY} Ws2_32.lib)
|
||||||
|
message("QT_QTCORE_LIBRARY: ${QT_QTCORE_LIBRARY}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(MyGUI REQUIRED)
|
||||||
|
if (${MYGUI_VERSION} VERSION_LESS "3.2.1")
|
||||||
|
message(FATAL_ERROR "OpenMW requires MyGUI 3.2.1 or later, please install the latest version from http://mygui.info")
|
||||||
|
endif()
|
||||||
|
|
||||||
find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})
|
find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})
|
||||||
|
find_package(SDL2 REQUIRED)
|
||||||
|
find_package(OpenAL REQUIRED)
|
||||||
|
find_package(Bullet REQUIRED)
|
||||||
|
if (NOT BULLET_FOUND OR BULLET_VERSION VERSION_LESS 283)
|
||||||
|
message(FATAL_ERROR "OpenMW requires Bullet version 2.83 or later")
|
||||||
|
endif()
|
||||||
|
|
||||||
include_directories("."
|
include_directories("."
|
||||||
SYSTEM
|
SYSTEM
|
||||||
${SDL2_INCLUDE_DIR}
|
${SDL2_INCLUDE_DIR}
|
||||||
${Boost_INCLUDE_DIR}
|
${Boost_INCLUDE_DIR}
|
||||||
${MyGUI_INCLUDE_DIRS}
|
${MYGUI_INCLUDE_DIRS}
|
||||||
${OPENAL_INCLUDE_DIR}
|
${OPENAL_INCLUDE_DIR}
|
||||||
${Bullet_INCLUDE_DIRS}
|
${BULLET_INCLUDE_DIRS}
|
||||||
)
|
)
|
||||||
|
|
||||||
link_directories(${SDL2_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS})
|
link_directories(${SDL2_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS} ${MYGUI_LIB_DIR})
|
||||||
|
|
||||||
if(MYGUI_STATIC)
|
if(MYGUI_STATIC)
|
||||||
add_definitions(-DMYGUI_STATIC)
|
add_definitions(-DMYGUI_STATIC)
|
||||||
|
@ -292,11 +326,6 @@ endif (APPLE)
|
||||||
# Set up DEBUG define
|
# Set up DEBUG define
|
||||||
set_directory_properties(PROPERTIES COMPILE_DEFINITIONS_DEBUG DEBUG=1)
|
set_directory_properties(PROPERTIES COMPILE_DEFINITIONS_DEBUG DEBUG=1)
|
||||||
|
|
||||||
if (NOT APPLE)
|
|
||||||
set(OPENMW_MYGUI_FILES_ROOT ${OpenMW_BINARY_DIR})
|
|
||||||
set(OPENMW_SHADERS_ROOT ${OpenMW_BINARY_DIR})
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
add_subdirectory(files/)
|
add_subdirectory(files/)
|
||||||
|
|
||||||
# Specify build paths
|
# Specify build paths
|
||||||
|
@ -315,72 +344,50 @@ endif (APPLE)
|
||||||
|
|
||||||
# Other files
|
# Other files
|
||||||
|
|
||||||
configure_resource_file(${OpenMW_SOURCE_DIR}/files/tes3mp/tes3mp-client-default.cfg
|
configure_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg
|
||||||
"${OpenMW_BINARY_DIR}" "tes3mp-client-default.cfg")
|
"${OpenMW_BINARY_DIR}/settings-default.cfg")
|
||||||
|
|
||||||
configure_resource_file(${OpenMW_SOURCE_DIR}/files/tes3mp/tes3mp-server-default.cfg
|
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local
|
||||||
"${OpenMW_BINARY_DIR}" "tes3mp-server-default.cfg")
|
"${OpenMW_BINARY_DIR}/openmw.cfg")
|
||||||
|
|
||||||
configure_resource_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg
|
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
|
||||||
"${OpenMW_BINARY_DIR}" "settings-default.cfg")
|
"${OpenMW_BINARY_DIR}/openmw.cfg.install")
|
||||||
|
|
||||||
if (NOT APPLE)
|
configure_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.cfg
|
||||||
configure_resource_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg.local
|
"${OpenMW_BINARY_DIR}/openmw-cs.cfg")
|
||||||
"${OpenMW_BINARY_DIR}" "openmw.cfg")
|
|
||||||
configure_resource_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
|
|
||||||
"${OpenMW_BINARY_DIR}" "openmw.cfg.install")
|
|
||||||
else ()
|
|
||||||
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
|
|
||||||
"${OpenMW_BINARY_DIR}/openmw.cfg")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
configure_resource_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.cfg
|
configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters
|
||||||
"${OpenMW_BINARY_DIR}" "openmw-cs.cfg")
|
"${OpenMW_BINARY_DIR}/resources/defaultfilters" COPYONLY)
|
||||||
|
|
||||||
# Needs the copy version because the configure version assumes the end of the file has been reached when a null character is reached and there are no CMake expressions to evaluate.
|
configure_file(${OpenMW_SOURCE_DIR}/files/gamecontrollerdb.txt
|
||||||
copy_resource_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters
|
"${OpenMW_BINARY_DIR}/gamecontrollerdb.txt")
|
||||||
"${OpenMW_BINARY_DIR}" "resources/defaultfilters")
|
|
||||||
|
|
||||||
configure_resource_file(${OpenMW_SOURCE_DIR}/files/gamecontrollerdb.txt
|
|
||||||
"${OpenMW_BINARY_DIR}" "gamecontrollerdb.txt")
|
|
||||||
|
|
||||||
configure_resource_file(${OpenMW_SOURCE_DIR}/files/gamecontrollerdb_204.txt
|
|
||||||
"${OpenMW_BINARY_DIR}" "gamecontrollerdb_204.txt")
|
|
||||||
|
|
||||||
configure_resource_file(${OpenMW_SOURCE_DIR}/files/gamecontrollerdb_205.txt
|
|
||||||
"${OpenMW_BINARY_DIR}" "gamecontrollerdb_205.txt")
|
|
||||||
|
|
||||||
if (NOT WIN32 AND NOT APPLE)
|
if (NOT WIN32 AND NOT APPLE)
|
||||||
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop
|
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop
|
||||||
"${OpenMW_BINARY_DIR}/openmw.desktop")
|
"${OpenMW_BINARY_DIR}/openmw.desktop")
|
||||||
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.appdata.xml
|
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.appdata.xml
|
||||||
"${OpenMW_BINARY_DIR}/openmw.appdata.xml")
|
"${OpenMW_BINARY_DIR}/openmw.appdata.xml")
|
||||||
configure_file(${OpenMW_SOURCE_DIR}/files/tes3mp-browser.desktop
|
|
||||||
"${OpenMW_BINARY_DIR}/tes3mp-browser.desktop")
|
|
||||||
configure_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.desktop
|
configure_file(${OpenMW_SOURCE_DIR}/files/openmw-cs.desktop
|
||||||
"${OpenMW_BINARY_DIR}/openmw-cs.desktop")
|
"${OpenMW_BINARY_DIR}/openmw-cs.desktop")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# CXX Compiler settings
|
# CXX Compiler settings
|
||||||
set(CMAKE_CXX_STANDARD 14)
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wundef -Wno-unused-parameter -pedantic -Wno-long-long")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wundef -Wno-unused-parameter -std=c++98 -pedantic -Wno-long-long")
|
||||||
add_definitions( -DBOOST_NO_CXX11_SCOPED_ENUMS=ON )
|
|
||||||
|
|
||||||
if (APPLE)
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE)
|
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE)
|
||||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.6 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 3.6)
|
execute_process(COMMAND ${CMAKE_C_COMPILER} --version OUTPUT_VARIABLE CLANG_VERSION)
|
||||||
|
string(REGEX REPLACE ".*version ([0-9\\.]*).*" "\\1" CLANG_VERSION ${CLANG_VERSION})
|
||||||
|
if ("${CLANG_VERSION}" VERSION_GREATER 3.6 OR "${CLANG_VERSION}" VERSION_EQUAL 3.6)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-potentially-evaluated-expression")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-potentially-evaluated-expression")
|
||||||
endif ()
|
endif ("${CLANG_VERSION}" VERSION_GREATER 3.6 OR "${CLANG_VERSION}" VERSION_EQUAL 3.6)
|
||||||
endif()
|
endif(CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE)
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.6 OR CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 4.6)
|
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion
|
||||||
|
OUTPUT_VARIABLE GCC_VERSION)
|
||||||
|
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU AND "${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-but-set-parameter")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-but-set-parameter")
|
||||||
endif()
|
endif(CMAKE_CXX_COMPILER_ID STREQUAL GNU AND "${GCC_VERSION}" VERSION_GREATER 4.6 OR "${GCC_VERSION}" VERSION_EQUAL 4.6)
|
||||||
elseif (MSVC)
|
elseif (MSVC)
|
||||||
# Enable link-time code generation globally for all linking
|
# Enable link-time code generation globally for all linking
|
||||||
if (OPENMW_LTO_BUILD)
|
if (OPENMW_LTO_BUILD)
|
||||||
|
@ -398,17 +405,11 @@ IF(NOT WIN32 AND NOT APPLE)
|
||||||
|
|
||||||
# Install binaries
|
# Install binaries
|
||||||
IF(BUILD_OPENMW)
|
IF(BUILD_OPENMW)
|
||||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/tes3mp" DESTINATION "${BINDIR}" )
|
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" )
|
||||||
ENDIF(BUILD_OPENMW)
|
ENDIF(BUILD_OPENMW)
|
||||||
IF(BUILD_OPENMW_MP)
|
|
||||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/tes3mp-server" DESTINATION "${BINDIR}")
|
|
||||||
ENDIF(BUILD_OPENMW_MP)
|
|
||||||
IF(BUILD_LAUNCHER)
|
IF(BUILD_LAUNCHER)
|
||||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-launcher" DESTINATION "${BINDIR}" )
|
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw-launcher" DESTINATION "${BINDIR}" )
|
||||||
ENDIF(BUILD_LAUNCHER)
|
ENDIF(BUILD_LAUNCHER)
|
||||||
IF(BUILD_BROWSER)
|
|
||||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/tes3mp-browser" DESTINATION "${BINDIR}" )
|
|
||||||
ENDIF(BUILD_BROWSER)
|
|
||||||
IF(BUILD_BSATOOL)
|
IF(BUILD_BSATOOL)
|
||||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" )
|
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" )
|
||||||
ENDIF(BUILD_BSATOOL)
|
ENDIF(BUILD_BSATOOL)
|
||||||
|
@ -435,15 +436,12 @@ IF(NOT WIN32 AND NOT APPLE)
|
||||||
#ENDIF(BUILD_MYGUI_PLUGIN)
|
#ENDIF(BUILD_MYGUI_PLUGIN)
|
||||||
|
|
||||||
# Install licenses
|
# Install licenses
|
||||||
INSTALL(FILES "files/mygui/DejaVu Font License.txt" DESTINATION "${LICDIR}" )
|
INSTALL(FILES "docs/license/DejaVu Font License.txt" DESTINATION "${LICDIR}" )
|
||||||
|
|
||||||
# Install icon and desktop file
|
# Install icon and desktop file
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "openmw")
|
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "openmw")
|
||||||
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}" COMPONENT "openmw")
|
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/launcher/images/openmw.png" DESTINATION "${ICONDIR}" COMPONENT "openmw")
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.appdata.xml" DESTINATION "${DATAROOTDIR}/metainfo" COMPONENT "openmw")
|
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.appdata.xml" DESTINATION "${DATAROOTDIR}/appdata" COMPONENT "openmw")
|
||||||
IF(BUILD_BROWSER)
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/tes3mp-browser.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "browser")
|
|
||||||
ENDIF(BUILD_BROWSER)
|
|
||||||
IF(BUILD_OPENCS)
|
IF(BUILD_OPENCS)
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "opencs")
|
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.desktop" DESTINATION "${DATAROOTDIR}/applications" COMPONENT "opencs")
|
||||||
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/opencs/openmw-cs.png" DESTINATION "${ICONDIR}" COMPONENT "opencs")
|
INSTALL(FILES "${OpenMW_SOURCE_DIR}/files/opencs/openmw-cs.png" DESTINATION "${ICONDIR}" COMPONENT "opencs")
|
||||||
|
@ -454,14 +452,6 @@ IF(NOT WIN32 AND NOT APPLE)
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "${SYSCONFDIR}" RENAME "openmw.cfg" COMPONENT "openmw")
|
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "${SYSCONFDIR}" RENAME "openmw.cfg" COMPONENT "openmw")
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/resources/version" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
INSTALL(FILES "${OpenMW_BINARY_DIR}/resources/version" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
INSTALL(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb_204.txt" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb_205.txt" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
|
||||||
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/tes3mp-client-default.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
|
||||||
#INSTALL(FILES "${OpenMW_BINARY_DIR}/tes3mp-client.install" DESTINATION "${SYSCONFDIR}" RENAME "tes3mp-client.cfg" COMPONENT "openmw")
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/tes3mp-server-default.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw-mp")
|
|
||||||
#INSTALL(FILES "${OpenMW_BINARY_DIR}/tes3mp-server.install" DESTINATION "${SYSCONFDIR}" RENAME "tes3mp-server.cfg" COMPONENT "openmw-mp")
|
|
||||||
#They both do not exist
|
|
||||||
|
|
||||||
IF(BUILD_OPENCS)
|
IF(BUILD_OPENCS)
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "opencs")
|
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "opencs")
|
||||||
|
@ -473,46 +463,42 @@ IF(NOT WIN32 AND NOT APPLE)
|
||||||
ENDIF(NOT WIN32 AND NOT APPLE)
|
ENDIF(NOT WIN32 AND NOT APPLE)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
FILE(GLOB dll_files_debug "${OpenMW_BINARY_DIR}/Debug/*.dll")
|
FILE(GLOB dll_files "${OpenMW_BINARY_DIR}/Release/*.dll")
|
||||||
FILE(GLOB dll_files_release "${OpenMW_BINARY_DIR}/Release/*.dll")
|
INSTALL(FILES ${dll_files} DESTINATION ".")
|
||||||
INSTALL(FILES ${dll_files_debug} DESTINATION "." CONFIGURATIONS Debug)
|
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg")
|
||||||
INSTALL(FILES ${dll_files_release} DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Debug/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg" CONFIGURATIONS Debug)
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg" CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
|
||||||
INSTALL(FILES "${OpenMW_SOURCE_DIR}/CHANGELOG.md" DESTINATION "." RENAME "CHANGELOG.txt")
|
INSTALL(FILES "${OpenMW_SOURCE_DIR}/CHANGELOG.md" DESTINATION "." RENAME "CHANGELOG.txt")
|
||||||
INSTALL(FILES "${OpenMW_SOURCE_DIR}/README.md" DESTINATION "." RENAME "README.txt")
|
INSTALL(FILES "${OpenMW_SOURCE_DIR}/README.md" DESTINATION "." RENAME "README.txt")
|
||||||
INSTALL(FILES "${OpenMW_SOURCE_DIR}/LICENSE" DESTINATION "." RENAME "LICENSE.txt")
|
|
||||||
INSTALL(FILES
|
INSTALL(FILES
|
||||||
"${OpenMW_SOURCE_DIR}/files/mygui/DejaVu Font License.txt"
|
"${OpenMW_SOURCE_DIR}/Docs/license/GPL3.txt"
|
||||||
|
"${OpenMW_SOURCE_DIR}/Docs/license/DejaVu Font License.txt"
|
||||||
|
"${OpenMW_BINARY_DIR}/settings-default.cfg"
|
||||||
|
"${OpenMW_BINARY_DIR}/gamecontrollerdb.txt"
|
||||||
|
"${OpenMW_BINARY_DIR}/Release/openmw.exe"
|
||||||
DESTINATION ".")
|
DESTINATION ".")
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Debug/settings-default.cfg" DESTINATION "." CONFIGURATIONS Debug)
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/settings-default.cfg" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Debug/tes3mp-client-default.cfg" DESTINATION "." CONFIGURATIONS Debug)
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/tes3mp-client-default.cfg" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Debug/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Debug)
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/gamecontrollerdb.txt" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Debug/gamecontrollerdb_204.txt" DESTINATION "." CONFIGURATIONS Debug)
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/gamecontrollerdb_204.txt" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Debug/gamecontrollerdb_205.txt" DESTINATION "." CONFIGURATIONS Debug)
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/gamecontrollerdb_205.txt" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
|
||||||
|
|
||||||
|
IF(BUILD_LAUNCHER)
|
||||||
|
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-launcher.exe" DESTINATION ".")
|
||||||
|
ENDIF(BUILD_LAUNCHER)
|
||||||
|
IF(BUILD_MWINIIMPORTER)
|
||||||
|
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-iniimporter.exe" DESTINATION ".")
|
||||||
|
ENDIF(BUILD_MWINIIMPORTER)
|
||||||
|
IF(BUILD_ESSIMPORTER)
|
||||||
|
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-essimporter.exe" DESTINATION ".")
|
||||||
|
ENDIF(BUILD_ESSIMPORTER)
|
||||||
|
IF(BUILD_OPENCS)
|
||||||
|
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-cs.exe" DESTINATION ".")
|
||||||
|
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION ".")
|
||||||
|
ENDIF(BUILD_OPENCS)
|
||||||
|
IF(BUILD_WIZARD)
|
||||||
|
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/openmw-wizard.exe" DESTINATION ".")
|
||||||
|
ENDIF(BUILD_WIZARD)
|
||||||
if(BUILD_MYGUI_PLUGIN)
|
if(BUILD_MYGUI_PLUGIN)
|
||||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Debug/Plugin_MyGUI_OpenMW_Resources.dll" DESTINATION "." CONFIGURATIONS Debug)
|
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/Plugin_MyGUI_OpenMW_Resources.dll" DESTINATION ".")
|
||||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/Plugin_MyGUI_OpenMW_Resources.dll" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
|
||||||
ENDIF(BUILD_MYGUI_PLUGIN)
|
ENDIF(BUILD_MYGUI_PLUGIN)
|
||||||
|
|
||||||
IF(DESIRED_QT_VERSION MATCHES 5)
|
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".")
|
||||||
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Debug/platforms" DESTINATION "." CONFIGURATIONS Debug)
|
FILE(GLOB plugin_dir "${OpenMW_BINARY_DIR}/Release/osgPlugins-*")
|
||||||
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/platforms" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
INSTALL(DIRECTORY ${plugin_dir} DESTINATION ".")
|
||||||
ENDIF()
|
|
||||||
|
|
||||||
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Debug/resources" DESTINATION "." CONFIGURATIONS Debug)
|
|
||||||
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/Release/resources" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
|
||||||
|
|
||||||
FILE(GLOB plugin_dir_debug "${OpenMW_BINARY_DIR}/Debug/osgPlugins-*")
|
|
||||||
FILE(GLOB plugin_dir_release "${OpenMW_BINARY_DIR}/Release/osgPlugins-*")
|
|
||||||
INSTALL(DIRECTORY ${plugin_dir_debug} DESTINATION "." CONFIGURATIONS Debug)
|
|
||||||
INSTALL(DIRECTORY ${plugin_dir_release} DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
|
||||||
|
|
||||||
SET(CPACK_GENERATOR "NSIS")
|
SET(CPACK_GENERATOR "NSIS")
|
||||||
SET(CPACK_PACKAGE_NAME "OpenMW")
|
SET(CPACK_PACKAGE_NAME "OpenMW")
|
||||||
|
@ -525,9 +511,6 @@ if(WIN32)
|
||||||
IF(BUILD_LAUNCHER)
|
IF(BUILD_LAUNCHER)
|
||||||
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-launcher;OpenMW Launcher")
|
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-launcher;OpenMW Launcher")
|
||||||
ENDIF(BUILD_LAUNCHER)
|
ENDIF(BUILD_LAUNCHER)
|
||||||
IF(BUILD_BROWSER)
|
|
||||||
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};tes3mp-browser;tes3mp Launcher")
|
|
||||||
ENDIF(BUILD_BROWSER)
|
|
||||||
IF(BUILD_OPENCS)
|
IF(BUILD_OPENCS)
|
||||||
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-cs;OpenMW Construction Set")
|
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};openmw-cs;OpenMW Construction Set")
|
||||||
ENDIF(BUILD_OPENCS)
|
ENDIF(BUILD_OPENCS)
|
||||||
|
@ -543,12 +526,11 @@ if(WIN32)
|
||||||
SET(CPACK_PACKAGE_DESCRIPTION_FILE "${OpenMW_SOURCE_DIR}/README.md")
|
SET(CPACK_PACKAGE_DESCRIPTION_FILE "${OpenMW_SOURCE_DIR}/README.md")
|
||||||
SET(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
|
SET(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
|
||||||
SET(CPACK_NSIS_DISPLAY_NAME "OpenMW ${OPENMW_VERSION}")
|
SET(CPACK_NSIS_DISPLAY_NAME "OpenMW ${OPENMW_VERSION}")
|
||||||
SET(CPACK_NSIS_HELP_LINK "https:\\\\\\\\www.openmw.org")
|
SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.openmw.org")
|
||||||
SET(CPACK_NSIS_URL_INFO_ABOUT "https:\\\\\\\\www.openmw.org")
|
SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.openmw.org")
|
||||||
SET(CPACK_NSIS_INSTALLED_ICON_NAME "openmw-launcher.exe")
|
SET(CPACK_NSIS_INSTALLED_ICON_NAME "openmw-launcher.exe")
|
||||||
SET(CPACK_NSIS_MUI_FINISHPAGE_RUN "openmw-launcher.exe")
|
SET(CPACK_NSIS_MUI_ICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico")
|
||||||
SET(CPACK_NSIS_MUI_ICON "${OpenMW_SOURCE_DIR}/files/tes3mp/tes3mp.ico")
|
SET(CPACK_NSIS_MUI_UNIICON "${OpenMW_SOURCE_DIR}/files/windows/openmw.ico")
|
||||||
SET(CPACK_NSIS_MUI_UNIICON "${OpenMW_SOURCE_DIR}/files/tes3mp/tes3mp.ico")
|
|
||||||
SET(CPACK_PACKAGE_ICON "${OpenMW_SOURCE_DIR}\\\\files\\\\openmw.bmp")
|
SET(CPACK_PACKAGE_ICON "${OpenMW_SOURCE_DIR}\\\\files\\\\openmw.bmp")
|
||||||
|
|
||||||
SET(VCREDIST32 "${OpenMW_BINARY_DIR}/vcredist_x86.exe")
|
SET(VCREDIST32 "${OpenMW_BINARY_DIR}/vcredist_x86.exe")
|
||||||
|
@ -578,26 +560,21 @@ if(WIN32)
|
||||||
endif(WIN32)
|
endif(WIN32)
|
||||||
|
|
||||||
# Extern
|
# Extern
|
||||||
IF(BUILD_OPENMW OR BUILD_OPENCS)
|
|
||||||
add_subdirectory (extern/osg-ffmpeg-videoplayer)
|
add_subdirectory (extern/osg-ffmpeg-videoplayer)
|
||||||
add_subdirectory (extern/oics)
|
add_subdirectory (extern/oics)
|
||||||
if (BUILD_OPENCS)
|
if (USE_QT)
|
||||||
add_subdirectory (extern/osgQt)
|
add_subdirectory (extern/osgQt)
|
||||||
endif()
|
endif()
|
||||||
ENDIF(BUILD_OPENMW OR BUILD_OPENCS)
|
|
||||||
|
|
||||||
# Components
|
# Components
|
||||||
add_subdirectory (components)
|
add_subdirectory (components)
|
||||||
|
|
||||||
|
# Plugins
|
||||||
|
#if (BUILD_MYGUI_PLUGIN)
|
||||||
|
# add_subdirectory(plugins/mygui_resource_plugin)
|
||||||
|
#endif()
|
||||||
|
|
||||||
# Apps and tools
|
# Apps and tools
|
||||||
if (BUILD_OPENMW_MP)
|
|
||||||
add_subdirectory( apps/openmw-mp )
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_MASTER)
|
|
||||||
add_subdirectory( apps/master )
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_OPENMW)
|
if (BUILD_OPENMW)
|
||||||
add_subdirectory( apps/openmw )
|
add_subdirectory( apps/openmw )
|
||||||
endif()
|
endif()
|
||||||
|
@ -614,10 +591,6 @@ if (BUILD_LAUNCHER)
|
||||||
add_subdirectory( apps/launcher )
|
add_subdirectory( apps/launcher )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (BUILD_BROWSER)
|
|
||||||
add_subdirectory( apps/browser )
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_MWINIIMPORTER)
|
if (BUILD_MWINIIMPORTER)
|
||||||
add_subdirectory( apps/mwiniimporter )
|
add_subdirectory( apps/mwiniimporter )
|
||||||
endif()
|
endif()
|
||||||
|
@ -656,20 +629,20 @@ if (WIN32)
|
||||||
endforeach( OUTPUTCONFIG )
|
endforeach( OUTPUTCONFIG )
|
||||||
|
|
||||||
if (USE_DEBUG_CONSOLE AND BUILD_OPENMW)
|
if (USE_DEBUG_CONSOLE AND BUILD_OPENMW)
|
||||||
set_target_properties(tes3mp PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE")
|
set_target_properties(openmw PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE")
|
||||||
set_target_properties(tes3mp PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE")
|
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE")
|
||||||
set_target_properties(tes3mp PROPERTIES COMPILE_DEFINITIONS_DEBUG "_CONSOLE")
|
set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_DEBUG "_CONSOLE")
|
||||||
elseif (BUILD_OPENMW)
|
elseif (BUILD_OPENMW)
|
||||||
# Turn off debug console, debug output will be written to visual studio output instead
|
# Turn off debug console, debug output will be written to visual studio output instead
|
||||||
set_target_properties(tes3mp PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:WINDOWS")
|
set_target_properties(openmw PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:WINDOWS")
|
||||||
set_target_properties(tes3mp PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:WINDOWS")
|
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:WINDOWS")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (BUILD_OPENMW)
|
if (BUILD_OPENMW)
|
||||||
# Release builds don't use the debug console
|
# Release builds use the debug console
|
||||||
set_target_properties(tes3mp PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
|
set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:CONSOLE")
|
||||||
set_target_properties(tes3mp PROPERTIES COMPILE_DEFINITIONS_RELEASE "_WINDOWS")
|
set_target_properties(openmw PROPERTIES COMPILE_DEFINITIONS_RELEASE "_CONSOLE")
|
||||||
set_target_properties(tes3mp PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
|
set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:CONSOLE")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Play a bit with the warning levels
|
# Play a bit with the warning levels
|
||||||
|
@ -680,8 +653,7 @@ if (WIN32)
|
||||||
# Warnings that aren't enabled normally and don't need to be enabled
|
# Warnings that aren't enabled normally and don't need to be enabled
|
||||||
# They're unneeded and sometimes completely retarded warnings that /Wall enables
|
# They're unneeded and sometimes completely retarded warnings that /Wall enables
|
||||||
# Not going to bother commenting them as they tend to warn on every standard library file
|
# Not going to bother commenting them as they tend to warn on every standard library file
|
||||||
4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4610 4619 4623 4625
|
4061 4263 4264 4266 4350 4371 4435 4514 4548 4571 4610 4619 4623 4625 4626 4628 4640 4668 4710 4711 4820 4826 4917 4946
|
||||||
4626 4628 4640 4668 4710 4711 4768 4820 4826 4917 4946 5032 5039 5045
|
|
||||||
|
|
||||||
# Warnings that are thrown on standard libraries and not OpenMW
|
# Warnings that are thrown on standard libraries and not OpenMW
|
||||||
4347 # Non-template function with same name and parameter count as template function
|
4347 # Non-template function with same name and parameter count as template function
|
||||||
|
@ -689,21 +661,13 @@ if (WIN32)
|
||||||
4510 4512 # Unable to generate copy constructor/assignment operator as it's not public in the base
|
4510 4512 # Unable to generate copy constructor/assignment operator as it's not public in the base
|
||||||
4706 # Assignment in conditional expression
|
4706 # Assignment in conditional expression
|
||||||
4738 # Storing 32-bit float result in memory, possible loss of performance
|
4738 # Storing 32-bit float result in memory, possible loss of performance
|
||||||
4774 # Format string expected in argument is not a string literal
|
|
||||||
4986 # Undocumented warning that occurs in the crtdbg.h file
|
4986 # Undocumented warning that occurs in the crtdbg.h file
|
||||||
4987 # nonstandard extension used (triggered by setjmp.h)
|
4987 # nonstandard extension used (triggered by setjmp.h)
|
||||||
4996 # Function was declared deprecated
|
4996 # Function was declared deprecated
|
||||||
|
|
||||||
# caused by OSG
|
|
||||||
4589 # Constructor of abstract class 'osg::Operation' ignores initializer for virtual base class 'osg::Referenced' (False warning)
|
|
||||||
|
|
||||||
# caused by boost
|
# caused by boost
|
||||||
4191 # 'type cast' : unsafe conversion (1.56, thread_primitives.hpp, normally off)
|
4191 # 'type cast' : unsafe conversion (1.56, thread_primitives.hpp, normally off)
|
||||||
|
|
||||||
# caused by MyGUI
|
|
||||||
4275 # non dll-interface class 'std::exception' used as base for dll-interface class 'MyGUI::Exception'
|
|
||||||
4297 # function assumed not to throw an exception but does
|
|
||||||
|
|
||||||
# OpenMW specific warnings
|
# OpenMW specific warnings
|
||||||
4099 # Type mismatch, declared class or struct is defined with other type
|
4099 # Type mismatch, declared class or struct is defined with other type
|
||||||
4100 # Unreferenced formal parameter (-Wunused-parameter)
|
4100 # Unreferenced formal parameter (-Wunused-parameter)
|
||||||
|
@ -717,29 +681,20 @@ if (WIN32)
|
||||||
4309 # Variable overflow, trying to store 128 in a signed char for example
|
4309 # Variable overflow, trying to store 128 in a signed char for example
|
||||||
4351 # New behavior: elements of array 'array' will be default initialized (desired behavior)
|
4351 # New behavior: elements of array 'array' will be default initialized (desired behavior)
|
||||||
4355 # Using 'this' in member initialization list
|
4355 # Using 'this' in member initialization list
|
||||||
4464 # relative include path contains '..'
|
|
||||||
4505 # Unreferenced local function has been removed
|
4505 # Unreferenced local function has been removed
|
||||||
4701 # Potentially uninitialized local variable used
|
4701 # Potentially uninitialized local variable used
|
||||||
4702 # Unreachable code
|
4702 # Unreachable code
|
||||||
4714 # function 'QString QString::trimmed(void) &&' marked as __forceinline not inlined
|
|
||||||
4800 # Boolean optimization warning, e.g. myBool = (myInt != 0) instead of myBool = myInt
|
4800 # Boolean optimization warning, e.g. myBool = (myInt != 0) instead of myBool = myInt
|
||||||
)
|
)
|
||||||
|
|
||||||
if (MSVC_VERSION GREATER 1800)
|
|
||||||
set(WARNINGS_DISABLE ${WARNINGS_DISABLE} 5026 5027
|
|
||||||
5031 # #pragma warning(pop): likely mismatch, popping warning state pushed in different file (config_begin.hpp, config_end.hpp)
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
foreach(d ${WARNINGS_DISABLE})
|
foreach(d ${WARNINGS_DISABLE})
|
||||||
set(WARNINGS "${WARNINGS} /wd${d}")
|
set(WARNINGS "${WARNINGS} /wd${d}")
|
||||||
endforeach(d)
|
endforeach(d)
|
||||||
|
|
||||||
set_target_properties(components PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
set_target_properties(components PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||||
|
# oics uses tinyxml, which has an initialized but unused variable
|
||||||
if (BUILD_OPENMW)
|
set_target_properties(oics PROPERTIES COMPILE_FLAGS "${WARNINGS} /wd4189 ${MT_BUILD}")
|
||||||
set_target_properties(osg-ffmpeg-videoplayer PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
set_target_properties(osg-ffmpeg-videoplayer PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_BSATOOL)
|
if (BUILD_BSATOOL)
|
||||||
set_target_properties(bsatool PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
set_target_properties(bsatool PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||||
|
@ -757,10 +712,6 @@ if (WIN32)
|
||||||
set_target_properties(openmw-launcher PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
set_target_properties(openmw-launcher PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (BUILD_BROWSER)
|
|
||||||
set_target_properties(tes3mp-browser PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_MWINIIMPORTER)
|
if (BUILD_MWINIIMPORTER)
|
||||||
set_target_properties(openmw-iniimporter PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
set_target_properties(openmw-iniimporter PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||||
endif()
|
endif()
|
||||||
|
@ -770,11 +721,7 @@ if (WIN32)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (BUILD_OPENMW)
|
if (BUILD_OPENMW)
|
||||||
if (OPENMW_UNITY_BUILD)
|
set_target_properties(openmw PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||||
set_target_properties(tes3mp PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD} /bigobj")
|
|
||||||
else()
|
|
||||||
set_target_properties(tes3mp PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (BUILD_WIZARD)
|
if (BUILD_WIZARD)
|
||||||
|
@ -783,27 +730,32 @@ if (WIN32)
|
||||||
endif(MSVC)
|
endif(MSVC)
|
||||||
|
|
||||||
# TODO: At some point release builds should not use the console but rather write to a log file
|
# TODO: At some point release builds should not use the console but rather write to a log file
|
||||||
#set_target_properties(tes3mp PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
|
#set_target_properties(openmw PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
|
||||||
#set_target_properties(tes3mp PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
|
#set_target_properties(openmw PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Apple bundling
|
# Apple bundling
|
||||||
if (OPENMW_OSX_DEPLOYMENT AND APPLE AND DESIRED_QT_VERSION MATCHES 5)
|
if (APPLE)
|
||||||
get_property(QT_COCOA_PLUGIN_PATH TARGET Qt5::QCocoaIntegrationPlugin PROPERTY LOCATION_RELEASE)
|
get_property(QT_COCOA_PLUGIN_PATH TARGET Qt5::QCocoaIntegrationPlugin PROPERTY LOCATION_RELEASE)
|
||||||
get_filename_component(QT_COCOA_PLUGIN_DIR "${QT_COCOA_PLUGIN_PATH}" DIRECTORY)
|
get_filename_component(QT_COCOA_PLUGIN_DIR "${QT_COCOA_PLUGIN_PATH}" DIRECTORY)
|
||||||
get_filename_component(QT_COCOA_PLUGIN_GROUP "${QT_COCOA_PLUGIN_DIR}" NAME)
|
get_filename_component(QT_COCOA_PLUGIN_GROUP "${QT_COCOA_PLUGIN_DIR}" NAME)
|
||||||
get_filename_component(QT_COCOA_PLUGIN_NAME "${QT_COCOA_PLUGIN_PATH}" NAME)
|
get_filename_component(QT_COCOA_PLUGIN_NAME "${QT_COCOA_PLUGIN_PATH}" NAME)
|
||||||
configure_file("${QT_COCOA_PLUGIN_PATH}" "${APP_BUNDLE_DIR}/Contents/PlugIns/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY)
|
configure_file("${QT_COCOA_PLUGIN_PATH}" "${APP_BUNDLE_DIR}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY)
|
||||||
configure_file("${OpenMW_SOURCE_DIR}/files/mac/qt.conf" "${APP_BUNDLE_DIR}/Contents/Resources/qt.conf" COPYONLY)
|
|
||||||
|
|
||||||
if (BUILD_OPENCS)
|
if (BUILD_OPENCS)
|
||||||
get_property(OPENCS_BUNDLE_NAME_TMP TARGET openmw-cs PROPERTY OUTPUT_NAME)
|
get_property(OPENCS_BUNDLE_NAME_TMP TARGET openmw-cs PROPERTY OUTPUT_NAME)
|
||||||
set(OPENCS_BUNDLE_NAME "${OPENCS_BUNDLE_NAME_TMP}.app")
|
set(OPENCS_BUNDLE_NAME "${OPENCS_BUNDLE_NAME_TMP}.app")
|
||||||
configure_file("${QT_COCOA_PLUGIN_PATH}" "${OPENCS_BUNDLE_NAME}/Contents/PlugIns/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY)
|
configure_file("${QT_COCOA_PLUGIN_PATH}" "${OPENCS_BUNDLE_NAME}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}" COPYONLY)
|
||||||
configure_file("${OpenMW_SOURCE_DIR}/files/mac/qt.conf" "${OPENCS_BUNDLE_NAME}/Contents/Resources/qt.conf" COPYONLY)
|
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "." COMPONENT Runtime)
|
set(INSTALL_SUBDIR OpenMW)
|
||||||
|
|
||||||
|
install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
||||||
|
install(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
||||||
|
install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
||||||
|
install(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
||||||
|
install(FILES "${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
||||||
|
install(FILES "${OpenMW_BINARY_DIR}/openmw-cs.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
||||||
|
|
||||||
set(CPACK_GENERATOR "DragNDrop")
|
set(CPACK_GENERATOR "DragNDrop")
|
||||||
set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION})
|
set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION})
|
||||||
|
@ -811,8 +763,8 @@ if (OPENMW_OSX_DEPLOYMENT AND APPLE AND DESIRED_QT_VERSION MATCHES 5)
|
||||||
set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR})
|
set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR})
|
||||||
set(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})
|
set(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})
|
||||||
|
|
||||||
set(INSTALLED_OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${APP_BUNDLE_NAME}")
|
set(INSTALLED_OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}")
|
||||||
set(INSTALLED_OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${OPENCS_BUNDLE_NAME}")
|
set(INSTALLED_OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}")
|
||||||
|
|
||||||
install(CODE "
|
install(CODE "
|
||||||
set(BU_CHMOD_BUNDLE_ITEMS ON)
|
set(BU_CHMOD_BUNDLE_ITEMS ON)
|
||||||
|
@ -822,16 +774,19 @@ if (OPENMW_OSX_DEPLOYMENT AND APPLE AND DESIRED_QT_VERSION MATCHES 5)
|
||||||
" COMPONENT Runtime)
|
" COMPONENT Runtime)
|
||||||
|
|
||||||
set(ABSOLUTE_PLUGINS "")
|
set(ABSOLUTE_PLUGINS "")
|
||||||
|
set(USED_OSG_PLUGINS
|
||||||
|
osgdb_dds
|
||||||
|
osgdb_jpeg
|
||||||
|
osgdb_png
|
||||||
|
osgdb_tga
|
||||||
|
)
|
||||||
|
|
||||||
foreach (PLUGIN_NAME ${USED_OSG_PLUGINS})
|
foreach (PLUGIN_NAME ${USED_OSG_PLUGINS})
|
||||||
set(PLUGIN_ABS "${OSGPlugins_LIB_DIR}/${PLUGIN_NAME}.so")
|
set(PLUGIN_ABS "${OSG_PLUGIN_LIB_SEARCH_PATH}/${PLUGIN_NAME}.so")
|
||||||
set(ABSOLUTE_PLUGINS ${PLUGIN_ABS} ${ABSOLUTE_PLUGINS})
|
set(ABSOLUTE_PLUGINS ${PLUGIN_ABS} ${ABSOLUTE_PLUGINS})
|
||||||
endforeach ()
|
endforeach ()
|
||||||
|
|
||||||
get_filename_component(OSG_PLUGIN_PREFIX_DIR "${OSGPlugins_LIB_DIR}" NAME)
|
get_filename_component(OSG_PLUGIN_PREFIX_DIR "${OSG_PLUGIN_LIB_SEARCH_PATH}" NAME)
|
||||||
if (NOT OSG_PLUGIN_PREFIX_DIR)
|
|
||||||
message(FATAL_ERROR "Can't get directory name for OSG plugins from '${OSGPlugins_LIB_DIR}'")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# installs used plugins in bundle at given path (bundle_path must be relative to ${CMAKE_INSTALL_PREFIX})
|
# installs used plugins in bundle at given path (bundle_path must be relative to ${CMAKE_INSTALL_PREFIX})
|
||||||
# and returns list of install paths for all installed plugins
|
# and returns list of install paths for all installed plugins
|
||||||
|
@ -856,11 +811,11 @@ if (OPENMW_OSX_DEPLOYMENT AND APPLE AND DESIRED_QT_VERSION MATCHES 5)
|
||||||
set(${plugins_var} ${PLUGINS} PARENT_SCOPE)
|
set(${plugins_var} ${PLUGINS} PARENT_SCOPE)
|
||||||
endfunction (install_plugins_for_bundle)
|
endfunction (install_plugins_for_bundle)
|
||||||
|
|
||||||
install_plugins_for_bundle("${APP_BUNDLE_NAME}" PLUGINS)
|
install_plugins_for_bundle("${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}" PLUGINS)
|
||||||
install_plugins_for_bundle("${OPENCS_BUNDLE_NAME}" OPENCS_PLUGINS)
|
install_plugins_for_bundle("${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}" OPENCS_PLUGINS)
|
||||||
|
|
||||||
set(PLUGINS ${PLUGINS} "${INSTALLED_OPENMW_APP}/Contents/PlugIns/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}")
|
set(PLUGINS ${PLUGINS} "${INSTALLED_OPENMW_APP}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}")
|
||||||
set(OPENCS_PLUGINS ${OPENCS_PLUGINS} "${INSTALLED_OPENCS_APP}/Contents/PlugIns/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}")
|
set(OPENCS_PLUGINS ${OPENCS_PLUGINS} "${INSTALLED_OPENCS_APP}/Contents/MacOS/${QT_COCOA_PLUGIN_GROUP}/${QT_COCOA_PLUGIN_NAME}")
|
||||||
|
|
||||||
install(CODE "
|
install(CODE "
|
||||||
function(gp_item_default_embedded_path_override item default_embedded_path_var)
|
function(gp_item_default_embedded_path_override item default_embedded_path_var)
|
||||||
|
@ -870,11 +825,12 @@ if (OPENMW_OSX_DEPLOYMENT AND APPLE AND DESIRED_QT_VERSION MATCHES 5)
|
||||||
endif()
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
cmake_policy(SET CMP0009 OLD)
|
||||||
fixup_bundle(\"${INSTALLED_OPENMW_APP}\" \"${PLUGINS}\" \"\")
|
fixup_bundle(\"${INSTALLED_OPENMW_APP}\" \"${PLUGINS}\" \"\")
|
||||||
fixup_bundle(\"${INSTALLED_OPENCS_APP}\" \"${OPENCS_PLUGINS}\" \"\")
|
fixup_bundle(\"${INSTALLED_OPENCS_APP}\" \"${OPENCS_PLUGINS}\" \"\")
|
||||||
" COMPONENT Runtime)
|
" COMPONENT Runtime)
|
||||||
include(CPack)
|
include(CPack)
|
||||||
endif ()
|
endif (APPLE)
|
||||||
|
|
||||||
# Doxygen Target -- simply run 'make doc' or 'make doc_pages'
|
# Doxygen Target -- simply run 'make doc' or 'make doc_pages'
|
||||||
# output directory for 'make doc' is "${OpenMW_BINARY_DIR}/docs/Doxygen"
|
# output directory for 'make doc' is "${OpenMW_BINARY_DIR}/docs/Doxygen"
|
||||||
|
|
115
CONTRIBUTING.md
115
CONTRIBUTING.md
|
@ -1,115 +1,16 @@
|
||||||
How to contribute to OpenMW
|
Description
|
||||||
=======================
|
===========
|
||||||
|
|
||||||
Not sure what to do with all your free time? Pick out a task from here:
|
Your pull request description should include (if applicable):
|
||||||
|
|
||||||
https://gitlab.com/OpenMW/openmw/issues
|
* A link back to the bug report or forum discussion that prompted the change
|
||||||
|
|
||||||
Currently, we are focused on completing the MW game experience and general polishing. Features out of this scope may be approved in some cases, but you should probably start a discussion first.
|
|
||||||
|
|
||||||
Note:
|
|
||||||
* Tasks set to 'openmw-future' are usually out of the current scope of the project and can't be started yet.
|
|
||||||
* Bugs that are not 'Confirmed' should be confirmed first.
|
|
||||||
* Often, it's best to start a discussion about possible solutions before you jump into coding, especially for larger features.
|
|
||||||
|
|
||||||
Aside from coding, you can also help by triaging the issues list. Check for bugs that are 'Unconfirmed' and try to confirm them on your end, working out any details that may be necessary. Check for bugs that do not conform to [Bug reporting guidelines](https://wiki.openmw.org/index.php?title=Bug_Reporting_Guidelines) and improve them to do so!
|
|
||||||
|
|
||||||
There are various [Tools](https://wiki.openmw.org/index.php?title=Tools) to facilitate testing/development.
|
|
||||||
|
|
||||||
Pull Request Guidelines
|
|
||||||
=======================
|
|
||||||
|
|
||||||
To facilitate the review process, your pull request description should include the following, if applicable:
|
|
||||||
|
|
||||||
* A link back to the bug report or forum discussion that prompted the change. Note: when linking bugs, use the syntax ```[Bug #xyz](https://bugs.openmw.org/issues/#xyz)``` to create a clickable link. Writing only 'Bug #xyz' will unfortunately create a link to the Github pull request with that number instead.
|
|
||||||
* Summary of the changes made
|
* Summary of the changes made
|
||||||
* Reasoning / motivation behind the change
|
* Reasoning / motivation behind the change
|
||||||
* What testing you have carried out to verify the change
|
* What testing you have carried out to verify the change
|
||||||
|
|
||||||
Furthermore, we advise to:
|
Other notes
|
||||||
|
|
||||||
* Avoid stuffing unrelated commits into one pull request. As a rule of thumb, each feature and each bugfix should go into a separate PR, unless they are closely related or dependent upon each other. Small pull requests are easier to review, and are less likely to require further changes before we can merge them. A "mega" pull request with lots of unrelated commits in it is likely to get held up in review for a long time.
|
|
||||||
* Feel free to submit incomplete pull requests. Even if the work can not be merged yet, pull requests are a great place to collect early feedback. Just make sure to mark it as *[Incomplete]* or *[Do not merge yet]* in the title.
|
|
||||||
* If you plan on contributing often, please read the [Developer Reference](https://wiki.openmw.org/index.php?title=Developer_Reference) on our wiki, especially the [Policies and Standards](https://wiki.openmw.org/index.php?title=Policies_and_Standards).
|
|
||||||
* Make sure each of your changes has a clear objective. Unnecessary changes may lead to merge conflicts, clutter the commit history and slow down review. Code formatting 'fixes' should be avoided, unless you were already changing that particular line anyway.
|
|
||||||
* Reference the bug / feature ticket(s) in your commit message (e.g. 'Bug #123') to make it easier to keep track of what we changed for what reason. Our bugtracker will show those commits next to the ticket. If your commit message includes 'Fixes #123', that bug/feature will automatically be set to 'Closed' when your commit is merged.
|
|
||||||
* When pulling changes from master, prefer rebase over merge. Consider using a merge if there are conflicts or for long-running PRs.
|
|
||||||
|
|
||||||
Guidelines for original engine "fixes"
|
|
||||||
=================================
|
|
||||||
|
|
||||||
From time to time you may be tempted to "fix" what you think was a "bug" in the original game engine.
|
|
||||||
|
|
||||||
Unfortunately, the definition of what is a "bug" is not so clear. Consider that your "bug" is actually a feature unless proven otherwise:
|
|
||||||
|
|
||||||
* We have no way of knowing what the original developers really intended (short of asking them, good luck with that).
|
|
||||||
* What may seem like an illogical mechanic can actually be part of an attempt to balance the game.
|
|
||||||
* Many people will actually <i>like</i> these "bugs" because that is what they remember the game for.
|
|
||||||
* Exploits may be part of the fun of an open-world game - they reward knowledge with power. There are too many of them to plug them all, anyway.
|
|
||||||
|
|
||||||
OpenMW, in its default configuration, is meant to be a faithful reimplementation of Morrowind, minus things like crash bugs, stability issues and severe design errors. However, we try to avoid touching anything that affects the core gameplay, the balancing of the game or introduces incompatibilities with existing mod content.
|
|
||||||
|
|
||||||
That said, we may sometimes evaluate such issues on an individual basis. Common exceptions to the above would be:
|
|
||||||
|
|
||||||
* Issues so glaring that they would severely limit the capabilities of the engine in the future (for example, the scripting engine not being allowed to access objects in remote cells)
|
|
||||||
* Bugs where the intent is very obvious, and that have little to no balancing impact (e.g. the bug were being tired made it easier to repair items, instead of harder)
|
|
||||||
* Bugs that were fixed in an official patch for Morrowind
|
|
||||||
|
|
||||||
Feature additions policy
|
|
||||||
=====================
|
|
||||||
|
|
||||||
We get it, you have waited so long for feature XYZ to be available in Morrowind and now that OpenMW is here you can not wait to implement your ingenious idea and share it with the world.
|
|
||||||
|
|
||||||
Unfortunately, since maintaining features comes at a cost and our resources are limited, we have to be a little selective in what features we allow into the main repository. Generally:
|
|
||||||
|
|
||||||
* Features should be as generic and non-redundant as possible.
|
|
||||||
* Any feature that is also possible with modding should be done as a mod instead.
|
|
||||||
* In the future, OpenMW plans to expand the scope of what is possible with modding, e.g. by moving certain game logic into editable scripts.
|
|
||||||
* Currently, modders can edit OpenMW's GUI skins and layout XML files, although there are still a few missing hooks (e.g. scripting support) in order to make this into a powerful way of modding.
|
|
||||||
* If a feature introduces new game UI strings, that reduces its chance of being accepted because we do not currently have any way of localizing these to the user's Morrowind installation language.
|
|
||||||
|
|
||||||
If you are in doubt of your feature being within our scope, it is probably best to start a forum discussion first. See the [settings documentation](https://openmw.readthedocs.io/en/stable/reference/modding/settings/index.html) and [Features list](https://wiki.openmw.org/index.php?title=Features) for some examples of features that were deemed acceptable.
|
|
||||||
|
|
||||||
Reviewing pull requests
|
|
||||||
=======================
|
|
||||||
|
|
||||||
We welcome any help in reviewing open PRs. You don't need to be a developer to comment on new features. We also encourage ["junior" developers to review senior's work](https://pagefault.blog/2018/04/08/why-junior-devs-should-review-seniors-commits/).
|
|
||||||
|
|
||||||
This review process is divided into two sections because complaining about code or style issues hardly makes sense until the functionality of the PR is deemed OK. Anyone can help with the Functionality Review while most parts of the Code Review require you to have programming experience.
|
|
||||||
|
|
||||||
In addition to the checklist below, make sure to check that the Pull Request Guidelines (first half of this document) were followed.
|
|
||||||
|
|
||||||
First review
|
|
||||||
============
|
|
||||||
|
|
||||||
1. Ask for missing information or clarifications. Compare against the project's design goals and roadmap.
|
|
||||||
2. Check if the automated tests are passing. If they are not, make the PR author aware of the issue and potentially link to the error line on Travis CI or Appveyor. If the error appears unrelated to the PR and/or the master branch is failing with the same error, our CI has broken and needs to be fixed independently of any open PRs. Raise this issue on the forums, bug tracker or with the relevant maintainer. The PR can be merged in this case as long as you've built it yourself to make sure it does build.
|
|
||||||
3. Make sure that the new code has been tested thoroughly, either by asking the author or, preferably, testing yourself. In a complex project like OpenMW, it is easy to make mistakes, typos, etc. Therefore, prefer testing all code changes, no matter how trivial they look. When you have tested a PR that no one has tested so far, post a comment letting us know.
|
|
||||||
4. On long running PRs, request the author to update its description with the current state or a checklist of things left to do.
|
|
||||||
|
|
||||||
Code Review
|
|
||||||
===========
|
===========
|
||||||
|
|
||||||
1. Carefully review each line for issues the author may not have thought of, paying special attention to 'special' cases. Often, people build their code with a particular mindset and forget about other configurations or unexpected interactions.
|
* Separate your work into multiple pull requests whenever possible. As a rule of thumb, each feature and each bugfix should go into a separate PR, unless they are closely related or dependent upon each other. Small pull requests are easier to review, and are less likely to require further changes before we can merge them. A "mega" pull request with lots of unrelated commits in it is likely to get held up in review for a long time.
|
||||||
2. If any changes are workarounds for an issue in an upstream library, make sure the issue was reported upstream so we can eventually drop the workaround when the issue is fixed and the new version of that library is a build dependency.
|
* Feel free to submit incomplete pull requests. Even if the work can not be merged yet, pull requests are a great place to collect early feedback. Just make sure to mark it as *[Incomplete]* or *[Do not merge yet]* in the title.
|
||||||
3. Make sure PRs do not turn into arguments about hardly related issues. If the PR author disagrees with an established part of the project (e.g. supported build environments), they should open a forum discussion or bug report and in the meantime adjust the PR to adhere to the established way, rather than leaving the PR hanging on a dispute.
|
* If you plan on contributing often, please read the [Developer Reference](https://wiki.openmw.org/index.php?title=Developer_Reference) on our wiki, especially the [Policies and Standards](https://wiki.openmw.org/index.php?title=Policies_and_Standards).
|
||||||
4. Check if the code matches our style guidelines.
|
|
||||||
5. Check to make sure the commit history is clean. Squashing should be considered if the review process has made the commit history particularly long. Commits that don't build should be avoided because they are a nuisance for ```git bisect```.
|
|
||||||
|
|
||||||
Merging
|
|
||||||
=======
|
|
||||||
|
|
||||||
To be able to merge PRs, commit priviledges are required. If you do not have the priviledges, just ping someone that does have them with a short comment like "Looks good to me @user".
|
|
||||||
|
|
||||||
The person to merge the PR may either use github's Merge button or if using the command line, use the ```--no-ff``` flag (so a merge commit is created, just like with Github's merge button) and include the pull request number in the commit description.
|
|
||||||
|
|
||||||
Dealing with regressions
|
|
||||||
========================
|
|
||||||
|
|
||||||
The master branch should always be in a working state that is not worse than the previous release in any way. If a regression is found, the first and foremost priority should be to get the regression fixed quickly, either by reverting the change that caused it or finding a better solution. Please avoid leaving the project in the 'broken' state for an extensive period of time while proper solutions are found. If the solution takes more than a day or so then it is usually better to revert the offending change first and reapply it later when fixed.
|
|
||||||
|
|
||||||
Other resources
|
|
||||||
===============
|
|
||||||
|
|
||||||
[GitHub blog - how to write the perfect pull request](https://blog.github.com/2015-01-21-how-to-write-the-perfect-pull-request/)
|
|
||||||
|
|
||||||
|
|
116
README.md
116
README.md
|
@ -1,49 +1,107 @@
|
||||||
TES3MP
|
OpenMW
|
||||||
======
|
======
|
||||||
|
|
||||||
Copyright (c) 2008-2015, OpenMW Team
|
[![Build Status](https://api.travis-ci.org/OpenMW/openmw.svg)](https://travis-ci.org/OpenMW/openmw) [![Build status](https://ci.appveyor.com/api/projects/status/e6bqw8oouy8ufd46?svg=true)](https://ci.appveyor.com/project/scrawl/openmw) [![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740)
|
||||||
Copyright (c) 2016-2019, Stanislav Zhukov & David Cernat
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/TES3MP/openmw-tes3mp.svg?branch=0.7.0)](https://travis-ci.org/TES3MP/openmw-tes3mp)
|
OpenMW is a recreation of the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work.
|
||||||
|
|
||||||
TES3MP is a project adding multiplayer functionality to [OpenMW](https://github.com/OpenMW/openmw), an open-source game engine that supports playing "The Elder Scrolls III: Morrowind" by Bethesda Softworks.
|
OpenMW also comes with OpenMW-CS, a replacement for Morrowind's TES Construction Set.
|
||||||
|
|
||||||
* TES3MP version: 0.7.0-alpha
|
* Version: 0.38.0
|
||||||
* OpenMW version: 0.44.0
|
* License: GPL (see docs/license/GPL3.txt for more information)
|
||||||
* License: GPLv3 (see [LICENSE](https://github.com/TES3MP/openmw-tes3mp/blob/master/LICENSE) for more information)
|
* Website: http://www.openmw.org
|
||||||
|
* IRC: #openmw on irc.freenode.net
|
||||||
|
|
||||||
Font Licenses:
|
Font Licenses:
|
||||||
* DejaVuLGCSansMono.ttf: custom (see [files/mygui/DejaVu Font License.txt](https://github.com/TES3MP/openmw-tes3mp/blob/master/files/mygui/DejaVu%20Font%20License.txt) for more information)
|
* DejaVuLGCSansMono.ttf: custom (see docs/license/DejaVu Font License.txt for more information)
|
||||||
|
|
||||||
Project status
|
Current Status
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
[Version changelog](https://github.com/TES3MP/openmw-tes3mp/blob/master/tes3mp-changelog.md)
|
The main quests in Morrowind, Tribunal and Bloodmoon are all completable. Some issues with side quests are to be expected (but rare). Check the [bug tracker](https://bugs.openmw.org/versions/21) for a list of issues we need to resolve before the "1.0" release. Even before the "1.0" release however, OpenMW boasts some new [features](https://wiki.openmw.org/index.php?title=Features), such as improved graphics and user interfaces.
|
||||||
|
|
||||||
As of version 0.7.0, TES3MP is fully playable, providing very extensive player, NPC, world and quest synchronization, as well as state saving and loading, all of which are highly customizable via [serverside Lua scripts](https://github.com/TES3MP/CoreScripts).
|
Pre-existing modifications created for the original Morrowind engine can be hit-and-miss. The OpenMW script compiler performs more thorough error-checking than Morrowind does, meaning that a mod created for Morrowind may not necessarily run in OpenMW. Some mods also rely on quirky behaviour or engine bugs in order to work. We are considering such compatibility issues on a case-by-case basis - in some cases adding a workaround to OpenMW may be feasible, in other cases fixing the mod will be the only option. If you know of any mods that work or don't work, feel free to add them to the [Mod status](https://wiki.openmw.org/index.php?title=Mod_status) wiki page.
|
||||||
|
|
||||||
Remaining gameplay problems mostly relate to AI and the synchronization of clientside script variables.
|
Getting Started
|
||||||
|
|
||||||
Donations
|
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
You can benefit the project by donating on Patreon to our two developers, [David Cernat](https://www.patreon.com/davidcernat) and [Koncord](https://www.patreon.com/Koncord), as well as by supporting [OpenMW](https://openmw.org).
|
* [Official forums](https://forum.openmw.org/)
|
||||||
|
* [Installation instructions](https://wiki.openmw.org/index.php?title=Installation_Instructions)
|
||||||
|
* [Build from source](https://wiki.openmw.org/index.php?title=Development_Environment_Setup)
|
||||||
|
* [Testing the game](https://wiki.openmw.org/index.php?title=Testing)
|
||||||
|
* [How to contribute](https://wiki.openmw.org/index.php?title=Contribution_Wanted)
|
||||||
|
* [Report a bug](http://bugs.openmw.org/projects/openmw) - read the [guidelines](https://wiki.openmw.org/index.php?title=Bug_Reporting_Guidelines) before submitting your first bug!
|
||||||
|
* [Known issues](http://bugs.openmw.org/projects/openmw/issues?utf8=%E2%9C%93&set_filter=1&f%5B%5D=status_id&op%5Bstatus_id%5D=%3D&v%5Bstatus_id%5D%5B%5D=7&f%5B%5D=tracker_id&op%5Btracker_id%5D=%3D&v%5Btracker_id%5D%5B%5D=1&f%5B%5D=&c%5B%5D=project&c%5B%5D=tracker&c%5B%5D=status&c%5B%5D=priority&c%5B%5D=subject&c%5B%5D=assigned_to&c%5B%5D=updated_on&group_by=tracker)
|
||||||
|
|
||||||
Contributing
|
The data path
|
||||||
---------------
|
-------------
|
||||||
|
|
||||||
Helping us with documentation, bug hunting and video showcases is always greatly appreciated.
|
The data path tells OpenMW where to find your Morrowind files. If you run the launcher, OpenMW should be able to pick up the location of these files on its own, if both Morrowind and OpenMW are installed properly (installing Morrowind under WINE is considered a proper install).
|
||||||
|
|
||||||
For code contributions, it's best to start out with modestly sized fixes and features and work your way up. There are so many different possible implementations of more major features – many of which would cause undesirable code or vision conflicts with OpenMW – that those should be talked over in advance with the existing developers before effort is spent on them.
|
Command line options
|
||||||
|
--------------------
|
||||||
|
|
||||||
Feel free to contact the [team members](https://github.com/TES3MP/openmw-tes3mp/blob/master/tes3mp-credits.md) for any questions you might have.
|
Syntax: openmw <options>
|
||||||
|
Allowed options:
|
||||||
|
--help print help message
|
||||||
|
--version print version information and quit
|
||||||
|
--data arg (=data) set data directories (later directories
|
||||||
|
have higher priority)
|
||||||
|
--data-local arg set local data directory (highest
|
||||||
|
priority)
|
||||||
|
--fallback-archive arg (=fallback-archive)
|
||||||
|
set fallback BSA archives (later
|
||||||
|
archives have higher priority)
|
||||||
|
--resources arg (=resources) set resources directory
|
||||||
|
--start arg set initial cell
|
||||||
|
--content arg content file(s): esm/esp, or
|
||||||
|
omwgame/omwaddon
|
||||||
|
--no-sound [=arg(=1)] (=0) disable all sounds
|
||||||
|
--script-verbose [=arg(=1)] (=0) verbose script output
|
||||||
|
--script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue
|
||||||
|
scripts) at startup
|
||||||
|
--script-all-dialogue [=arg(=1)] (=0) compile all dialogue scripts at startup
|
||||||
|
--script-console [=arg(=1)] (=0) enable console-only script
|
||||||
|
functionality
|
||||||
|
--script-run arg select a file containing a list of
|
||||||
|
console commands that is executed on
|
||||||
|
startup
|
||||||
|
--script-warn [=arg(=1)] (=1) handling of warnings when compiling
|
||||||
|
scripts
|
||||||
|
0 - ignore warning
|
||||||
|
1 - show warning but consider script as
|
||||||
|
correctly compiled anyway
|
||||||
|
2 - treat warnings as errors
|
||||||
|
--script-blacklist arg ignore the specified script (if the use
|
||||||
|
of the blacklist is enabled)
|
||||||
|
--script-blacklist-use [=arg(=1)] (=1)
|
||||||
|
enable script blacklisting
|
||||||
|
--load-savegame arg load a save game file on game startup
|
||||||
|
(specify an absolute filename or a
|
||||||
|
filename relative to the current
|
||||||
|
working directory)
|
||||||
|
--skip-menu [=arg(=1)] (=0) skip main menu on game startup
|
||||||
|
--new-game [=arg(=1)] (=0) run new game sequence (ignored if
|
||||||
|
skip-menu=0)
|
||||||
|
--fs-strict [=arg(=1)] (=0) strict file system handling (no case
|
||||||
|
folding)
|
||||||
|
--encoding arg (=win1252) Character encoding used in OpenMW game
|
||||||
|
messages:
|
||||||
|
|
||||||
Getting started
|
win1250 - Central and Eastern European
|
||||||
---------------
|
such as Polish, Czech, Slovak,
|
||||||
|
Hungarian, Slovene, Bosnian, Croatian,
|
||||||
|
Serbian (Latin script), Romanian and
|
||||||
|
Albanian languages
|
||||||
|
|
||||||
* [Quickstart guide](https://github.com/TES3MP/openmw-tes3mp/wiki/Quickstart-guide)
|
win1251 - Cyrillic alphabet such as
|
||||||
* [Steam group](https://steamcommunity.com/groups/mwmulti) and its [detailed FAQ](https://steamcommunity.com/groups/mwmulti/discussions/1/353916184342480541/)
|
Russian, Bulgarian, Serbian Cyrillic
|
||||||
* [TES3MP section on OpenMW forums](https://forum.openmw.org/viewforum.php?f=45)
|
and other languages
|
||||||
* [Discord server](https://discord.gg/ECJk293)
|
|
||||||
* [Subreddit](https://www.reddit.com/r/tes3mp)
|
win1252 - Western European (Latin)
|
||||||
* [Known issues and bug reports](https://github.com/TES3MP/openmw-tes3mp/issues)
|
alphabet, used by default
|
||||||
|
--fallback arg fallback values
|
||||||
|
--no-grab Don't grab mouse cursor
|
||||||
|
--export-fonts [=arg(=1)] (=0) Export Morrowind .fnt fonts to PNG
|
||||||
|
image and XML file in current directory
|
||||||
|
--activate-dist arg (=-1) activation distance override
|
||||||
|
|
|
@ -1,100 +0,0 @@
|
||||||
|
|
||||||
set (CMAKE_CXX_STANDARD 14)
|
|
||||||
|
|
||||||
set(BROWSER_UI
|
|
||||||
${CMAKE_SOURCE_DIR}/files/tes3mp/ui/Main.ui
|
|
||||||
${CMAKE_SOURCE_DIR}/files/tes3mp/ui/ServerInfo.ui
|
|
||||||
)
|
|
||||||
set(BROWSER
|
|
||||||
main.cpp
|
|
||||||
MainWindow.cpp
|
|
||||||
ServerModel.cpp
|
|
||||||
ServerInfoDialog.cpp
|
|
||||||
MySortFilterProxyModel.cpp
|
|
||||||
netutils/HTTPNetwork.cpp
|
|
||||||
netutils/Utils.cpp
|
|
||||||
netutils/QueryClient.cpp
|
|
||||||
PingUpdater.cpp
|
|
||||||
PingHelper.cpp
|
|
||||||
QueryHelper.cpp
|
|
||||||
${CMAKE_SOURCE_DIR}/files/tes3mp/browser.rc
|
|
||||||
)
|
|
||||||
|
|
||||||
set(BROWSER_HEADER_MOC
|
|
||||||
MainWindow.hpp
|
|
||||||
ServerModel.hpp
|
|
||||||
ServerInfoDialog.hpp
|
|
||||||
MySortFilterProxyModel.hpp
|
|
||||||
PingUpdater.hpp
|
|
||||||
PingHelper.hpp
|
|
||||||
QueryHelper.hpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(BROWSER_HEADER
|
|
||||||
${BROWSER_HEADER_MOC}
|
|
||||||
netutils/HTTPNetwork.hpp
|
|
||||||
netutils/Utils.hpp
|
|
||||||
netutils/QueryClient.hpp
|
|
||||||
Types.hpp
|
|
||||||
)
|
|
||||||
|
|
||||||
source_group(browser FILES ${BROWSER} ${BROWSER_HEADER})
|
|
||||||
|
|
||||||
set(QT_USE_QTGUI 1)
|
|
||||||
|
|
||||||
# Set some platform specific settings
|
|
||||||
if(WIN32)
|
|
||||||
set(GUI_TYPE WIN32)
|
|
||||||
set(QT_USE_QTMAIN TRUE)
|
|
||||||
endif(WIN32)
|
|
||||||
|
|
||||||
if (DESIRED_QT_VERSION MATCHES 4)
|
|
||||||
message(SEND_ERROR "QT4 is not supported.")
|
|
||||||
#include(${QT_USE_FILE})
|
|
||||||
#QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)
|
|
||||||
#QT4_WRAP_CPP(MOC_SRCS ${BROWSER_HEADER_MOC})
|
|
||||||
#QT4_WRAP_UI(UI_HDRS ${BROWSER_UI})
|
|
||||||
else()
|
|
||||||
QT5_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)
|
|
||||||
QT5_WRAP_CPP(MOC_SRCS ${BROWSER_HEADER_MOC})
|
|
||||||
QT5_WRAP_UI(UI_HDRS ${BROWSER_UI})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
if(NOT WIN32)
|
|
||||||
include_directories(${LIBUNSHIELD_INCLUDE_DIR})
|
|
||||||
endif(NOT WIN32)
|
|
||||||
|
|
||||||
# Main executable
|
|
||||||
add_executable(tes3mp-browser
|
|
||||||
${GUI_TYPE}
|
|
||||||
${BROWSER}
|
|
||||||
${BROWSER_HEADER}
|
|
||||||
${RCC_SRCS}
|
|
||||||
${MOC_SRCS}
|
|
||||||
${UI_HDRS}
|
|
||||||
)
|
|
||||||
|
|
||||||
if (WIN32)
|
|
||||||
INSTALL(TARGETS tes3mp-browser RUNTIME DESTINATION ".")
|
|
||||||
endif (WIN32)
|
|
||||||
|
|
||||||
target_link_libraries(tes3mp-browser
|
|
||||||
${SDL2_LIBRARY_ONLY}
|
|
||||||
${RakNet_LIBRARY}
|
|
||||||
components
|
|
||||||
)
|
|
||||||
|
|
||||||
if (DESIRED_QT_VERSION MATCHES 4)
|
|
||||||
# target_link_libraries(tes3mp-browser ${QT_QTGUI_LIBRARY} ${QT_QTCORE_LIBRARY})
|
|
||||||
# if(WIN32)
|
|
||||||
# target_link_libraries(tes3mp-browser ${QT_QTMAIN_LIBRARY})
|
|
||||||
# endif(WIN32)
|
|
||||||
else()
|
|
||||||
qt5_use_modules(tes3mp-browser Widgets Core)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_WITH_CODE_COVERAGE)
|
|
||||||
add_definitions (--coverage)
|
|
||||||
target_link_libraries(tes3mp-browser gcov)
|
|
||||||
endif()
|
|
|
@ -1,246 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 06.01.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "MainWindow.hpp"
|
|
||||||
#include "QueryHelper.hpp"
|
|
||||||
#include "PingHelper.hpp"
|
|
||||||
#include "ServerInfoDialog.hpp"
|
|
||||||
#include "components/files/configurationmanager.hpp"
|
|
||||||
#include <qdebug.h>
|
|
||||||
#include <QInputDialog>
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QJsonArray>
|
|
||||||
#include <QFile>
|
|
||||||
#include <QJsonDocument>
|
|
||||||
|
|
||||||
|
|
||||||
using namespace Process;
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
MainWindow::MainWindow(QWidget *parent)
|
|
||||||
{
|
|
||||||
setupUi(this);
|
|
||||||
|
|
||||||
mGameInvoker = new ProcessInvoker();
|
|
||||||
|
|
||||||
browser = new ServerModel;
|
|
||||||
favorites = new ServerModel;
|
|
||||||
proxyModel = new MySortFilterProxyModel(this);
|
|
||||||
proxyModel->setSourceModel(browser);
|
|
||||||
tblServerBrowser->setModel(proxyModel);
|
|
||||||
tblFavorites->setModel(proxyModel);
|
|
||||||
|
|
||||||
// Remove Favorites tab while it remains broken
|
|
||||||
tabWidget->removeTab(1);
|
|
||||||
|
|
||||||
tblServerBrowser->hideColumn(ServerData::ADDR);
|
|
||||||
tblFavorites->hideColumn(ServerData::ADDR);
|
|
||||||
|
|
||||||
PingHelper::Get().SetModel((ServerModel*)proxyModel->sourceModel());
|
|
||||||
queryHelper = new QueryHelper(proxyModel->sourceModel());
|
|
||||||
connect(queryHelper, &QueryHelper::started, [this](){actionRefresh->setEnabled(false);});
|
|
||||||
connect(queryHelper, &QueryHelper::finished, [this](){actionRefresh->setEnabled(true);});
|
|
||||||
|
|
||||||
connect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabSwitched(int)));
|
|
||||||
connect(actionAdd, SIGNAL(triggered(bool)), this, SLOT(addServer()));
|
|
||||||
connect(actionAdd_by_IP, SIGNAL(triggered(bool)), this, SLOT(addServerByIP()));
|
|
||||||
connect(actionDelete, SIGNAL(triggered(bool)), this, SLOT(deleteServer()));
|
|
||||||
connect(actionRefresh, SIGNAL(triggered(bool)), queryHelper, SLOT(refresh()));
|
|
||||||
connect(actionPlay, SIGNAL(triggered(bool)), this, SLOT(play()));
|
|
||||||
connect(tblServerBrowser, SIGNAL(clicked(QModelIndex)), this, SLOT(serverSelected()));
|
|
||||||
connect(tblFavorites, SIGNAL(clicked(QModelIndex)), this, SLOT(serverSelected()));
|
|
||||||
connect(tblFavorites, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(play()));
|
|
||||||
connect(tblServerBrowser, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(play()));
|
|
||||||
connect(cBoxNotFull, SIGNAL(toggled(bool)), this, SLOT(notFullSwitch(bool)));
|
|
||||||
connect(cBoxWithPlayers, SIGNAL(toggled(bool)), this, SLOT(havePlayersSwitch(bool)));
|
|
||||||
connect(cBBoxWOPass, SIGNAL(toggled(bool)), this, SLOT(noPasswordSwitch(bool)));
|
|
||||||
connect(comboLatency, SIGNAL(currentIndexChanged(int)), this, SLOT(maxLatencyChanged(int)));
|
|
||||||
connect(leGamemode, SIGNAL(textChanged(const QString &)), this, SLOT(gamemodeChanged(const QString &)));
|
|
||||||
loadFavorites();
|
|
||||||
queryHelper->refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
MainWindow::~MainWindow()
|
|
||||||
{
|
|
||||||
delete mGameInvoker;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::addServerAndUpdate(const QString &addr)
|
|
||||||
{
|
|
||||||
favorites->insertRow(0);
|
|
||||||
QModelIndex mi = favorites->index(0, ServerData::ADDR);
|
|
||||||
favorites->setData(mi, addr, Qt::EditRole);
|
|
||||||
//NetController::get()->updateInfo(favorites, mi);
|
|
||||||
//QueryClient::Update(RakNet::SystemAddress())
|
|
||||||
/*auto data = QueryClient::Get().Query();
|
|
||||||
if (data.empty())
|
|
||||||
return;
|
|
||||||
transform(data.begin(), data.end(), back_inserter());*/
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::addServer()
|
|
||||||
{
|
|
||||||
int id = tblServerBrowser->selectionModel()->currentIndex().row();
|
|
||||||
|
|
||||||
if (id >= 0)
|
|
||||||
{
|
|
||||||
int sourceId = proxyModel->mapToSource(proxyModel->index(id, ServerData::ADDR)).row();
|
|
||||||
favorites->myData.push_back(browser->myData[sourceId]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::addServerByIP()
|
|
||||||
{
|
|
||||||
bool ok;
|
|
||||||
QString text = QInputDialog::getText(this, tr("Add Server by address"), tr("Address:"), QLineEdit::Normal, "", &ok);
|
|
||||||
if (ok && !text.isEmpty())
|
|
||||||
addServerAndUpdate(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::deleteServer()
|
|
||||||
{
|
|
||||||
if (tabWidget->currentIndex() != 1)
|
|
||||||
return;
|
|
||||||
int id = tblFavorites->selectionModel()->currentIndex().row();
|
|
||||||
if (id >= 0)
|
|
||||||
{
|
|
||||||
int sourceId = proxyModel->mapToSource(proxyModel->index(id, ServerData::ADDR)).row();
|
|
||||||
favorites->removeRow(sourceId);
|
|
||||||
if (favorites->myData.isEmpty())
|
|
||||||
{
|
|
||||||
actionPlay->setEnabled(false);
|
|
||||||
actionDelete->setEnabled(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::play()
|
|
||||||
{
|
|
||||||
QTableView *curTable = tabWidget->currentIndex() ? tblFavorites : tblServerBrowser;
|
|
||||||
int id = curTable->selectionModel()->currentIndex().row();
|
|
||||||
if (id < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
|
|
||||||
ServerModel *sm = ((ServerModel*)proxyModel->sourceModel());
|
|
||||||
|
|
||||||
int sourceId = proxyModel->mapToSource(proxyModel->index(id, ServerData::ADDR)).row();
|
|
||||||
ServerInfoDialog infoDialog(sm->myData[sourceId].addr, this);
|
|
||||||
|
|
||||||
if (!infoDialog.exec())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!infoDialog.isUpdated())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QStringList arguments;
|
|
||||||
arguments.append(QLatin1String("--connect=") + sm->myData[sourceId].addr.toLatin1());
|
|
||||||
|
|
||||||
if (sm->myData[sourceId].GetPassword() == 1)
|
|
||||||
{
|
|
||||||
bool ok;
|
|
||||||
QString passw = QInputDialog::getText(this, tr("Connecting to: ") + sm->myData[sourceId].addr, tr("Password: "),
|
|
||||||
QLineEdit::Password, "", &ok);
|
|
||||||
if (!ok)
|
|
||||||
return;
|
|
||||||
arguments.append(QLatin1String("--password=") + passw.toLatin1());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mGameInvoker->startProcess(QLatin1String("tes3mp"), arguments, true))
|
|
||||||
return qApp->quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::tabSwitched(int index)
|
|
||||||
{
|
|
||||||
if (index == 0)
|
|
||||||
{
|
|
||||||
proxyModel->setSourceModel(browser);
|
|
||||||
actionDelete->setEnabled(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
proxyModel->setSourceModel(favorites);
|
|
||||||
}
|
|
||||||
actionPlay->setEnabled(false);
|
|
||||||
actionAdd->setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::serverSelected()
|
|
||||||
{
|
|
||||||
actionPlay->setEnabled(true);
|
|
||||||
if (tabWidget->currentIndex() == 0)
|
|
||||||
actionAdd->setEnabled(true);
|
|
||||||
if (tabWidget->currentIndex() == 1)
|
|
||||||
actionDelete->setEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::closeEvent(QCloseEvent *event)
|
|
||||||
{
|
|
||||||
Files::ConfigurationManager cfgMgr;
|
|
||||||
QString cfgPath = QString::fromStdString((cfgMgr.getUserConfigPath() / "favorites.dat").string());
|
|
||||||
|
|
||||||
QJsonArray saveData;
|
|
||||||
for (auto server : favorites->myData)
|
|
||||||
saveData.push_back(server.addr);
|
|
||||||
|
|
||||||
QFile file(cfgPath);
|
|
||||||
|
|
||||||
if (!file.open(QIODevice::WriteOnly))
|
|
||||||
{
|
|
||||||
qDebug() << "Cannot save " << cfgPath;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
file.write(QJsonDocument(saveData).toJson());
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void MainWindow::loadFavorites()
|
|
||||||
{
|
|
||||||
Files::ConfigurationManager cfgMgr;
|
|
||||||
QString cfgPath = QString::fromStdString((cfgMgr.getUserConfigPath() / "favorites.dat").string());
|
|
||||||
|
|
||||||
QFile file(cfgPath);
|
|
||||||
if (!file.open(QIODevice::ReadOnly))
|
|
||||||
{
|
|
||||||
qDebug() << "Cannot open " << cfgPath;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonDocument jsonDoc(QJsonDocument::fromJson(file.readAll()));
|
|
||||||
|
|
||||||
for (auto server : jsonDoc.array())
|
|
||||||
addServerAndUpdate(server.toString());
|
|
||||||
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::notFullSwitch(bool state)
|
|
||||||
{
|
|
||||||
proxyModel->filterFullServer(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::havePlayersSwitch(bool state)
|
|
||||||
{
|
|
||||||
proxyModel->filterEmptyServers(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::noPasswordSwitch(bool state)
|
|
||||||
{
|
|
||||||
proxyModel->filterPassworded(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::maxLatencyChanged(int index)
|
|
||||||
{
|
|
||||||
int maxLatency = index * 50;
|
|
||||||
proxyModel->pingLessThan(maxLatency);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::gamemodeChanged(const QString &text)
|
|
||||||
{
|
|
||||||
proxyModel->setFilterFixedString(text);
|
|
||||||
proxyModel->setFilterKeyColumn(ServerData::MODNAME);
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 06.01.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef NEWLAUNCHER_MAIN_HPP
|
|
||||||
#define NEWLAUNCHER_MAIN_HPP
|
|
||||||
|
|
||||||
|
|
||||||
#include "ui_Main.h"
|
|
||||||
#include "ServerModel.hpp"
|
|
||||||
#include "MySortFilterProxyModel.hpp"
|
|
||||||
#include <components/process/processinvoker.hpp>
|
|
||||||
|
|
||||||
class QueryHelper;
|
|
||||||
|
|
||||||
class MainWindow : public QMainWindow, private Ui::MainWindow
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit MainWindow(QWidget *parent = nullptr);
|
|
||||||
~MainWindow() override;
|
|
||||||
protected:
|
|
||||||
void closeEvent(QCloseEvent * event) Q_DECL_OVERRIDE;
|
|
||||||
void addServerAndUpdate(const QString &addr);
|
|
||||||
protected slots:
|
|
||||||
void tabSwitched(int index);
|
|
||||||
void addServer();
|
|
||||||
void addServerByIP();
|
|
||||||
void deleteServer();
|
|
||||||
void play();
|
|
||||||
void serverSelected();
|
|
||||||
void notFullSwitch(bool state);
|
|
||||||
void havePlayersSwitch(bool state);
|
|
||||||
void noPasswordSwitch(bool state);
|
|
||||||
void maxLatencyChanged(int index);
|
|
||||||
void gamemodeChanged(const QString &text);
|
|
||||||
private:
|
|
||||||
QueryHelper *queryHelper;
|
|
||||||
Process::ProcessInvoker *mGameInvoker;
|
|
||||||
ServerModel *browser, *favorites;
|
|
||||||
MySortFilterProxyModel *proxyModel;
|
|
||||||
void loadFavorites();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //NEWLAUNCHER_MAIN_HPP
|
|
|
@ -1,84 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 30.01.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "MySortFilterProxyModel.hpp"
|
|
||||||
#include "ServerModel.hpp"
|
|
||||||
|
|
||||||
#include <qdebug.h>
|
|
||||||
#include <apps/browser/netutils/Utils.hpp>
|
|
||||||
|
|
||||||
bool MySortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
|
||||||
{
|
|
||||||
|
|
||||||
QModelIndex pingIndex = sourceModel()->index(sourceRow, ServerData::PING, sourceParent);
|
|
||||||
QModelIndex plIndex = sourceModel()->index(sourceRow, ServerData::PLAYERS, sourceParent);
|
|
||||||
QModelIndex maxPlIndex = sourceModel()->index(sourceRow, ServerData::MAX_PLAYERS, sourceParent);
|
|
||||||
QModelIndex passwordIndex = sourceModel()->index(sourceRow, ServerData::PASSW, sourceParent);
|
|
||||||
|
|
||||||
bool pingOk;
|
|
||||||
int ping = sourceModel()->data(pingIndex).toInt(&pingOk);
|
|
||||||
int players = sourceModel()->data(plIndex).toInt();
|
|
||||||
int maxPlayers = sourceModel()->data(maxPlIndex).toInt();
|
|
||||||
|
|
||||||
if (maxPing > 0 && (ping == -1 || ping > maxPing || !pingOk))
|
|
||||||
return false;
|
|
||||||
if (filterEmpty && players == 0)
|
|
||||||
return false;
|
|
||||||
if (filterFull && players >= maxPlayers)
|
|
||||||
return false;
|
|
||||||
if(filterPasswEnabled && sourceModel()->data(passwordIndex).toString() == "Yes")
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MySortFilterProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
|
|
||||||
{
|
|
||||||
if(sortColumn() == ServerData::PING)
|
|
||||||
{
|
|
||||||
bool valid;
|
|
||||||
|
|
||||||
int pingright = sourceModel()->data(source_right).toInt(&valid);
|
|
||||||
pingright = valid ? pingright : PING_UNREACHABLE;
|
|
||||||
|
|
||||||
int pingleft = sourceModel()->data(source_left).toInt(&valid);
|
|
||||||
pingleft = valid ? pingleft : PING_UNREACHABLE;
|
|
||||||
return pingleft < pingright;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return QSortFilterProxyModel::lessThan(source_left, source_right);
|
|
||||||
}
|
|
||||||
|
|
||||||
MySortFilterProxyModel::MySortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent)
|
|
||||||
{
|
|
||||||
filterEmpty = false;
|
|
||||||
filterFull = false;
|
|
||||||
filterPasswEnabled = false;
|
|
||||||
maxPing = 0;
|
|
||||||
setSortCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MySortFilterProxyModel::filterEmptyServers(bool state)
|
|
||||||
{
|
|
||||||
filterEmpty = state;
|
|
||||||
invalidateFilter();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MySortFilterProxyModel::filterFullServer(bool state)
|
|
||||||
{
|
|
||||||
filterFull = state;
|
|
||||||
invalidateFilter();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MySortFilterProxyModel::pingLessThan(int maxPing)
|
|
||||||
{
|
|
||||||
this->maxPing = maxPing;
|
|
||||||
invalidateFilter();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MySortFilterProxyModel::filterPassworded(bool state)
|
|
||||||
{
|
|
||||||
filterPasswEnabled = state;
|
|
||||||
invalidateFilter();
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 30.01.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef OPENMW_MYSORTFILTERPROXYMODEL_HPP
|
|
||||||
#define OPENMW_MYSORTFILTERPROXYMODEL_HPP
|
|
||||||
|
|
||||||
|
|
||||||
#include <QSortFilterProxyModel>
|
|
||||||
|
|
||||||
class MySortFilterProxyModel : public QSortFilterProxyModel
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
protected:
|
|
||||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const Q_DECL_FINAL;
|
|
||||||
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const Q_DECL_FINAL;
|
|
||||||
public:
|
|
||||||
explicit MySortFilterProxyModel(QObject *parent);
|
|
||||||
void filterFullServer(bool state);
|
|
||||||
void filterEmptyServers(bool state);
|
|
||||||
void filterPassworded(bool state);
|
|
||||||
void pingLessThan(int maxPing);
|
|
||||||
private:
|
|
||||||
bool filterEmpty, filterFull, filterPasswEnabled;
|
|
||||||
int maxPing;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //OPENMW_MYSORTFILTERPROXYMODEL_HPP
|
|
|
@ -1,56 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 03.05.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "PingHelper.hpp"
|
|
||||||
#include "ServerModel.hpp"
|
|
||||||
#include <QDebug>
|
|
||||||
#include "PingUpdater.hpp"
|
|
||||||
|
|
||||||
void PingHelper::Add(int row, const AddrPair &addrPair)
|
|
||||||
{
|
|
||||||
pingUpdater->addServer(row, addrPair);
|
|
||||||
if (!pingThread->isRunning())
|
|
||||||
pingThread->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PingHelper::Reset()
|
|
||||||
{
|
|
||||||
//if (pingThread->isRunning())
|
|
||||||
Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PingHelper::Stop()
|
|
||||||
{
|
|
||||||
emit pingUpdater->stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PingHelper::SetModel(QAbstractTableModel *model)
|
|
||||||
{
|
|
||||||
this->model = model;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PingHelper::update(int row, unsigned ping)
|
|
||||||
{
|
|
||||||
model->setData(model->index(row, ServerData::PING), ping);
|
|
||||||
}
|
|
||||||
|
|
||||||
PingHelper &PingHelper::Get()
|
|
||||||
{
|
|
||||||
static PingHelper helper;
|
|
||||||
return helper;
|
|
||||||
}
|
|
||||||
|
|
||||||
PingHelper::PingHelper() : QObject()
|
|
||||||
{
|
|
||||||
pingThread = new QThread;
|
|
||||||
pingUpdater = new PingUpdater;
|
|
||||||
pingUpdater->moveToThread(pingThread);
|
|
||||||
|
|
||||||
connect(pingThread, SIGNAL(started()), pingUpdater, SLOT(process()));
|
|
||||||
connect(pingUpdater, SIGNAL(start()), pingThread, SLOT(start()));
|
|
||||||
connect(pingUpdater, SIGNAL(finished()), pingThread, SLOT(quit()));
|
|
||||||
connect(this, SIGNAL(stop()), pingUpdater, SLOT(stop()));
|
|
||||||
//connect(pingUpdater, SIGNAL(finished()), pingUpdater, SLOT(deleteLater()));
|
|
||||||
connect(pingUpdater, SIGNAL(updateModel(int, unsigned)), this, SLOT(update(int, unsigned)));
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 03.05.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef OPENMW_PINGHELPER_HPP
|
|
||||||
#define OPENMW_PINGHELPER_HPP
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QAbstractTableModel>
|
|
||||||
#include <QThread>
|
|
||||||
#include "Types.hpp"
|
|
||||||
|
|
||||||
class PingUpdater;
|
|
||||||
|
|
||||||
class PingHelper : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
|
|
||||||
void Reset();
|
|
||||||
void Add(int row, const AddrPair &addrPair);
|
|
||||||
void Stop();
|
|
||||||
void SetModel(QAbstractTableModel *model);
|
|
||||||
//void UpdateImmedialy(PingUpdater::AddrPair addrPair);
|
|
||||||
static PingHelper &Get();
|
|
||||||
|
|
||||||
PingHelper(const PingHelper&) = delete;
|
|
||||||
PingHelper& operator=(const PingHelper&) = delete;
|
|
||||||
private:
|
|
||||||
PingHelper();
|
|
||||||
signals:
|
|
||||||
void stop();
|
|
||||||
public slots:
|
|
||||||
void update(int row, unsigned ping);
|
|
||||||
private:
|
|
||||||
QThread *pingThread;
|
|
||||||
PingUpdater *pingUpdater;
|
|
||||||
QAbstractTableModel *model;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //OPENMW_PINGHELPER_HPP
|
|
|
@ -1,50 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 02.05.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "PingUpdater.hpp"
|
|
||||||
#include "netutils/Utils.hpp"
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QModelIndex>
|
|
||||||
#include <QThread>
|
|
||||||
|
|
||||||
void PingUpdater::stop()
|
|
||||||
{
|
|
||||||
servers.clear();
|
|
||||||
run = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PingUpdater::addServer(int row, const AddrPair &addr)
|
|
||||||
{
|
|
||||||
servers.push_back({row, addr});
|
|
||||||
run = true;
|
|
||||||
emit start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PingUpdater::process()
|
|
||||||
{
|
|
||||||
while (run)
|
|
||||||
{
|
|
||||||
if (servers.count() == 0)
|
|
||||||
{
|
|
||||||
QThread::msleep(1000);
|
|
||||||
if (servers.count() == 0)
|
|
||||||
{
|
|
||||||
qDebug() << "PingUpdater stopped due to inactivity";
|
|
||||||
run = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ServerRow server = servers.back();
|
|
||||||
servers.pop_back();
|
|
||||||
|
|
||||||
unsigned ping = PingRakNetServer(server.second.first.toLatin1(), server.second.second);
|
|
||||||
|
|
||||||
qDebug() << "Pong from" << server.second.first + "|" + QString::number(server.second.second)
|
|
||||||
<< ":" << ping << "ms" << "Sizeof servers: " << servers.size();
|
|
||||||
|
|
||||||
emit updateModel(server.first, ping);
|
|
||||||
}
|
|
||||||
emit finished();
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 02.05.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef OPENMW_PINGUPDATER_HPP
|
|
||||||
#define OPENMW_PINGUPDATER_HPP
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QVector>
|
|
||||||
|
|
||||||
#include "Types.hpp"
|
|
||||||
|
|
||||||
class PingUpdater : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
void addServer(int row, const AddrPair &addrPair);
|
|
||||||
public slots:
|
|
||||||
void stop();
|
|
||||||
void process();
|
|
||||||
signals:
|
|
||||||
void start();
|
|
||||||
void updateModel(int row, unsigned ping);
|
|
||||||
void finished();
|
|
||||||
private:
|
|
||||||
QVector<ServerRow> servers;
|
|
||||||
bool run;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //OPENMW_PINGUPDATER_HPP
|
|
|
@ -1,87 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 27.05.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "netutils/QueryClient.hpp"
|
|
||||||
#include "netutils/Utils.hpp"
|
|
||||||
#include "QueryHelper.hpp"
|
|
||||||
#include "PingHelper.hpp"
|
|
||||||
|
|
||||||
QueryUpdate *queryUpdate;
|
|
||||||
|
|
||||||
|
|
||||||
QueryHelper::QueryHelper(QAbstractItemModel *model)
|
|
||||||
{
|
|
||||||
qRegisterMetaType<QueryData>("QueryData");
|
|
||||||
queryThread = new QThread;
|
|
||||||
queryUpdate = new QueryUpdate;
|
|
||||||
_model = model;
|
|
||||||
connect(queryThread, SIGNAL(started()), queryUpdate, SLOT(process()));
|
|
||||||
connect(queryUpdate, SIGNAL(finished()), queryThread, SLOT(quit()));
|
|
||||||
connect(queryUpdate, &QueryUpdate::finished, [this](){emit finished();});
|
|
||||||
connect(queryUpdate, SIGNAL(updateModel(const QString&, unsigned short, const QueryData&)),
|
|
||||||
this, SLOT(update(const QString&, unsigned short, const QueryData&)));
|
|
||||||
queryUpdate->moveToThread(queryThread);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QueryHelper::refresh()
|
|
||||||
{
|
|
||||||
if (!queryThread->isRunning())
|
|
||||||
{
|
|
||||||
_model->removeRows(0, _model->rowCount());
|
|
||||||
PingHelper::Get().Stop();
|
|
||||||
queryThread->start();
|
|
||||||
emit started();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QueryHelper::terminate()
|
|
||||||
{
|
|
||||||
queryThread->terminate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QueryHelper::update(const QString &addr, unsigned short port, const QueryData& data)
|
|
||||||
{
|
|
||||||
ServerModel *model = ((ServerModel*)_model);
|
|
||||||
model->insertRow(model->rowCount());
|
|
||||||
int row = model->rowCount() - 1;
|
|
||||||
|
|
||||||
QModelIndex mi = model->index(row, ServerData::ADDR);
|
|
||||||
model->setData(mi, addr + ":" + QString::number(port));
|
|
||||||
|
|
||||||
mi = model->index(row, ServerData::PLAYERS);
|
|
||||||
model->setData(mi, (int)data.players.size());
|
|
||||||
|
|
||||||
mi = model->index(row, ServerData::MAX_PLAYERS);
|
|
||||||
model->setData(mi, data.GetMaxPlayers());
|
|
||||||
|
|
||||||
mi = model->index(row, ServerData::HOSTNAME);
|
|
||||||
model->setData(mi, data.GetName());
|
|
||||||
|
|
||||||
mi = model->index(row, ServerData::MODNAME);
|
|
||||||
model->setData(mi, data.GetGameMode());
|
|
||||||
|
|
||||||
mi = model->index(row, ServerData::VERSION);
|
|
||||||
model->setData(mi, data.GetVersion());
|
|
||||||
|
|
||||||
mi = model->index(row, ServerData::PASSW);
|
|
||||||
model->setData(mi, data.GetPassword() == 1);
|
|
||||||
|
|
||||||
mi = model->index(row, ServerData::PING);
|
|
||||||
model->setData(mi, PING_UNREACHABLE);
|
|
||||||
PingHelper::Get().Add(row, {addr, port});
|
|
||||||
}
|
|
||||||
|
|
||||||
void QueryUpdate::process()
|
|
||||||
{
|
|
||||||
auto data = QueryClient::Get().Query();
|
|
||||||
if (QueryClient::Get().Status() != ID_MASTER_QUERY)
|
|
||||||
{
|
|
||||||
emit finished();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto &server : data)
|
|
||||||
emit updateModel(server.first.ToString(false), server.first.GetPort(), server.second);
|
|
||||||
emit finished();
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 27.05.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef OPENMW_QUERYHELPER_HPP
|
|
||||||
#define OPENMW_QUERYHELPER_HPP
|
|
||||||
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <vector>
|
|
||||||
#include <QAbstractItemModel>
|
|
||||||
#include <components/openmw-mp/Master/MasterData.hpp>
|
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(QueryData)
|
|
||||||
|
|
||||||
class QueryHelper : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit QueryHelper(QAbstractItemModel *model);
|
|
||||||
public slots:
|
|
||||||
void refresh();
|
|
||||||
void terminate();
|
|
||||||
private slots:
|
|
||||||
void update(const QString &addr, unsigned short port, const QueryData& data);
|
|
||||||
signals:
|
|
||||||
void finished();
|
|
||||||
void started();
|
|
||||||
private:
|
|
||||||
QThread *queryThread;
|
|
||||||
QAbstractItemModel *_model;
|
|
||||||
};
|
|
||||||
|
|
||||||
class QueryUpdate : public QObject
|
|
||||||
{
|
|
||||||
friend class QueryHelper;
|
|
||||||
Q_OBJECT
|
|
||||||
signals:
|
|
||||||
void finished();
|
|
||||||
void updateModel(const QString &addr, unsigned short port, const QueryData& data);
|
|
||||||
public slots:
|
|
||||||
void process();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //OPENMW_QUERYHELPER_HPP
|
|
|
@ -1,107 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 07.01.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <apps/browser/netutils/QueryClient.hpp>
|
|
||||||
#include "qdebug.h"
|
|
||||||
|
|
||||||
#include "ServerInfoDialog.hpp"
|
|
||||||
#include <algorithm>
|
|
||||||
#include <utility>
|
|
||||||
#include <QThread>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
using namespace RakNet;
|
|
||||||
|
|
||||||
ThrWorker::ThrWorker(ServerInfoDialog *dialog, QString addr, unsigned short port): addr(std::move(addr)), port(port), stopped(false)
|
|
||||||
{
|
|
||||||
this->dialog = dialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ThrWorker::process()
|
|
||||||
{
|
|
||||||
stopped = false;
|
|
||||||
auto newSD = QueryClient::Get().Update(SystemAddress(addr.toUtf8(), port));
|
|
||||||
if (dialog != nullptr)
|
|
||||||
dialog->setData(newSD);
|
|
||||||
stopped = true;
|
|
||||||
emit finished();
|
|
||||||
}
|
|
||||||
|
|
||||||
ServerInfoDialog::ServerInfoDialog(const QString &addr, QWidget *parent): QDialog(parent)
|
|
||||||
{
|
|
||||||
setupUi(this);
|
|
||||||
refreshThread = new QThread;
|
|
||||||
|
|
||||||
QStringList list = addr.split(':');
|
|
||||||
worker = new ThrWorker(this, list[0].toLatin1(), list[1].toUShort());
|
|
||||||
worker->moveToThread(refreshThread);
|
|
||||||
connect(refreshThread, SIGNAL(started()), worker, SLOT(process()));
|
|
||||||
connect(worker, SIGNAL(finished()), refreshThread, SLOT(quit()));
|
|
||||||
connect(refreshThread, SIGNAL(finished()), this, SLOT(refresh()));
|
|
||||||
|
|
||||||
connect(btnRefresh, &QPushButton::clicked, [this]{
|
|
||||||
if (!refreshThread->isRunning())
|
|
||||||
refreshThread->start();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ServerInfoDialog::~ServerInfoDialog()
|
|
||||||
{
|
|
||||||
worker->dialog = nullptr;
|
|
||||||
if (!refreshThread->isRunning())
|
|
||||||
refreshThread->terminate();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ServerInfoDialog::isUpdated()
|
|
||||||
{
|
|
||||||
return sd.first != UNASSIGNED_SYSTEM_ADDRESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ServerInfoDialog::setData(std::pair<RakNet::SystemAddress, QueryData> &newSD)
|
|
||||||
{
|
|
||||||
sd = newSD;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ServerInfoDialog::refresh()
|
|
||||||
{
|
|
||||||
if (sd.first != UNASSIGNED_SYSTEM_ADDRESS)
|
|
||||||
{
|
|
||||||
leAddr->setText(sd.first.ToString(true, ':'));
|
|
||||||
lblName->setText(sd.second.GetName());
|
|
||||||
int ping = PingRakNetServer(sd.first.ToString(false), sd.first.GetPort());
|
|
||||||
lblPing->setNum(ping);
|
|
||||||
btnConnect->setDisabled(ping == PING_UNREACHABLE);
|
|
||||||
|
|
||||||
listPlayers->clear();
|
|
||||||
for (const auto &player : sd.second.players)
|
|
||||||
listPlayers->addItem(QString::fromStdString(player));
|
|
||||||
|
|
||||||
listPlugins->clear();
|
|
||||||
for (const auto &plugin : sd.second.plugins)
|
|
||||||
listPlugins->addItem(QString::fromStdString(plugin.name));
|
|
||||||
|
|
||||||
listRules->clear();
|
|
||||||
const static vector<std::string> defaultRules {"gamemode", "maxPlayers", "name", "passw", "players", "version"};
|
|
||||||
for (auto &rule : sd.second.rules)
|
|
||||||
{
|
|
||||||
if (::find(defaultRules.begin(), defaultRules.end(), rule.first) != defaultRules.end())
|
|
||||||
continue;
|
|
||||||
QString ruleStr = QString::fromStdString(rule.first) + " : ";
|
|
||||||
if (rule.second.type == 's')
|
|
||||||
ruleStr += QString::fromStdString(rule.second.str);
|
|
||||||
else
|
|
||||||
ruleStr += QString::number(rule.second.val);
|
|
||||||
listRules->addItem(ruleStr);
|
|
||||||
}
|
|
||||||
|
|
||||||
lblPlayers->setText(QString::number(sd.second.players.size()) + " / " + QString::number(sd.second.GetMaxPlayers()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int ServerInfoDialog::exec()
|
|
||||||
{
|
|
||||||
if (!refreshThread->isRunning())
|
|
||||||
refreshThread->start();
|
|
||||||
return QDialog::exec();
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 07.01.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef NEWLAUNCHER_SERVERINFODIALOG_HPP
|
|
||||||
#define NEWLAUNCHER_SERVERINFODIALOG_HPP
|
|
||||||
|
|
||||||
#include "ui_ServerInfo.h"
|
|
||||||
#include <apps/browser/netutils/Utils.hpp>
|
|
||||||
#include <RakNetTypes.h>
|
|
||||||
#include <components/openmw-mp/Master/MasterData.hpp>
|
|
||||||
|
|
||||||
class ThrWorker;
|
|
||||||
|
|
||||||
class ServerInfoDialog : public QDialog, public Ui::Dialog
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit ServerInfoDialog(const QString &addr, QWidget *parent = nullptr);
|
|
||||||
~ServerInfoDialog() override;
|
|
||||||
bool isUpdated();
|
|
||||||
void setData(std::pair<RakNet::SystemAddress, QueryData> &newSD);
|
|
||||||
public slots:
|
|
||||||
void refresh();
|
|
||||||
int exec() Q_DECL_OVERRIDE;
|
|
||||||
private:
|
|
||||||
QThread *refreshThread;
|
|
||||||
ThrWorker* worker;
|
|
||||||
std::pair<RakNet::SystemAddress, QueryData> sd;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ThrWorker: public QObject
|
|
||||||
{
|
|
||||||
friend class ServerInfoDialog;
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
ThrWorker(ServerInfoDialog *dialog, QString addr, unsigned short port);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void process();
|
|
||||||
signals:
|
|
||||||
void finished();
|
|
||||||
private:
|
|
||||||
QString addr;
|
|
||||||
unsigned short port;
|
|
||||||
bool stopped;
|
|
||||||
ServerInfoDialog *dialog;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //NEWLAUNCHER_SERVERINFODIALOG_HPP
|
|
|
@ -1,191 +0,0 @@
|
||||||
#include <qmessagebox.h>
|
|
||||||
#include "ServerModel.hpp"
|
|
||||||
#include <qdebug.h>
|
|
||||||
#include <apps/browser/netutils/Utils.hpp>
|
|
||||||
|
|
||||||
ServerModel::ServerModel(QObject *parent) : QAbstractTableModel(parent)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
/*QHash<int, QByteArray> ServerModel::roleNames() const
|
|
||||||
{
|
|
||||||
return roles;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
QVariant ServerModel::data(const QModelIndex &index, int role) const
|
|
||||||
{
|
|
||||||
if (!index.isValid())
|
|
||||||
return QVariant();
|
|
||||||
if (index.row() < 0 || index.row() > myData.size())
|
|
||||||
return QVariant();
|
|
||||||
|
|
||||||
const ServerData &sd = myData.at(index.row());
|
|
||||||
|
|
||||||
if (role == Qt::DisplayRole)
|
|
||||||
{
|
|
||||||
QVariant var;
|
|
||||||
switch (index.column())
|
|
||||||
{
|
|
||||||
case ServerData::ADDR:
|
|
||||||
var = sd.addr;
|
|
||||||
break;
|
|
||||||
case ServerData::PASSW:
|
|
||||||
var = (int)(sd.rules.at("passw").val) == 1 ? "Yes" : "No";
|
|
||||||
break;
|
|
||||||
case ServerData::VERSION:
|
|
||||||
var = QString(sd.rules.at("version").str.c_str());
|
|
||||||
break;
|
|
||||||
case ServerData::PLAYERS:
|
|
||||||
var = (int) sd.rules.at("players").val;
|
|
||||||
break;
|
|
||||||
case ServerData::MAX_PLAYERS:
|
|
||||||
var = (int) sd.rules.at("maxPlayers").val;
|
|
||||||
break;
|
|
||||||
case ServerData::HOSTNAME:
|
|
||||||
var = QString(sd.rules.at("name").str.c_str());
|
|
||||||
break;
|
|
||||||
case ServerData::PING:
|
|
||||||
var = sd.ping == PING_UNREACHABLE ? QVariant("Unreachable") : sd.ping;
|
|
||||||
break;
|
|
||||||
case ServerData::MODNAME:
|
|
||||||
if (sd.rules.at("gamemode").str == "")
|
|
||||||
var = "default";
|
|
||||||
else
|
|
||||||
var = QString(sd.rules.at("gamemode").str.c_str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return var;
|
|
||||||
}
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant ServerModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
||||||
{
|
|
||||||
QVariant var;
|
|
||||||
if (orientation == Qt::Horizontal)
|
|
||||||
{
|
|
||||||
if (role == Qt::SizeHintRole)
|
|
||||||
{
|
|
||||||
/*if (section == ServerData::HOSTNAME)
|
|
||||||
var = QSize(200, 25);*/
|
|
||||||
}
|
|
||||||
else if (role == Qt::DisplayRole)
|
|
||||||
{
|
|
||||||
|
|
||||||
switch (section)
|
|
||||||
{
|
|
||||||
case ServerData::ADDR:
|
|
||||||
var = "Address";
|
|
||||||
break;
|
|
||||||
case ServerData::PASSW:
|
|
||||||
var = "Password";
|
|
||||||
break;
|
|
||||||
case ServerData::VERSION:
|
|
||||||
var = "Version";
|
|
||||||
break;
|
|
||||||
case ServerData::HOSTNAME:
|
|
||||||
var = "Host name";
|
|
||||||
break;
|
|
||||||
case ServerData::PLAYERS:
|
|
||||||
var = "Players";
|
|
||||||
break;
|
|
||||||
case ServerData::MAX_PLAYERS:
|
|
||||||
var = "Max players";
|
|
||||||
break;
|
|
||||||
case ServerData::PING:
|
|
||||||
var = "Ping";
|
|
||||||
break;
|
|
||||||
case ServerData::MODNAME:
|
|
||||||
var = "Game mode";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return var;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ServerModel::rowCount(const QModelIndex &parent) const
|
|
||||||
{
|
|
||||||
return myData.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
int ServerModel::columnCount(const QModelIndex &parent) const
|
|
||||||
{
|
|
||||||
return ServerData::LAST;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ServerModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
|
||||||
{
|
|
||||||
if (index.isValid() && role == Qt::EditRole)
|
|
||||||
{
|
|
||||||
int row = index.row();
|
|
||||||
int col = index.column();
|
|
||||||
|
|
||||||
ServerData &sd = myData[row];
|
|
||||||
bool ok = true;
|
|
||||||
switch(col)
|
|
||||||
{
|
|
||||||
case ServerData::ADDR:
|
|
||||||
sd.addr = value.toString();
|
|
||||||
ok = !sd.addr.isEmpty();
|
|
||||||
break;
|
|
||||||
case ServerData::PASSW:
|
|
||||||
sd.SetPassword(value.toBool());
|
|
||||||
break;
|
|
||||||
case ServerData::VERSION:
|
|
||||||
sd.SetVersion(value.toString().toUtf8());
|
|
||||||
ok = !sd.addr.isEmpty();
|
|
||||||
break;
|
|
||||||
case ServerData::PLAYERS:
|
|
||||||
sd.SetPlayers(value.toInt(&ok));
|
|
||||||
break;
|
|
||||||
case ServerData::MAX_PLAYERS:
|
|
||||||
sd.SetMaxPlayers(value.toInt(&ok));
|
|
||||||
break;
|
|
||||||
case ServerData::HOSTNAME:
|
|
||||||
sd.SetName(value.toString().toUtf8());
|
|
||||||
ok = !sd.addr.isEmpty();
|
|
||||||
break;
|
|
||||||
case ServerData::PING:
|
|
||||||
sd.ping = value.toInt(&ok);
|
|
||||||
break;
|
|
||||||
case ServerData::MODNAME:
|
|
||||||
sd.SetGameMode(value.toString().toUtf8());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (ok)
|
|
||||||
emit(dataChanged(index, index));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ServerModel::insertRows(int position, int count, const QModelIndex &index)
|
|
||||||
{
|
|
||||||
Q_UNUSED(index);
|
|
||||||
beginInsertRows(QModelIndex(), position, position + count - 1);
|
|
||||||
|
|
||||||
myData.insert(position, count, {});
|
|
||||||
|
|
||||||
endInsertRows();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ServerModel::removeRows(int position, int count, const QModelIndex &parent)
|
|
||||||
{
|
|
||||||
if (count == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
beginRemoveRows(parent, position, position + count - 1);
|
|
||||||
myData.erase(myData.begin()+position, myData.begin() + position + count);
|
|
||||||
endRemoveRows();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex ServerModel::index(int row, int column, const QModelIndex &parent) const
|
|
||||||
{
|
|
||||||
|
|
||||||
QModelIndex index = QAbstractTableModel::index(row, column, parent);
|
|
||||||
return index;
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
#ifndef SERVERMODEL_FONTMODEL_HPP
|
|
||||||
#define SERVERMODEL_FONTMODEL_HPP
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <vector>
|
|
||||||
#include <QString>
|
|
||||||
#include <QAbstractTableModel>
|
|
||||||
#include <components/openmw-mp/Master/MasterData.hpp>
|
|
||||||
|
|
||||||
struct ServerData : public QueryData
|
|
||||||
{
|
|
||||||
QString addr;
|
|
||||||
int ping;
|
|
||||||
enum IDS
|
|
||||||
{
|
|
||||||
ADDR,
|
|
||||||
HOSTNAME,
|
|
||||||
PLAYERS,
|
|
||||||
MAX_PLAYERS,
|
|
||||||
PASSW,
|
|
||||||
MODNAME,
|
|
||||||
PING,
|
|
||||||
VERSION,
|
|
||||||
LAST
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class ServerModel: public QAbstractTableModel
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit ServerModel(QObject *parent = nullptr);
|
|
||||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_FINAL;
|
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_FINAL;
|
|
||||||
int columnCount(const QModelIndex &parent) const Q_DECL_FINAL;
|
|
||||||
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) Q_DECL_FINAL;
|
|
||||||
|
|
||||||
bool insertRows(int row, int count, const QModelIndex &index = QModelIndex()) Q_DECL_FINAL;
|
|
||||||
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) Q_DECL_FINAL;
|
|
||||||
|
|
||||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const Q_DECL_FINAL;
|
|
||||||
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_FINAL;
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
|
||||||
//QHash<int, QByteArray> roles;
|
|
||||||
QVector<ServerData> myData;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //SERVERMODEL_FONTMODEL_HPP
|
|
|
@ -1,15 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 07.05.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef OPENMW_TYPES_HPP
|
|
||||||
#define OPENMW_TYPES_HPP
|
|
||||||
|
|
||||||
#include <QPair>
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
typedef QPair <QString, unsigned short> AddrPair;
|
|
||||||
typedef QPair <int, AddrPair> ServerRow;
|
|
||||||
|
|
||||||
|
|
||||||
#endif //OPENMW_TYPES_HPP
|
|
|
@ -1,53 +0,0 @@
|
||||||
#include <QApplication>
|
|
||||||
#include <components/settings/settings.hpp>
|
|
||||||
#include <components/files/configurationmanager.hpp>
|
|
||||||
#include <apps/browser/netutils/QueryClient.hpp>
|
|
||||||
#include "MainWindow.hpp"
|
|
||||||
|
|
||||||
std::string loadSettings (Settings::Manager & settings)
|
|
||||||
{
|
|
||||||
Files::ConfigurationManager mCfgMgr;
|
|
||||||
// Create the settings manager and load default settings file
|
|
||||||
const std::string localdefault = (mCfgMgr.getLocalPath() / "tes3mp-client-default.cfg").string();
|
|
||||||
const std::string globaldefault = (mCfgMgr.getGlobalPath() / "tes3mp-client-default.cfg").string();
|
|
||||||
|
|
||||||
// prefer local
|
|
||||||
if (boost::filesystem::exists(localdefault))
|
|
||||||
settings.loadDefault(localdefault);
|
|
||||||
else if (boost::filesystem::exists(globaldefault))
|
|
||||||
settings.loadDefault(globaldefault);
|
|
||||||
else
|
|
||||||
throw std::runtime_error ("No default settings file found! Make sure the file \"tes3mp-client-default.cfg\" was properly installed.");
|
|
||||||
|
|
||||||
// load user settings if they exist
|
|
||||||
const std::string settingspath = (mCfgMgr.getUserConfigPath() / "tes3mp-client.cfg").string();
|
|
||||||
if (boost::filesystem::exists(settingspath))
|
|
||||||
settings.loadUser(settingspath);
|
|
||||||
|
|
||||||
return settingspath;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
Settings::Manager mgr;
|
|
||||||
|
|
||||||
loadSettings(mgr);
|
|
||||||
|
|
||||||
std::string addr = mgr.getString("address", "Master");
|
|
||||||
int port = mgr.getInt("port", "Master");
|
|
||||||
|
|
||||||
// Is this an attempt to connect to the official master server at the old port? If so,
|
|
||||||
// redirect it to the correct port for the currently used fork of RakNet
|
|
||||||
if (Misc::StringUtils::ciEqual(addr, "master.tes3mp.com") && port == 25560)
|
|
||||||
port = 25561;
|
|
||||||
|
|
||||||
// initialize resources, if needed
|
|
||||||
// Q_INIT_RESOURCE(resfile);
|
|
||||||
|
|
||||||
QueryClient::Get().SetServer(addr, port);
|
|
||||||
QApplication app(argc, argv);
|
|
||||||
MainWindow d;
|
|
||||||
|
|
||||||
d.show();
|
|
||||||
return app.exec();
|
|
||||||
}
|
|
|
@ -1,99 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 07.01.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <RakPeer.h>
|
|
||||||
#include <HTTPConnection2.h>
|
|
||||||
#include <TCPInterface.h>
|
|
||||||
#include <RakSleep.h>
|
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#include "HTTPNetwork.hpp"
|
|
||||||
|
|
||||||
using namespace RakNet;
|
|
||||||
|
|
||||||
HTTPNetwork::HTTPNetwork(std::string addr, unsigned short port) : address(addr), port(port)
|
|
||||||
{
|
|
||||||
httpConnection = HTTPConnection2::GetInstance();
|
|
||||||
tcpInterface = new TCPInterface;
|
|
||||||
tcpInterface->Start(0, 64);
|
|
||||||
tcpInterface->AttachPlugin(httpConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
HTTPNetwork::~HTTPNetwork()
|
|
||||||
{
|
|
||||||
delete tcpInterface;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string HTTPNetwork::answer()
|
|
||||||
{
|
|
||||||
RakNet::SystemAddress sa;
|
|
||||||
RakNet::Packet *packet;
|
|
||||||
RakNet::SystemAddress hostReceived;
|
|
||||||
RakNet::RakString response;
|
|
||||||
RakNet::RakString transmitted, hostTransmitted;
|
|
||||||
int contentOffset = 0;
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
// This is kind of crappy, but for TCP plugins, always do HasCompletedConnectionAttempt,
|
|
||||||
// then Receive(), then HasFailedConnectionAttempt(),HasLostConnection()
|
|
||||||
sa = tcpInterface->HasCompletedConnectionAttempt();
|
|
||||||
if (sa != RakNet::UNASSIGNED_SYSTEM_ADDRESS)
|
|
||||||
printf("Connected to master server: %s\n", sa.ToString());
|
|
||||||
|
|
||||||
sa = tcpInterface->HasFailedConnectionAttempt();
|
|
||||||
if (sa != RakNet::UNASSIGNED_SYSTEM_ADDRESS)
|
|
||||||
{
|
|
||||||
printf("Failed to connect to master server: %s\n", sa.ToString());
|
|
||||||
return "FAIL_CONNECT";
|
|
||||||
}
|
|
||||||
sa = tcpInterface->HasLostConnection();
|
|
||||||
if (sa != RakNet::UNASSIGNED_SYSTEM_ADDRESS)
|
|
||||||
{
|
|
||||||
printf("Lost connection to master server: %s\n", sa.ToString());
|
|
||||||
return "LOST_CONNECTION";
|
|
||||||
}
|
|
||||||
|
|
||||||
for (packet = tcpInterface->Receive(); packet; tcpInterface->DeallocatePacket(
|
|
||||||
packet), packet = tcpInterface->Receive());
|
|
||||||
|
|
||||||
if (httpConnection->GetResponse(transmitted, hostTransmitted, response, hostReceived, contentOffset))
|
|
||||||
{
|
|
||||||
if (contentOffset < 0)
|
|
||||||
return "NO_CONTENT"; // no content
|
|
||||||
tcpInterface->CloseConnection(sa);
|
|
||||||
|
|
||||||
return (response.C_String() + contentOffset);
|
|
||||||
}
|
|
||||||
RakSleep(30);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string HTTPNetwork::getData(const char *uri)
|
|
||||||
{
|
|
||||||
RakNet::RakString createRequest = RakNet::RakString::FormatForGET(uri);
|
|
||||||
|
|
||||||
if (!httpConnection->TransmitRequest(createRequest, address.c_str(), port))
|
|
||||||
return "UNKNOWN_ADDRESS";
|
|
||||||
return answer();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string HTTPNetwork::getDataPOST(const char *uri, const char* body, const char* contentType)
|
|
||||||
{
|
|
||||||
RakNet::RakString createRequest = RakNet::RakString::FormatForPOST(uri, contentType, body);
|
|
||||||
|
|
||||||
if (!httpConnection->TransmitRequest(createRequest, address.c_str(), port))
|
|
||||||
return "UNKNOWN_ADDRESS";
|
|
||||||
return answer();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string HTTPNetwork::getDataPUT(const char *uri, const char* body, const char* contentType)
|
|
||||||
{
|
|
||||||
RakNet::RakString createRequest = RakNet::RakString::FormatForPUT(uri, contentType, body);
|
|
||||||
|
|
||||||
if (!httpConnection->TransmitRequest(createRequest, address.c_str(), port))
|
|
||||||
return "UNKNOWN_ADDRESS";
|
|
||||||
return answer();
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 07.01.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef NEWLAUNCHER_HTTPNETWORK_HPP
|
|
||||||
#define NEWLAUNCHER_HTTPNETWORK_HPP
|
|
||||||
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace RakNet
|
|
||||||
{
|
|
||||||
class TCPInterface;
|
|
||||||
class HTTPConnection2;
|
|
||||||
}
|
|
||||||
|
|
||||||
class HTTPNetwork
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
HTTPNetwork(std::string addr, unsigned short port);
|
|
||||||
~HTTPNetwork();
|
|
||||||
std::string getData(const char *uri);
|
|
||||||
std::string getDataPOST(const char *uri, const char* body, const char* contentType = "application/json");
|
|
||||||
std::string getDataPUT(const char *uri, const char* body, const char* contentType = "application/json");
|
|
||||||
|
|
||||||
protected:
|
|
||||||
RakNet::TCPInterface *tcpInterface;
|
|
||||||
RakNet::HTTPConnection2 *httpConnection;
|
|
||||||
std::string address;
|
|
||||||
unsigned short port;
|
|
||||||
std::string answer();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //NEWLAUNCHER_HTTPNETWORK_HPP
|
|
|
@ -1,212 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 24.04.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "QueryClient.hpp"
|
|
||||||
#include <RakSleep.h>
|
|
||||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
|
||||||
#include <iostream>
|
|
||||||
#include <components/openmw-mp/Version.hpp>
|
|
||||||
#include <qdebug.h>
|
|
||||||
|
|
||||||
using namespace RakNet;
|
|
||||||
using namespace std;
|
|
||||||
using namespace mwmp;
|
|
||||||
|
|
||||||
QueryClient::QueryClient()
|
|
||||||
{
|
|
||||||
peer = RakPeerInterface::GetInstance();
|
|
||||||
pmq = new PacketMasterQuery(peer);
|
|
||||||
pmu = new PacketMasterUpdate(peer);
|
|
||||||
RakNet::SocketDescriptor sd;
|
|
||||||
peer->Startup(8, &sd, 1);
|
|
||||||
status = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
QueryClient::~QueryClient()
|
|
||||||
{
|
|
||||||
delete pmq;
|
|
||||||
delete pmu;
|
|
||||||
RakPeerInterface::DestroyInstance(peer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QueryClient::SetServer(const string &addr, unsigned short port)
|
|
||||||
{
|
|
||||||
masterAddr = SystemAddress(addr.c_str(), port);
|
|
||||||
}
|
|
||||||
|
|
||||||
QueryClient &QueryClient::Get()
|
|
||||||
{
|
|
||||||
static QueryClient myInstance;
|
|
||||||
return myInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
map<SystemAddress, QueryData> QueryClient::Query()
|
|
||||||
{
|
|
||||||
map<SystemAddress, QueryData> query;
|
|
||||||
BitStream bs;
|
|
||||||
bs.Write((unsigned char) (ID_MASTER_QUERY));
|
|
||||||
qDebug() << "Locking mutex in QueryClient::Query()";
|
|
||||||
mxServers.lock();
|
|
||||||
status = -1;
|
|
||||||
int attempts = 3;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (Connect() == IS_NOT_CONNECTED)
|
|
||||||
{
|
|
||||||
qDebug() << "Unlocking mutex in QueryClient::Query()";
|
|
||||||
mxServers.unlock();
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
int code = peer->Send(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, CHANNEL_MASTER, masterAddr, false);
|
|
||||||
|
|
||||||
if (code == 0)
|
|
||||||
{
|
|
||||||
qDebug() << "Unlocking mutex in QueryClient::Query()";
|
|
||||||
mxServers.unlock();
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
pmq->SetServers(&query);
|
|
||||||
status = GetAnswer(ID_MASTER_QUERY);
|
|
||||||
RakSleep(100);
|
|
||||||
}
|
|
||||||
while(status != ID_MASTER_QUERY && attempts-- > 0);
|
|
||||||
if(status != ID_MASTER_QUERY)
|
|
||||||
qDebug() << "Getting query was failed";
|
|
||||||
qDebug() << "Unlocking mutex in QueryClient::Query()";
|
|
||||||
peer->CloseConnection(masterAddr, true);
|
|
||||||
mxServers.unlock();
|
|
||||||
qDebug() <<"Answer" << (status == ID_MASTER_QUERY ? "ok." : "wrong.");
|
|
||||||
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
pair<SystemAddress, QueryData> QueryClient::Update(const RakNet::SystemAddress &addr)
|
|
||||||
{
|
|
||||||
qDebug() << "Locking mutex in QueryClient::Update(RakNet::SystemAddress addr)";
|
|
||||||
pair<SystemAddress, QueryData> server;
|
|
||||||
BitStream bs;
|
|
||||||
bs.Write((unsigned char) (ID_MASTER_UPDATE));
|
|
||||||
bs.Write(addr);
|
|
||||||
|
|
||||||
mxServers.lock();
|
|
||||||
status = -1;
|
|
||||||
int attempts = 3;
|
|
||||||
pmu->SetServer(&server);
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (Connect() == IS_NOT_CONNECTED)
|
|
||||||
{
|
|
||||||
qDebug() << IS_NOT_CONNECTED;
|
|
||||||
qDebug() << "Unlocking mutex in QueryClient::Update(RakNet::SystemAddress addr)";
|
|
||||||
mxServers.unlock();
|
|
||||||
return server;
|
|
||||||
}
|
|
||||||
|
|
||||||
peer->Send(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, CHANNEL_MASTER, masterAddr, false);
|
|
||||||
status = GetAnswer(ID_MASTER_UPDATE);
|
|
||||||
RakSleep(100);
|
|
||||||
}
|
|
||||||
while(status != ID_MASTER_UPDATE && attempts-- > 0);
|
|
||||||
if(status != ID_MASTER_UPDATE)
|
|
||||||
qDebug() << "Getting update was failed";
|
|
||||||
peer->CloseConnection(masterAddr, true);
|
|
||||||
qDebug() << "Unlocking mutex in QueryClient::Update(RakNet::SystemAddress addr)";
|
|
||||||
mxServers.unlock();
|
|
||||||
return server;
|
|
||||||
}
|
|
||||||
|
|
||||||
MASTER_PACKETS QueryClient::GetAnswer(MASTER_PACKETS waitingPacket)
|
|
||||||
{
|
|
||||||
RakNet::Packet *packet;
|
|
||||||
bool update = true;
|
|
||||||
unsigned char pid = 0;
|
|
||||||
int id = -1;
|
|
||||||
while (update)
|
|
||||||
{
|
|
||||||
for (packet = peer->Receive(); packet; peer->DeallocatePacket(packet), packet = peer->Receive())
|
|
||||||
{
|
|
||||||
BitStream data(packet->data, packet->length, false);
|
|
||||||
pmq->SetReadStream(&data);
|
|
||||||
pmu->SetReadStream(&data);
|
|
||||||
data.Read(pid);
|
|
||||||
switch(pid)
|
|
||||||
{
|
|
||||||
case ID_CONNECTION_LOST:
|
|
||||||
qDebug() << "ID_CONNECTION_LOST";
|
|
||||||
case ID_DISCONNECTION_NOTIFICATION:
|
|
||||||
qDebug() << "Disconnected";
|
|
||||||
update = false;
|
|
||||||
break;
|
|
||||||
case ID_MASTER_QUERY:
|
|
||||||
qDebug() << "ID_MASTER_QUERY";
|
|
||||||
if (waitingPacket == ID_MASTER_QUERY)
|
|
||||||
pmq->Read();
|
|
||||||
else
|
|
||||||
qDebug() << "Got wrong packet";
|
|
||||||
update = false;
|
|
||||||
id = pid;
|
|
||||||
break;
|
|
||||||
case ID_MASTER_UPDATE:
|
|
||||||
qDebug() << "ID_MASTER_UPDATE";
|
|
||||||
if (waitingPacket == ID_MASTER_UPDATE)
|
|
||||||
pmu->Read();
|
|
||||||
else
|
|
||||||
qDebug() << "Got wrong packet";
|
|
||||||
update = false;
|
|
||||||
id = pid;
|
|
||||||
break;
|
|
||||||
case ID_MASTER_ANNOUNCE:
|
|
||||||
qDebug() << "ID_MASTER_ANNOUNCE";
|
|
||||||
update = false;
|
|
||||||
id = pid;
|
|
||||||
break;
|
|
||||||
case ID_CONNECTION_REQUEST_ACCEPTED:
|
|
||||||
qDebug() << "ID_CONNECTION_REQUEST_ACCEPTED";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RakSleep(500);
|
|
||||||
}
|
|
||||||
return (MASTER_PACKETS)(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
ConnectionState QueryClient::Connect()
|
|
||||||
{
|
|
||||||
|
|
||||||
ConnectionAttemptResult car = peer->Connect(masterAddr.ToString(false), masterAddr.GetPort(), TES3MP_MASTERSERVER_PASSW,
|
|
||||||
strlen(TES3MP_MASTERSERVER_PASSW), nullptr, 0, 5, 500);
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
ConnectionState state = peer->GetConnectionState(masterAddr);
|
|
||||||
switch (state)
|
|
||||||
{
|
|
||||||
case IS_CONNECTED:
|
|
||||||
qDebug() << "Connected";
|
|
||||||
return IS_CONNECTED;
|
|
||||||
case IS_NOT_CONNECTED:
|
|
||||||
case IS_DISCONNECTED:
|
|
||||||
case IS_SILENTLY_DISCONNECTING:
|
|
||||||
case IS_DISCONNECTING:
|
|
||||||
{
|
|
||||||
qDebug() << "Cannot connect to the master server. Code:"<< state;
|
|
||||||
return IS_NOT_CONNECTED;
|
|
||||||
}
|
|
||||||
case IS_PENDING:
|
|
||||||
case IS_CONNECTING:
|
|
||||||
qDebug() << "Pending";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
RakSleep(500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int QueryClient::Status()
|
|
||||||
{
|
|
||||||
return status;
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 24.04.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef OPENMW_QUERYCLIENT_HPP
|
|
||||||
#define OPENMW_QUERYCLIENT_HPP
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <RakPeerInterface.h>
|
|
||||||
#include <components/openmw-mp/Master/PacketMasterQuery.hpp>
|
|
||||||
#include <components/openmw-mp/Master/PacketMasterUpdate.hpp>
|
|
||||||
#include <apps/browser/ServerModel.hpp>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
class QueryClient
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
QueryClient(QueryClient const &) = delete;
|
|
||||||
QueryClient(QueryClient &&) = delete;
|
|
||||||
QueryClient &operator=(QueryClient const &) = delete;
|
|
||||||
QueryClient &operator=(QueryClient &&) = delete;
|
|
||||||
|
|
||||||
static QueryClient &Get();
|
|
||||||
void SetServer(const std::string &addr, unsigned short port);
|
|
||||||
std::map<RakNet::SystemAddress, QueryData> Query();
|
|
||||||
std::pair<RakNet::SystemAddress, QueryData> Update(const RakNet::SystemAddress &addr);
|
|
||||||
int Status();
|
|
||||||
private:
|
|
||||||
RakNet::ConnectionState Connect();
|
|
||||||
MASTER_PACKETS GetAnswer(MASTER_PACKETS packet);
|
|
||||||
protected:
|
|
||||||
QueryClient();
|
|
||||||
~QueryClient();
|
|
||||||
private:
|
|
||||||
int status;
|
|
||||||
RakNet::RakPeerInterface *peer;
|
|
||||||
RakNet::SystemAddress masterAddr;
|
|
||||||
mwmp::PacketMasterQuery *pmq;
|
|
||||||
mwmp::PacketMasterUpdate *pmu;
|
|
||||||
std::pair<RakNet::SystemAddress, ServerData> server;
|
|
||||||
std::mutex mxServers;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //OPENMW_QUERYCLIENT_HPP
|
|
|
@ -1,167 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 07.01.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <RakPeer.h>
|
|
||||||
#include <MessageIdentifiers.h>
|
|
||||||
#include <RakSleep.h>
|
|
||||||
#include <GetTime.h>
|
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
#include <components/openmw-mp/Version.hpp>
|
|
||||||
|
|
||||||
#include "Utils.hpp"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
unsigned int PingRakNetServer(const char *addr, unsigned short port)
|
|
||||||
{
|
|
||||||
RakNet::Packet *packet;
|
|
||||||
bool done = false;
|
|
||||||
RakNet::TimeMS time = PING_UNREACHABLE;
|
|
||||||
|
|
||||||
RakNet::SocketDescriptor socketDescriptor{0, ""};
|
|
||||||
RakNet::RakPeerInterface *peer = RakNet::RakPeerInterface::GetInstance();
|
|
||||||
peer->Startup(1, &socketDescriptor, 1);
|
|
||||||
if (!peer->Ping(addr, port, false))
|
|
||||||
return time;
|
|
||||||
RakNet::TimeMS start = RakNet::GetTimeMS();
|
|
||||||
while (!done)
|
|
||||||
{
|
|
||||||
RakNet::TimeMS now = RakNet::GetTimeMS();
|
|
||||||
if (now - start >= PING_UNREACHABLE)
|
|
||||||
break;
|
|
||||||
|
|
||||||
packet = peer->Receive();
|
|
||||||
if (!packet)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
switch (packet->data[0])
|
|
||||||
{
|
|
||||||
case ID_DISCONNECTION_NOTIFICATION:
|
|
||||||
case ID_CONNECTION_LOST:
|
|
||||||
done = true;
|
|
||||||
break;
|
|
||||||
case ID_CONNECTED_PING:
|
|
||||||
case ID_UNCONNECTED_PONG:
|
|
||||||
{
|
|
||||||
RakNet::BitStream bsIn(&packet->data[1], packet->length, false);
|
|
||||||
bsIn.Read(time);
|
|
||||||
time = now - time;
|
|
||||||
done = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
peer->DeallocatePacket(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
peer->Shutdown(0);
|
|
||||||
RakNet::RakPeerInterface::DestroyInstance(peer);
|
|
||||||
return time > PING_UNREACHABLE ? PING_UNREACHABLE : time;
|
|
||||||
}
|
|
||||||
|
|
||||||
ServerExtendedData getExtendedData(const char *addr, unsigned short port)
|
|
||||||
{
|
|
||||||
ServerExtendedData data;
|
|
||||||
RakNet::SocketDescriptor socketDescriptor = {0, ""};
|
|
||||||
RakNet::RakPeerInterface *peer = RakNet::RakPeerInterface::GetInstance();
|
|
||||||
peer->Startup(1, &socketDescriptor, 1);
|
|
||||||
|
|
||||||
stringstream sstr;
|
|
||||||
sstr << TES3MP_VERSION;
|
|
||||||
sstr << TES3MP_PROTO_VERSION;
|
|
||||||
|
|
||||||
std::string msg;
|
|
||||||
|
|
||||||
if (peer->Connect(addr, port, sstr.str().c_str(), (int)(sstr.str().size()), nullptr, 0, 3, 500, 0) != RakNet::CONNECTION_ATTEMPT_STARTED)
|
|
||||||
msg = "Connection attempt failed.\n";
|
|
||||||
|
|
||||||
|
|
||||||
int queue = 0;
|
|
||||||
while (queue == 0)
|
|
||||||
{
|
|
||||||
for (RakNet::Packet *packet = peer->Receive(); packet; peer->DeallocatePacket(
|
|
||||||
packet), packet = peer->Receive())
|
|
||||||
{
|
|
||||||
switch (packet->data[0])
|
|
||||||
{
|
|
||||||
case ID_CONNECTION_ATTEMPT_FAILED:
|
|
||||||
{
|
|
||||||
msg = "Connection failed.\n"
|
|
||||||
"Either the IP address is wrong or a firewall on either system is blocking\n"
|
|
||||||
"UDP packets on the port you have chosen.";
|
|
||||||
queue = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ID_INVALID_PASSWORD:
|
|
||||||
{
|
|
||||||
msg = "Connection failed.\n"
|
|
||||||
"The client or server is outdated.\n";
|
|
||||||
queue = -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ID_CONNECTION_REQUEST_ACCEPTED:
|
|
||||||
{
|
|
||||||
msg = "Connection accepted.\n";
|
|
||||||
queue = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ID_DISCONNECTION_NOTIFICATION:
|
|
||||||
throw runtime_error("ID_DISCONNECTION_NOTIFICATION.\n");
|
|
||||||
case ID_CONNECTION_BANNED:
|
|
||||||
throw runtime_error("ID_CONNECTION_BANNED.\n");
|
|
||||||
case ID_CONNECTION_LOST:
|
|
||||||
throw runtime_error("ID_CONNECTION_LOST.\n");
|
|
||||||
default:
|
|
||||||
printf("Connection message with identifier %i has arrived in initialization.\n", packet->data[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
puts(msg.c_str());
|
|
||||||
|
|
||||||
if (queue == -1) // connection is failed
|
|
||||||
return data;
|
|
||||||
|
|
||||||
{
|
|
||||||
RakNet::BitStream bs;
|
|
||||||
bs.Write((unsigned char) (ID_USER_PACKET_ENUM + 1));
|
|
||||||
peer->Send(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, 0, RakNet::UNASSIGNED_SYSTEM_ADDRESS, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
RakNet::Packet *packet;
|
|
||||||
bool done = false;
|
|
||||||
while (!done)
|
|
||||||
{
|
|
||||||
for (packet = peer->Receive(); packet; peer->DeallocatePacket(packet), packet = peer->Receive())
|
|
||||||
{
|
|
||||||
if (packet->data[0] == (ID_USER_PACKET_ENUM + 1))
|
|
||||||
{
|
|
||||||
RakNet::BitStream bs(packet->data, packet->length, false);
|
|
||||||
bs.IgnoreBytes(1);
|
|
||||||
size_t length = 0;
|
|
||||||
bs.Read(length);
|
|
||||||
for (size_t i = 0; i < length; i++)
|
|
||||||
{
|
|
||||||
RakNet::RakString str;
|
|
||||||
bs.Read(str);
|
|
||||||
data.players.emplace_back(str.C_String());
|
|
||||||
}
|
|
||||||
bs.Read(length);
|
|
||||||
for (size_t i = 0; i < length; i++)
|
|
||||||
{
|
|
||||||
RakNet::RakString str;
|
|
||||||
bs.Read(str);
|
|
||||||
data.plugins.emplace_back(str.C_String());
|
|
||||||
}
|
|
||||||
done = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
peer->Shutdown(0);
|
|
||||||
RakSleep(10);
|
|
||||||
RakNet::RakPeerInterface::DestroyInstance(peer);
|
|
||||||
return data;
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 07.01.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef NEWLAUNCHER_PING_HPP
|
|
||||||
#define NEWLAUNCHER_PING_HPP
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
|
|
||||||
#define PING_UNREACHABLE 999
|
|
||||||
|
|
||||||
unsigned int PingRakNetServer(const char *addr, unsigned short port);
|
|
||||||
|
|
||||||
struct ServerExtendedData
|
|
||||||
{
|
|
||||||
std::vector<std::string> players;
|
|
||||||
std::vector<std::string> plugins;
|
|
||||||
};
|
|
||||||
|
|
||||||
ServerExtendedData getExtendedData(const char *addr, unsigned short port);
|
|
||||||
|
|
||||||
#endif //NEWLAUNCHER_PING_HPP
|
|
|
@ -4,7 +4,7 @@ set(BSATOOL
|
||||||
source_group(apps\\bsatool FILES ${BSATOOL})
|
source_group(apps\\bsatool FILES ${BSATOOL})
|
||||||
|
|
||||||
# Main executable
|
# Main executable
|
||||||
openmw_add_executable(bsatool
|
add_executable(bsatool
|
||||||
${BSATOOL}
|
${BSATOOL}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
#include <boost/program_options.hpp>
|
#include <boost/program_options.hpp>
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
|
|
|
@ -8,7 +8,7 @@ set(ESMTOOL
|
||||||
source_group(apps\\esmtool FILES ${ESMTOOL})
|
source_group(apps\\esmtool FILES ${ESMTOOL})
|
||||||
|
|
||||||
# Main executable
|
# Main executable
|
||||||
openmw_add_executable(esmtool
|
add_executable(esmtool
|
||||||
${ESMTOOL}
|
${ESMTOOL}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
#include <boost/program_options.hpp>
|
#include <boost/program_options.hpp>
|
||||||
|
|
||||||
|
@ -357,10 +356,10 @@ int load(Arguments& info)
|
||||||
EsmTool::RecordBase *record = EsmTool::RecordBase::create(n);
|
EsmTool::RecordBase *record = EsmTool::RecordBase::create(n);
|
||||||
if (record == 0)
|
if (record == 0)
|
||||||
{
|
{
|
||||||
if (std::find(skipped.begin(), skipped.end(), n.intval) == skipped.end())
|
if (std::find(skipped.begin(), skipped.end(), n.val) == skipped.end())
|
||||||
{
|
{
|
||||||
std::cout << "Skipping " << n.toString() << " records." << std::endl;
|
std::cout << "Skipping " << n.toString() << " records." << std::endl;
|
||||||
skipped.push_back(n.intval);
|
skipped.push_back(n.val);
|
||||||
}
|
}
|
||||||
|
|
||||||
esm.skipRecord();
|
esm.skipRecord();
|
||||||
|
@ -392,7 +391,7 @@ int load(Arguments& info)
|
||||||
record->print();
|
record->print();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (record->getType().intval == ESM::REC_CELL && loadCells && interested)
|
if (record->getType().val == ESM::REC_CELL && loadCells && interested)
|
||||||
{
|
{
|
||||||
loadCell(record->cast<ESM::Cell>()->get(), esm, info);
|
loadCell(record->cast<ESM::Cell>()->get(), esm, info);
|
||||||
}
|
}
|
||||||
|
@ -405,7 +404,7 @@ int load(Arguments& info)
|
||||||
{
|
{
|
||||||
delete record;
|
delete record;
|
||||||
}
|
}
|
||||||
++info.data.mRecordStats[n.intval];
|
++info.data.mRecordStats[n.val];
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch(std::exception &e) {
|
} catch(std::exception &e) {
|
||||||
|
@ -443,18 +442,23 @@ int clone(Arguments& info)
|
||||||
size_t recordCount = info.data.mRecords.size();
|
size_t recordCount = info.data.mRecords.size();
|
||||||
|
|
||||||
int digitCount = 1; // For a nicer output
|
int digitCount = 1; // For a nicer output
|
||||||
if (recordCount > 0)
|
if (recordCount > 9) ++digitCount;
|
||||||
digitCount = (int)std::log10(recordCount) + 1;
|
if (recordCount > 99) ++digitCount;
|
||||||
|
if (recordCount > 999) ++digitCount;
|
||||||
|
if (recordCount > 9999) ++digitCount;
|
||||||
|
if (recordCount > 99999) ++digitCount;
|
||||||
|
if (recordCount > 999999) ++digitCount;
|
||||||
|
|
||||||
std::cout << "Loaded " << recordCount << " records:" << std::endl << std::endl;
|
std::cout << "Loaded " << recordCount << " records:" << std::endl << std::endl;
|
||||||
|
|
||||||
|
ESM::NAME name;
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
typedef std::map<int, int> Stats;
|
typedef std::map<int, int> Stats;
|
||||||
Stats &stats = info.data.mRecordStats;
|
Stats &stats = info.data.mRecordStats;
|
||||||
for (Stats::iterator it = stats.begin(); it != stats.end(); ++it)
|
for (Stats::iterator it = stats.begin(); it != stats.end(); ++it)
|
||||||
{
|
{
|
||||||
ESM::NAME name;
|
name.val = it->first;
|
||||||
name.intval = it->first;
|
|
||||||
int amount = it->second;
|
int amount = it->second;
|
||||||
std::cout << std::setw(digitCount) << amount << " " << name.toString() << " ";
|
std::cout << std::setw(digitCount) << amount << " " << name.toString() << " ";
|
||||||
|
|
||||||
|
@ -487,12 +491,12 @@ int clone(Arguments& info)
|
||||||
for (Records::iterator it = records.begin(); it != records.end() && i > 0; ++it)
|
for (Records::iterator it = records.begin(); it != records.end() && i > 0; ++it)
|
||||||
{
|
{
|
||||||
EsmTool::RecordBase *record = *it;
|
EsmTool::RecordBase *record = *it;
|
||||||
const ESM::NAME& typeName = record->getType();
|
name.val = record->getType().val;
|
||||||
|
|
||||||
esm.startRecord(typeName.toString(), record->getFlags());
|
esm.startRecord(name.toString(), record->getFlags());
|
||||||
|
|
||||||
record->save(esm);
|
record->save(esm);
|
||||||
if (typeName.intval == ESM::REC_CELL) {
|
if (name.val == ESM::REC_CELL) {
|
||||||
ESM::Cell *ptr = &record->cast<ESM::Cell>()->get();
|
ESM::Cell *ptr = &record->cast<ESM::Cell>()->get();
|
||||||
if (!info.data.mCellRefs[ptr].empty()) {
|
if (!info.data.mCellRefs[ptr].empty()) {
|
||||||
typedef std::deque<std::pair<ESM::CellRef, bool> > RefList;
|
typedef std::deque<std::pair<ESM::CellRef, bool> > RefList;
|
||||||
|
@ -504,7 +508,7 @@ int clone(Arguments& info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
esm.endRecord(typeName.toString());
|
esm.endRecord(name.toString());
|
||||||
|
|
||||||
saved++;
|
saved++;
|
||||||
int perc = (int)((saved / (float)recordCount)*100);
|
int perc = (int)((saved / (float)recordCount)*100);
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <components/esm/loadrace.hpp>
|
#include <components/esm/loadrace.hpp>
|
||||||
#include <components/esm/loadspel.hpp>
|
#include <components/esm/loadspel.hpp>
|
||||||
#include <components/esm/loadweap.hpp>
|
#include <components/esm/loadweap.hpp>
|
||||||
|
#include <components/esm/aipackage.hpp>
|
||||||
|
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
|
|
||||||
|
|
|
@ -179,7 +179,7 @@ RecordBase::create(ESM::NAME type)
|
||||||
{
|
{
|
||||||
RecordBase *record = 0;
|
RecordBase *record = 0;
|
||||||
|
|
||||||
switch (type.intval) {
|
switch (type.val) {
|
||||||
case ESM::REC_ACTI:
|
case ESM::REC_ACTI:
|
||||||
{
|
{
|
||||||
record = new EsmTool::Record<ESM::Activator>;
|
record = new EsmTool::Record<ESM::Activator>;
|
||||||
|
@ -494,7 +494,7 @@ void Record<ESM::Book>::print()
|
||||||
std::cout << " Weight: " << mData.mData.mWeight << std::endl;
|
std::cout << " Weight: " << mData.mData.mWeight << std::endl;
|
||||||
std::cout << " Value: " << mData.mData.mValue << std::endl;
|
std::cout << " Value: " << mData.mData.mValue << std::endl;
|
||||||
std::cout << " IsScroll: " << mData.mData.mIsScroll << std::endl;
|
std::cout << " IsScroll: " << mData.mData.mIsScroll << std::endl;
|
||||||
std::cout << " SkillId: " << mData.mData.mSkillId << std::endl;
|
std::cout << " SkillID: " << mData.mData.mSkillID << std::endl;
|
||||||
std::cout << " Enchantment Points: " << mData.mData.mEnchant << std::endl;
|
std::cout << " Enchantment Points: " << mData.mData.mEnchant << std::endl;
|
||||||
if (mPrintPlain)
|
if (mPrintPlain)
|
||||||
{
|
{
|
||||||
|
@ -694,7 +694,7 @@ void Record<ESM::Dialogue>::print()
|
||||||
// loads, rather than loading and then dumping. :-( Anyone mind if
|
// loads, rather than loading and then dumping. :-( Anyone mind if
|
||||||
// I change this?
|
// I change this?
|
||||||
ESM::Dialogue::InfoContainer::iterator iit;
|
ESM::Dialogue::InfoContainer::iterator iit;
|
||||||
for (iit = mData.mInfo.begin(); iit != mData.mInfo.end(); ++iit)
|
for (iit = mData.mInfo.begin(); iit != mData.mInfo.end(); iit++)
|
||||||
std::cout << "INFO!" << iit->mId << std::endl;
|
std::cout << "INFO!" << iit->mId << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1040,47 +1040,45 @@ void Record<ESM::NPC>::print()
|
||||||
|
|
||||||
if (mData.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
|
if (mData.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
|
||||||
{
|
{
|
||||||
std::cout << " Level: " << mData.mNpdt.mLevel << std::endl;
|
std::cout << " Level: " << mData.mNpdt12.mLevel << std::endl;
|
||||||
std::cout << " Reputation: " << (int)mData.mNpdt.mReputation << std::endl;
|
std::cout << " Reputation: " << (int)mData.mNpdt12.mReputation << std::endl;
|
||||||
std::cout << " Disposition: " << (int)mData.mNpdt.mDisposition << std::endl;
|
std::cout << " Disposition: " << (int)mData.mNpdt12.mDisposition << std::endl;
|
||||||
std::cout << " Rank: " << (int)mData.mNpdt.mRank << std::endl;
|
std::cout << " Rank: " << (int)mData.mNpdt12.mRank << std::endl;
|
||||||
//Why do we want to print these fields? They are padding in the struct and contain
|
std::cout << " Unknown1: "
|
||||||
// nothing of real value. Now we don't deal with NPDTstruct12 in runtime either...
|
<< (unsigned int)((unsigned char)mData.mNpdt12.mUnknown1) << std::endl;
|
||||||
//std::cout << " Unknown1: "
|
std::cout << " Unknown2: "
|
||||||
// << (unsigned int)((unsigned char)mData.mNpdt12.mUnknown1) << std::endl;
|
<< (unsigned int)((unsigned char)mData.mNpdt12.mUnknown2) << std::endl;
|
||||||
//std::cout << " Unknown2: "
|
std::cout << " Unknown3: "
|
||||||
// << (unsigned int)((unsigned char)mData.mNpdt12.mUnknown2) << std::endl;
|
<< (unsigned int)((unsigned char)mData.mNpdt12.mUnknown3) << std::endl;
|
||||||
//std::cout << " Unknown3: "
|
std::cout << " Gold: " << mData.mNpdt12.mGold << std::endl;
|
||||||
// << (unsigned int)((unsigned char)mData.mNpdt12.mUnknown3) << std::endl;
|
|
||||||
std::cout << " Gold: " << mData.mNpdt.mGold << std::endl;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
std::cout << " Level: " << mData.mNpdt.mLevel << std::endl;
|
std::cout << " Level: " << mData.mNpdt52.mLevel << std::endl;
|
||||||
std::cout << " Reputation: " << (int)mData.mNpdt.mReputation << std::endl;
|
std::cout << " Reputation: " << (int)mData.mNpdt52.mReputation << std::endl;
|
||||||
std::cout << " Disposition: " << (int)mData.mNpdt.mDisposition << std::endl;
|
std::cout << " Disposition: " << (int)mData.mNpdt52.mDisposition << std::endl;
|
||||||
std::cout << " Rank: " << (int)mData.mNpdt.mRank << std::endl;
|
std::cout << " Rank: " << (int)mData.mNpdt52.mRank << std::endl;
|
||||||
std::cout << " FactionID: " << (int)mData.mNpdt.mFactionID << std::endl;
|
std::cout << " FactionID: " << (int)mData.mNpdt52.mFactionID << std::endl;
|
||||||
|
|
||||||
std::cout << " Attributes:" << std::endl;
|
std::cout << " Attributes:" << std::endl;
|
||||||
std::cout << " Strength: " << (int)mData.mNpdt.mStrength << std::endl;
|
std::cout << " Strength: " << (int)mData.mNpdt52.mStrength << std::endl;
|
||||||
std::cout << " Intelligence: " << (int)mData.mNpdt.mIntelligence << std::endl;
|
std::cout << " Intelligence: " << (int)mData.mNpdt52.mIntelligence << std::endl;
|
||||||
std::cout << " Willpower: " << (int)mData.mNpdt.mWillpower << std::endl;
|
std::cout << " Willpower: " << (int)mData.mNpdt52.mWillpower << std::endl;
|
||||||
std::cout << " Agility: " << (int)mData.mNpdt.mAgility << std::endl;
|
std::cout << " Agility: " << (int)mData.mNpdt52.mAgility << std::endl;
|
||||||
std::cout << " Speed: " << (int)mData.mNpdt.mSpeed << std::endl;
|
std::cout << " Speed: " << (int)mData.mNpdt52.mSpeed << std::endl;
|
||||||
std::cout << " Endurance: " << (int)mData.mNpdt.mEndurance << std::endl;
|
std::cout << " Endurance: " << (int)mData.mNpdt52.mEndurance << std::endl;
|
||||||
std::cout << " Personality: " << (int)mData.mNpdt.mPersonality << std::endl;
|
std::cout << " Personality: " << (int)mData.mNpdt52.mPersonality << std::endl;
|
||||||
std::cout << " Luck: " << (int)mData.mNpdt.mLuck << std::endl;
|
std::cout << " Luck: " << (int)mData.mNpdt52.mLuck << std::endl;
|
||||||
|
|
||||||
std::cout << " Skills:" << std::endl;
|
std::cout << " Skills:" << std::endl;
|
||||||
for (int i = 0; i != ESM::Skill::Length; i++)
|
for (int i = 0; i != ESM::Skill::Length; i++)
|
||||||
std::cout << " " << skillLabel(i) << ": "
|
std::cout << " " << skillLabel(i) << ": "
|
||||||
<< (int)(mData.mNpdt.mSkills[i]) << std::endl;
|
<< (int)(mData.mNpdt52.mSkills[i]) << std::endl;
|
||||||
|
|
||||||
std::cout << " Health: " << mData.mNpdt.mHealth << std::endl;
|
std::cout << " Health: " << mData.mNpdt52.mHealth << std::endl;
|
||||||
std::cout << " Magicka: " << mData.mNpdt.mMana << std::endl;
|
std::cout << " Magicka: " << mData.mNpdt52.mMana << std::endl;
|
||||||
std::cout << " Fatigue: " << mData.mNpdt.mFatigue << std::endl;
|
std::cout << " Fatigue: " << mData.mNpdt52.mFatigue << std::endl;
|
||||||
std::cout << " Unknown: " << (int)mData.mNpdt.mUnknown << std::endl;
|
std::cout << " Unknown: " << (int)mData.mNpdt52.mUnknown << std::endl;
|
||||||
std::cout << " Gold: " << mData.mNpdt.mGold << std::endl;
|
std::cout << " Gold: " << mData.mNpdt52.mGold << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ESM::ContItem>::iterator cit;
|
std::vector<ESM::ContItem>::iterator cit;
|
||||||
|
@ -1125,7 +1123,7 @@ void Record<ESM::Pathgrid>::print()
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
ESM::Pathgrid::PointList::iterator pit;
|
ESM::Pathgrid::PointList::iterator pit;
|
||||||
for (pit = mData.mPoints.begin(); pit != mData.mPoints.end(); ++pit)
|
for (pit = mData.mPoints.begin(); pit != mData.mPoints.end(); pit++)
|
||||||
{
|
{
|
||||||
std::cout << " Point[" << i << "]:" << std::endl;
|
std::cout << " Point[" << i << "]:" << std::endl;
|
||||||
std::cout << " Coordinates: (" << pit->mX << ","
|
std::cout << " Coordinates: (" << pit->mX << ","
|
||||||
|
@ -1137,7 +1135,7 @@ void Record<ESM::Pathgrid>::print()
|
||||||
}
|
}
|
||||||
i = 0;
|
i = 0;
|
||||||
ESM::Pathgrid::EdgeList::iterator eit;
|
ESM::Pathgrid::EdgeList::iterator eit;
|
||||||
for (eit = mData.mEdges.begin(); eit != mData.mEdges.end(); ++eit)
|
for (eit = mData.mEdges.begin(); eit != mData.mEdges.end(); eit++)
|
||||||
{
|
{
|
||||||
std::cout << " Edge[" << i << "]: " << eit->mV0 << " -> " << eit->mV1 << std::endl;
|
std::cout << " Edge[" << i << "]: " << eit->mV0 << " -> " << eit->mV1 << std::endl;
|
||||||
if (eit->mV0 >= mData.mData.mS2 || eit->mV1 >= mData.mData.mS2)
|
if (eit->mV0 >= mData.mData.mS2 || eit->mV1 >= mData.mData.mS2)
|
||||||
|
|
|
@ -16,8 +16,6 @@ set(ESSIMPORTER_FILES
|
||||||
importjour.cpp
|
importjour.cpp
|
||||||
importscri.cpp
|
importscri.cpp
|
||||||
importscpt.cpp
|
importscpt.cpp
|
||||||
importproj.cpp
|
|
||||||
importsplm.cpp
|
|
||||||
importercontext.cpp
|
importercontext.cpp
|
||||||
converter.cpp
|
converter.cpp
|
||||||
convertacdt.cpp
|
convertacdt.cpp
|
||||||
|
@ -30,7 +28,7 @@ set(ESSIMPORTER_FILES
|
||||||
convertplayer.cpp
|
convertplayer.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
openmw_add_executable(openmw-essimporter
|
add_executable(openmw-essimporter
|
||||||
${ESSIMPORTER_FILES}
|
${ESSIMPORTER_FILES}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -44,7 +42,3 @@ if (BUILD_WITH_CODE_COVERAGE)
|
||||||
add_definitions (--coverage)
|
add_definitions (--coverage)
|
||||||
target_link_libraries(openmw-essimporter gcov)
|
target_link_libraries(openmw-essimporter gcov)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (WIN32)
|
|
||||||
INSTALL(TARGETS openmw-essimporter RUNTIME DESTINATION ".")
|
|
||||||
endif(WIN32)
|
|
||||||
|
|
|
@ -1,9 +1,3 @@
|
||||||
#include <string>
|
|
||||||
#include <iostream>
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
#include <components/misc/stringops.hpp>
|
|
||||||
|
|
||||||
#include "convertacdt.hpp"
|
#include "convertacdt.hpp"
|
||||||
|
|
||||||
namespace ESSImport
|
namespace ESSImport
|
||||||
|
@ -55,49 +49,4 @@ namespace ESSImport
|
||||||
npcStats.mTimeToStartDrowning = actorData.mACDT.mBreathMeter;
|
npcStats.mTimeToStartDrowning = actorData.mACDT.mBreathMeter;
|
||||||
}
|
}
|
||||||
|
|
||||||
void convertANIS (const ANIS& anis, ESM::AnimationState& state)
|
|
||||||
{
|
|
||||||
static const char* animGroups[] =
|
|
||||||
{
|
|
||||||
"Idle", "Idle2", "Idle3", "Idle4", "Idle5", "Idle6", "Idle7", "Idle8", "Idle9", "Idlehh", "Idle1h", "Idle2c",
|
|
||||||
"Idle2w", "IdleSwim", "IdleSpell", "IdleCrossbow", "IdleSneak", "IdleStorm", "Torch", "Hit1", "Hit2", "Hit3",
|
|
||||||
"Hit4", "Hit5", "SwimHit1", "SwimHit2", "SwimHit3", "Death1", "Death2", "Death3", "Death4", "Death5",
|
|
||||||
"DeathKnockDown", "DeathKnockOut", "KnockDown", "KnockOut", "SwimDeath", "SwimDeath2", "SwimDeath3",
|
|
||||||
"SwimDeathKnockDown", "SwimDeathKnockOut", "SwimKnockOut", "SwimKnockDown", "SwimWalkForward",
|
|
||||||
"SwimWalkBack", "SwimWalkLeft", "SwimWalkRight", "SwimRunForward", "SwimRunBack", "SwimRunLeft",
|
|
||||||
"SwimRunRight", "SwimTurnLeft", "SwimTurnRight", "WalkForward", "WalkBack", "WalkLeft", "WalkRight",
|
|
||||||
"TurnLeft", "TurnRight", "RunForward", "RunBack", "RunLeft", "RunRight", "SneakForward", "SneakBack",
|
|
||||||
"SneakLeft", "SneakRight", "Jump", "WalkForwardhh", "WalkBackhh", "WalkLefthh", "WalkRighthh",
|
|
||||||
"TurnLefthh", "TurnRighthh", "RunForwardhh", "RunBackhh", "RunLefthh", "RunRighthh", "SneakForwardhh",
|
|
||||||
"SneakBackhh", "SneakLefthh", "SneakRighthh", "Jumphh", "WalkForward1h", "WalkBack1h", "WalkLeft1h",
|
|
||||||
"WalkRight1h", "TurnLeft1h", "TurnRight1h", "RunForward1h", "RunBack1h", "RunLeft1h", "RunRight1h",
|
|
||||||
"SneakForward1h", "SneakBack1h", "SneakLeft1h", "SneakRight1h", "Jump1h", "WalkForward2c", "WalkBack2c",
|
|
||||||
"WalkLeft2c", "WalkRight2c", "TurnLeft2c", "TurnRight2c", "RunForward2c", "RunBack2c", "RunLeft2c",
|
|
||||||
"RunRight2c", "SneakForward2c", "SneakBack2c", "SneakLeft2c", "SneakRight2c", "Jump2c", "WalkForward2w",
|
|
||||||
"WalkBack2w", "WalkLeft2w", "WalkRight2w", "TurnLeft2w", "TurnRight2w", "RunForward2w", "RunBack2w",
|
|
||||||
"RunLeft2w", "RunRight2w", "SneakForward2w", "SneakBack2w", "SneakLeft2w", "SneakRight2w", "Jump2w",
|
|
||||||
"SpellCast", "SpellTurnLeft", "SpellTurnRight", "Attack1", "Attack2", "Attack3", "SwimAttack1",
|
|
||||||
"SwimAttack2", "SwimAttack3", "HandToHand", "Crossbow", "BowAndArrow", "ThrowWeapon", "WeaponOneHand",
|
|
||||||
"WeaponTwoHand", "WeaponTwoWide", "Shield", "PickProbe", "InventoryHandToHand", "InventoryWeaponOneHand",
|
|
||||||
"InventoryWeaponTwoHand", "InventoryWeaponTwoWide"
|
|
||||||
};
|
|
||||||
|
|
||||||
if (anis.mGroupIndex < (sizeof(animGroups) / sizeof(*animGroups)))
|
|
||||||
{
|
|
||||||
std::string group(animGroups[anis.mGroupIndex]);
|
|
||||||
Misc::StringUtils::lowerCaseInPlace(group);
|
|
||||||
|
|
||||||
ESM::AnimationState::ScriptedAnimation scriptedAnim;
|
|
||||||
scriptedAnim.mGroup = group;
|
|
||||||
scriptedAnim.mTime = anis.mTime;
|
|
||||||
scriptedAnim.mAbsolute = true;
|
|
||||||
// Neither loop count nor queueing seems to be supported by the ess format.
|
|
||||||
scriptedAnim.mLoopCount = std::numeric_limits<size_t>::max();
|
|
||||||
state.mScriptedAnims.push_back(scriptedAnim);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
// TODO: Handle 0xFF index, which seems to be used for finished animations.
|
|
||||||
std::cerr << "unknown animation group index: " << static_cast<unsigned int>(anis.mGroupIndex) << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
#include <components/esm/creaturestats.hpp>
|
#include <components/esm/creaturestats.hpp>
|
||||||
#include <components/esm/npcstats.hpp>
|
#include <components/esm/npcstats.hpp>
|
||||||
#include <components/esm/loadskil.hpp>
|
#include <components/esm/loadskil.hpp>
|
||||||
#include <components/esm/animationstate.hpp>
|
|
||||||
|
|
||||||
#include "importacdt.hpp"
|
#include "importacdt.hpp"
|
||||||
|
|
||||||
|
@ -19,8 +18,6 @@ namespace ESSImport
|
||||||
void convertACSC (const ACSC& acsc, ESM::CreatureStats& cStats);
|
void convertACSC (const ACSC& acsc, ESM::CreatureStats& cStats);
|
||||||
|
|
||||||
void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats);
|
void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats);
|
||||||
|
|
||||||
void convertANIS (const ANIS& anis, ESM::AnimationState& state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#include "converter.hpp"
|
#include "converter.hpp"
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include <osgDB/WriteFile>
|
#include <osgDB/WriteFile>
|
||||||
|
|
||||||
|
@ -35,9 +34,6 @@ namespace
|
||||||
objstate.mCount = 0;
|
objstate.mCount = 0;
|
||||||
convertSCRI(cellref.mSCRI, objstate.mLocals);
|
convertSCRI(cellref.mSCRI, objstate.mLocals);
|
||||||
objstate.mHasLocals = !objstate.mLocals.mVariables.empty();
|
objstate.mHasLocals = !objstate.mLocals.mVariables.empty();
|
||||||
|
|
||||||
if (cellref.mHasANIS)
|
|
||||||
convertANIS(cellref.mANIS, objstate.mAnimationState);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isIndexedRefId(const std::string& indexedRefId)
|
bool isIndexedRefId(const std::string& indexedRefId)
|
||||||
|
@ -54,36 +50,6 @@ namespace
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void splitIndexedRefId(const std::string& indexedRefId, int& refIndex, std::string& refId)
|
|
||||||
{
|
|
||||||
std::stringstream stream;
|
|
||||||
stream << std::hex << indexedRefId.substr(indexedRefId.size()-8,8);
|
|
||||||
stream >> refIndex;
|
|
||||||
|
|
||||||
refId = indexedRefId.substr(0,indexedRefId.size()-8);
|
|
||||||
}
|
|
||||||
|
|
||||||
int convertActorId(const std::string& indexedRefId, ESSImport::Context& context)
|
|
||||||
{
|
|
||||||
if (isIndexedRefId(indexedRefId))
|
|
||||||
{
|
|
||||||
int refIndex;
|
|
||||||
std::string refId;
|
|
||||||
splitIndexedRefId(indexedRefId, refIndex, refId);
|
|
||||||
|
|
||||||
auto it = context.mActorIdMap.find(std::make_pair(refIndex, refId));
|
|
||||||
if (it == context.mActorIdMap.end())
|
|
||||||
return -1;
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
else if (indexedRefId == "PlayerSaveGame")
|
|
||||||
{
|
|
||||||
return context.mPlayer.mObject.mCreatureStats.mActorId;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ESSImport
|
namespace ESSImport
|
||||||
|
@ -168,7 +134,7 @@ namespace ESSImport
|
||||||
osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("png");
|
osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("png");
|
||||||
if (!readerwriter)
|
if (!readerwriter)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: can't write global map image, no png readerwriter found" << std::endl;
|
std::cerr << "can't write global map image, no png readerwriter found" << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +143,7 @@ namespace ESSImport
|
||||||
osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*image2, ostream);
|
osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*image2, ostream);
|
||||||
if (!result.success())
|
if (!result.success())
|
||||||
{
|
{
|
||||||
std::cerr << "Error: can't write global map image: " << result.message() << " code " << result.status() << std::endl;
|
std::cerr << "can't write global map image: " << result.message() << " code " << result.status() << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,9 +319,12 @@ namespace ESSImport
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
std::stringstream stream;
|
||||||
|
stream << std::hex << cellref.mIndexedRefId.substr(cellref.mIndexedRefId.size()-8,8);
|
||||||
int refIndex;
|
int refIndex;
|
||||||
splitIndexedRefId(cellref.mIndexedRefId, refIndex, out.mRefID);
|
stream >> refIndex;
|
||||||
|
|
||||||
|
out.mRefID = cellref.mIndexedRefId.substr(0,cellref.mIndexedRefId.size()-8);
|
||||||
std::string idLower = Misc::StringUtils::lowerCase(out.mRefID);
|
std::string idLower = Misc::StringUtils::lowerCase(out.mRefID);
|
||||||
|
|
||||||
std::map<std::pair<int, std::string>, NPCC>::const_iterator npccIt = mContext->mNpcChanges.find(
|
std::map<std::pair<int, std::string>, NPCC>::const_iterator npccIt = mContext->mNpcChanges.find(
|
||||||
|
@ -375,10 +344,6 @@ namespace ESSImport
|
||||||
convertNpcData(cellref, objstate.mNpcStats);
|
convertNpcData(cellref, objstate.mNpcStats);
|
||||||
convertNPCC(npccIt->second, objstate);
|
convertNPCC(npccIt->second, objstate);
|
||||||
convertCellRef(cellref, objstate);
|
convertCellRef(cellref, objstate);
|
||||||
|
|
||||||
objstate.mCreatureStats.mActorId = mContext->generateActorId();
|
|
||||||
mContext->mActorIdMap.insert(std::make_pair(std::make_pair(refIndex, out.mRefID), objstate.mCreatureStats.mActorId));
|
|
||||||
|
|
||||||
esm.writeHNT ("OBJE", ESM::REC_NPC_);
|
esm.writeHNT ("OBJE", ESM::REC_NPC_);
|
||||||
objstate.save(esm);
|
objstate.save(esm);
|
||||||
continue;
|
continue;
|
||||||
|
@ -415,10 +380,6 @@ namespace ESSImport
|
||||||
convertACSC(cellref.mACSC, objstate.mCreatureStats);
|
convertACSC(cellref.mACSC, objstate.mCreatureStats);
|
||||||
convertCREC(crecIt->second, objstate);
|
convertCREC(crecIt->second, objstate);
|
||||||
convertCellRef(cellref, objstate);
|
convertCellRef(cellref, objstate);
|
||||||
|
|
||||||
objstate.mCreatureStats.mActorId = mContext->generateActorId();
|
|
||||||
mContext->mActorIdMap.insert(std::make_pair(std::make_pair(refIndex, out.mRefID), objstate.mCreatureStats.mActorId));
|
|
||||||
|
|
||||||
esm.writeHNT ("OBJE", ESM::REC_CREA);
|
esm.writeHNT ("OBJE", ESM::REC_CREA);
|
||||||
objstate.save(esm);
|
objstate.save(esm);
|
||||||
continue;
|
continue;
|
||||||
|
@ -449,73 +410,4 @@ namespace ESSImport
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConvertPROJ::read(ESM::ESMReader& esm)
|
|
||||||
{
|
|
||||||
mProj.load(esm);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConvertPROJ::write(ESM::ESMWriter& esm)
|
|
||||||
{
|
|
||||||
for (const PROJ::PNAM& pnam : mProj.mProjectiles)
|
|
||||||
{
|
|
||||||
if (!pnam.isMagic())
|
|
||||||
{
|
|
||||||
ESM::ProjectileState out;
|
|
||||||
convertBaseState(out, pnam);
|
|
||||||
|
|
||||||
out.mBowId = pnam.mBowId.toString();
|
|
||||||
out.mVelocity = pnam.mVelocity;
|
|
||||||
out.mAttackStrength = pnam.mAttackStrength;
|
|
||||||
|
|
||||||
esm.startRecord(ESM::REC_PROJ);
|
|
||||||
out.save(esm);
|
|
||||||
esm.endRecord(ESM::REC_PROJ);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ESM::MagicBoltState out;
|
|
||||||
convertBaseState(out, pnam);
|
|
||||||
|
|
||||||
auto it = std::find_if(mContext->mActiveSpells.begin(), mContext->mActiveSpells.end(),
|
|
||||||
[&pnam](const SPLM::ActiveSpell& spell) -> bool { return spell.mIndex == pnam.mSplmIndex; });
|
|
||||||
|
|
||||||
if (it == mContext->mActiveSpells.end())
|
|
||||||
{
|
|
||||||
std::cerr << "Warning: Skipped conversion for magic projectile \"" << pnam.mArrowId.toString() << "\" (invalid spell link)" << std::endl;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
out.mSpellId = it->mSPDT.mId.toString();
|
|
||||||
out.mSpeed = pnam.mSpeed * 0.001f; // not sure where this factor comes from
|
|
||||||
|
|
||||||
esm.startRecord(ESM::REC_MPRJ);
|
|
||||||
out.save(esm);
|
|
||||||
esm.endRecord(ESM::REC_MPRJ);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConvertPROJ::convertBaseState(ESM::BaseProjectileState& base, const PROJ::PNAM& pnam)
|
|
||||||
{
|
|
||||||
base.mId = pnam.mArrowId.toString();
|
|
||||||
base.mPosition = pnam.mPosition;
|
|
||||||
|
|
||||||
osg::Quat orient;
|
|
||||||
orient.makeRotate(osg::Vec3f(0,1,0), pnam.mVelocity);
|
|
||||||
base.mOrientation = orient;
|
|
||||||
|
|
||||||
base.mActorId = convertActorId(pnam.mActorId.toString(), *mContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConvertSPLM::read(ESM::ESMReader& esm)
|
|
||||||
{
|
|
||||||
mSPLM.load(esm);
|
|
||||||
mContext->mActiveSpells = mSPLM.mActiveSpells;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConvertSPLM::write(ESM::ESMWriter& esm)
|
|
||||||
{
|
|
||||||
std::cerr << "Warning: Skipped active spell conversion (not implemented)" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
#include <components/esm/globalscript.hpp>
|
#include <components/esm/globalscript.hpp>
|
||||||
#include <components/esm/queststate.hpp>
|
#include <components/esm/queststate.hpp>
|
||||||
#include <components/esm/stolenitems.hpp>
|
#include <components/esm/stolenitems.hpp>
|
||||||
#include <components/esm/projectilestate.hpp>
|
|
||||||
|
|
||||||
#include "importcrec.hpp"
|
#include "importcrec.hpp"
|
||||||
#include "importcntc.hpp"
|
#include "importcntc.hpp"
|
||||||
|
@ -36,8 +35,6 @@
|
||||||
#include "importques.hpp"
|
#include "importques.hpp"
|
||||||
#include "importjour.hpp"
|
#include "importjour.hpp"
|
||||||
#include "importscpt.hpp"
|
#include "importscpt.hpp"
|
||||||
#include "importproj.h"
|
|
||||||
#include "importsplm.h"
|
|
||||||
|
|
||||||
#include "convertacdt.hpp"
|
#include "convertacdt.hpp"
|
||||||
#include "convertnpcc.hpp"
|
#include "convertnpcc.hpp"
|
||||||
|
@ -122,9 +119,9 @@ public:
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt.mLevel;
|
mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel;
|
||||||
mContext->mPlayerBase = npc;
|
mContext->mPlayerBase = npc;
|
||||||
ESM::SpellState::SpellParams empty;
|
std::map<int, float> empty;
|
||||||
// FIXME: player start spells and birthsign spells aren't listed here,
|
// FIXME: player start spells and birthsign spells aren't listed here,
|
||||||
// need to fix openmw to account for this
|
// need to fix openmw to account for this
|
||||||
for (std::vector<std::string>::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it)
|
for (std::vector<std::string>::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it)
|
||||||
|
@ -205,7 +202,7 @@ public:
|
||||||
bool isDeleted = false;
|
bool isDeleted = false;
|
||||||
|
|
||||||
book.load(esm, isDeleted);
|
book.load(esm, isDeleted);
|
||||||
if (book.mData.mSkillId == -1)
|
if (book.mData.mSkillID == -1)
|
||||||
mContext->mPlayer.mObject.mNpcStats.mUsedIds.push_back(Misc::StringUtils::lowerCase(book.mId));
|
mContext->mPlayer.mObject.mNpcStats.mUsedIds.push_back(Misc::StringUtils::lowerCase(book.mId));
|
||||||
|
|
||||||
mRecords[book.mId] = book;
|
mRecords[book.mId] = book;
|
||||||
|
@ -274,34 +271,23 @@ private:
|
||||||
class ConvertPCDT : public Converter
|
class ConvertPCDT : public Converter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ConvertPCDT()
|
ConvertPCDT() : mFirstPersonCam(true) {}
|
||||||
: mFirstPersonCam(true),
|
|
||||||
mTeleportingEnabled(true),
|
|
||||||
mLevitationEnabled(true)
|
|
||||||
{}
|
|
||||||
|
|
||||||
virtual void read(ESM::ESMReader &esm)
|
virtual void read(ESM::ESMReader &esm)
|
||||||
{
|
{
|
||||||
PCDT pcdt;
|
PCDT pcdt;
|
||||||
pcdt.load(esm);
|
pcdt.load(esm);
|
||||||
|
|
||||||
convertPCDT(pcdt, mContext->mPlayer, mContext->mDialogueState.mKnownTopics, mFirstPersonCam, mTeleportingEnabled, mLevitationEnabled, mContext->mControlsState);
|
convertPCDT(pcdt, mContext->mPlayer, mContext->mDialogueState.mKnownTopics, mFirstPersonCam);
|
||||||
}
|
}
|
||||||
virtual void write(ESM::ESMWriter &esm)
|
virtual void write(ESM::ESMWriter &esm)
|
||||||
{
|
{
|
||||||
esm.startRecord(ESM::REC_ENAB);
|
|
||||||
esm.writeHNT("TELE", mTeleportingEnabled);
|
|
||||||
esm.writeHNT("LEVT", mLevitationEnabled);
|
|
||||||
esm.endRecord(ESM::REC_ENAB);
|
|
||||||
|
|
||||||
esm.startRecord(ESM::REC_CAM_);
|
esm.startRecord(ESM::REC_CAM_);
|
||||||
esm.writeHNT("FIRS", mFirstPersonCam);
|
esm.writeHNT("FIRS", mFirstPersonCam);
|
||||||
esm.endRecord(ESM::REC_CAM_);
|
esm.endRecord(ESM::REC_CAM_);
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
bool mFirstPersonCam;
|
bool mFirstPersonCam;
|
||||||
bool mTeleportingEnabled;
|
|
||||||
bool mLevitationEnabled;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ConvertCNTC : public Converter
|
class ConvertCNTC : public Converter
|
||||||
|
@ -596,27 +582,6 @@ private:
|
||||||
std::vector<ESM::GlobalScript> mScripts;
|
std::vector<ESM::GlobalScript> mScripts;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Projectile converter
|
|
||||||
class ConvertPROJ : public Converter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual int getStage() override { return 2; }
|
|
||||||
virtual void read(ESM::ESMReader& esm) override;
|
|
||||||
virtual void write(ESM::ESMWriter& esm) override;
|
|
||||||
private:
|
|
||||||
void convertBaseState(ESM::BaseProjectileState& base, const PROJ::PNAM& pnam);
|
|
||||||
PROJ mProj;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ConvertSPLM : public Converter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual void read(ESM::ESMReader& esm) override;
|
|
||||||
virtual void write(ESM::ESMWriter& esm) override;
|
|
||||||
private:
|
|
||||||
SPLM mSPLM;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
namespace ESSImport
|
namespace ESSImport
|
||||||
{
|
{
|
||||||
|
|
||||||
void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector<std::string>& outDialogueTopics, bool& firstPersonCam, bool& teleportingEnabled, bool& levitationEnabled, ESM::ControlsState& controls)
|
void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector<std::string>& outDialogueTopics, bool& firstPersonCam)
|
||||||
{
|
{
|
||||||
out.mBirthsign = pcdt.mBirthsign;
|
out.mBirthsign = pcdt.mBirthsign;
|
||||||
out.mObject.mNpcStats.mBounty = pcdt.mBounty;
|
out.mObject.mNpcStats.mBounty = pcdt.mBounty;
|
||||||
|
@ -17,72 +17,24 @@ namespace ESSImport
|
||||||
faction.mReputation = it->mReputation;
|
faction.mReputation = it->mReputation;
|
||||||
out.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(it->mFactionName.toString())] = faction;
|
out.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(it->mFactionName.toString())] = faction;
|
||||||
}
|
}
|
||||||
for (int i=0; i<3; ++i)
|
|
||||||
out.mObject.mNpcStats.mSpecIncreases[i] = pcdt.mPNAM.mSpecIncreases[i];
|
|
||||||
for (int i=0; i<8; ++i)
|
for (int i=0; i<8; ++i)
|
||||||
out.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i];
|
out.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i];
|
||||||
for (int i=0; i<27; ++i)
|
for (int i=0; i<27; ++i)
|
||||||
out.mObject.mNpcStats.mSkills[i].mProgress = pcdt.mPNAM.mSkillProgress[i];
|
out.mObject.mNpcStats.mSkills[i].mProgress = pcdt.mPNAM.mSkillProgress[i];
|
||||||
out.mObject.mNpcStats.mLevelProgress = pcdt.mPNAM.mLevelProgress;
|
out.mObject.mNpcStats.mLevelProgress = pcdt.mPNAM.mLevelProgress;
|
||||||
|
|
||||||
if (pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_WeaponDrawn)
|
if (pcdt.mPNAM.mDrawState & PCDT::DrawState_Weapon)
|
||||||
out.mObject.mCreatureStats.mDrawState = 1;
|
out.mObject.mCreatureStats.mDrawState = 1;
|
||||||
if (pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_SpellDrawn)
|
if (pcdt.mPNAM.mDrawState & PCDT::DrawState_Spell)
|
||||||
out.mObject.mCreatureStats.mDrawState = 2;
|
out.mObject.mCreatureStats.mDrawState = 2;
|
||||||
|
|
||||||
firstPersonCam = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ThirdPerson);
|
firstPersonCam = (pcdt.mPNAM.mCameraState == PCDT::CameraState_FirstPerson);
|
||||||
teleportingEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_TeleportingDisabled);
|
|
||||||
levitationEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_LevitationDisabled);
|
|
||||||
|
|
||||||
for (std::vector<std::string>::const_iterator it = pcdt.mKnownDialogueTopics.begin();
|
for (std::vector<std::string>::const_iterator it = pcdt.mKnownDialogueTopics.begin();
|
||||||
it != pcdt.mKnownDialogueTopics.end(); ++it)
|
it != pcdt.mKnownDialogueTopics.end(); ++it)
|
||||||
{
|
{
|
||||||
outDialogueTopics.push_back(Misc::StringUtils::lowerCase(*it));
|
outDialogueTopics.push_back(Misc::StringUtils::lowerCase(*it));
|
||||||
}
|
}
|
||||||
|
|
||||||
controls.mViewSwitchDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ViewSwitchDisabled;
|
|
||||||
controls.mControlsDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ControlsDisabled;
|
|
||||||
controls.mJumpingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_JumpingDisabled;
|
|
||||||
controls.mLookingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_LookingDisabled;
|
|
||||||
controls.mVanityModeDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_VanityModeDisabled;
|
|
||||||
controls.mWeaponDrawingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_WeaponDrawingDisabled;
|
|
||||||
controls.mSpellDrawingDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_SpellDrawingDisabled;
|
|
||||||
|
|
||||||
if (pcdt.mHasMark)
|
|
||||||
{
|
|
||||||
out.mHasMark = 1;
|
|
||||||
|
|
||||||
const PCDT::PNAM::MarkLocation& mark = pcdt.mPNAM.mMarkLocation;
|
|
||||||
|
|
||||||
ESM::CellId cell;
|
|
||||||
cell.mWorldspace = ESM::CellId::sDefaultWorldspace;
|
|
||||||
cell.mPaged = true;
|
|
||||||
|
|
||||||
cell.mIndex.mX = mark.mCellX;
|
|
||||||
cell.mIndex.mY = mark.mCellY;
|
|
||||||
|
|
||||||
// TODO: Figure out a better way to detect interiors. (0, 0) is a valid exterior cell.
|
|
||||||
if (mark.mCellX == 0 && mark.mCellY == 0)
|
|
||||||
{
|
|
||||||
cell.mWorldspace = pcdt.mMNAM;
|
|
||||||
cell.mPaged = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
out.mMarkedCell = cell;
|
|
||||||
out.mMarkedPosition.pos[0] = mark.mX;
|
|
||||||
out.mMarkedPosition.pos[1] = mark.mY;
|
|
||||||
out.mMarkedPosition.pos[2] = mark.mZ;
|
|
||||||
out.mMarkedPosition.rot[0] = out.mMarkedPosition.rot[1] = 0.0f;
|
|
||||||
out.mMarkedPosition.rot[2] = mark.mRotZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pcdt.mHasENAM)
|
|
||||||
{
|
|
||||||
const int cellSize = 8192;
|
|
||||||
out.mLastKnownExteriorPosition[0] = (pcdt.mENAM.mCellX + 0.5f) * cellSize;
|
|
||||||
out.mLastKnownExteriorPosition[1] = (pcdt.mENAM.mCellY + 0.5f) * cellSize;
|
|
||||||
out.mLastKnownExteriorPosition[2] = 0.0f;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,11 @@
|
||||||
#include "importplayer.hpp"
|
#include "importplayer.hpp"
|
||||||
|
|
||||||
#include <components/esm/player.hpp>
|
#include <components/esm/player.hpp>
|
||||||
#include <components/esm/controlsstate.hpp>
|
|
||||||
|
|
||||||
namespace ESSImport
|
namespace ESSImport
|
||||||
{
|
{
|
||||||
|
|
||||||
void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector<std::string>& outDialogueTopics, bool& firstPersonCam, bool& teleportingEnabled, bool& levitationEnabled, ESM::ControlsState& controls);
|
void convertPCDT(const PCDT& pcdt, ESM::Player& out, std::vector<std::string>& outDialogueTopics, bool& firstPersonCam);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ namespace ESSImport
|
||||||
// unsure at which point between TGTN and CRED
|
// unsure at which point between TGTN and CRED
|
||||||
if (esm.isNextSub("AADT"))
|
if (esm.isNextSub("AADT"))
|
||||||
{
|
{
|
||||||
// occurred when a creature was in the middle of its attack, 44 bytes
|
// occured when a creature was in the middle of its attack, 44 bytes
|
||||||
esm.skipHSub();
|
esm.skipHSub();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,13 +123,8 @@ namespace ESSImport
|
||||||
|
|
||||||
if (esm.isNextSub("ND3D"))
|
if (esm.isNextSub("ND3D"))
|
||||||
esm.skipHSub();
|
esm.skipHSub();
|
||||||
|
|
||||||
mHasANIS = false;
|
|
||||||
if (esm.isNextSub("ANIS"))
|
if (esm.isNextSub("ANIS"))
|
||||||
{
|
esm.skipHSub();
|
||||||
mHasANIS = true;
|
|
||||||
esm.getHT(mANIS);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,12 +55,6 @@ namespace ESSImport
|
||||||
unsigned char mCorpseClearCountdown; // hours?
|
unsigned char mCorpseClearCountdown; // hours?
|
||||||
unsigned char mUnknown3[71];
|
unsigned char mUnknown3[71];
|
||||||
};
|
};
|
||||||
struct ANIS
|
|
||||||
{
|
|
||||||
unsigned char mGroupIndex;
|
|
||||||
unsigned char mUnknown[3];
|
|
||||||
float mTime;
|
|
||||||
};
|
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
struct ActorData : public ESM::CellRef
|
struct ActorData : public ESM::CellRef
|
||||||
|
@ -83,9 +77,6 @@ namespace ESSImport
|
||||||
|
|
||||||
SCRI mSCRI;
|
SCRI mSCRI;
|
||||||
|
|
||||||
bool mHasANIS;
|
|
||||||
ANIS mANIS; // scripted animation state
|
|
||||||
|
|
||||||
void load(ESM::ESMReader& esm);
|
void load(ESM::ESMReader& esm);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
|
||||||
#include <boost/filesystem/fstream.hpp>
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
#include <osgDB/ReadFile>
|
#include <osgDB/ReadFile>
|
||||||
#include <osg/ImageUtils>
|
#include <osg/ImageUtils>
|
||||||
|
@ -38,7 +38,7 @@ namespace
|
||||||
{
|
{
|
||||||
if (fileHeader.mSCRS.size() != 128*128*4)
|
if (fileHeader.mSCRS.size() != 128*128*4)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: unexpected screenshot size " << std::endl;
|
std::cerr << "unexpected screenshot size " << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,6 @@ namespace
|
||||||
{
|
{
|
||||||
for (int x=0; x<128; ++x)
|
for (int x=0; x<128; ++x)
|
||||||
{
|
{
|
||||||
assert(image->data(x,y));
|
|
||||||
*(image->data(x,y)+2) = *it++;
|
*(image->data(x,y)+2) = *it++;
|
||||||
*(image->data(x,y)+1) = *it++;
|
*(image->data(x,y)+1) = *it++;
|
||||||
*image->data(x,y) = *it++;
|
*image->data(x,y) = *it++;
|
||||||
|
@ -66,14 +65,14 @@ namespace
|
||||||
osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("jpg");
|
osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("jpg");
|
||||||
if (!readerwriter)
|
if (!readerwriter)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: can't write screenshot: no jpg readerwriter found" << std::endl;
|
std::cerr << "can't write screenshot: no jpg readerwriter found" << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*image, ostream);
|
osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*image, ostream);
|
||||||
if (!result.success())
|
if (!result.success())
|
||||||
{
|
{
|
||||||
std::cerr << "Error: can't write screenshot: " << result.message() << " code " << result.status() << std::endl;
|
std::cerr << "can't write screenshot: " << result.message() << " code " << result.status() << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,50 +270,48 @@ namespace ESSImport
|
||||||
const unsigned int recSTLN = ESM::FourCC<'S','T','L','N'>::value;
|
const unsigned int recSTLN = ESM::FourCC<'S','T','L','N'>::value;
|
||||||
const unsigned int recGAME = ESM::FourCC<'G','A','M','E'>::value;
|
const unsigned int recGAME = ESM::FourCC<'G','A','M','E'>::value;
|
||||||
const unsigned int recJOUR = ESM::FourCC<'J','O','U','R'>::value;
|
const unsigned int recJOUR = ESM::FourCC<'J','O','U','R'>::value;
|
||||||
const unsigned int recSPLM = ESM::FourCC<'S','P','L','M'>::value;
|
|
||||||
|
|
||||||
std::map<unsigned int, std::shared_ptr<Converter> > converters;
|
std::map<unsigned int, boost::shared_ptr<Converter> > converters;
|
||||||
converters[ESM::REC_GLOB] = std::shared_ptr<Converter>(new ConvertGlobal());
|
converters[ESM::REC_GLOB] = boost::shared_ptr<Converter>(new ConvertGlobal());
|
||||||
converters[ESM::REC_BOOK] = std::shared_ptr<Converter>(new ConvertBook());
|
converters[ESM::REC_BOOK] = boost::shared_ptr<Converter>(new ConvertBook());
|
||||||
converters[ESM::REC_NPC_] = std::shared_ptr<Converter>(new ConvertNPC());
|
converters[ESM::REC_NPC_] = boost::shared_ptr<Converter>(new ConvertNPC());
|
||||||
converters[ESM::REC_CREA] = std::shared_ptr<Converter>(new ConvertCREA());
|
converters[ESM::REC_CREA] = boost::shared_ptr<Converter>(new ConvertCREA());
|
||||||
converters[ESM::REC_NPCC] = std::shared_ptr<Converter>(new ConvertNPCC());
|
converters[ESM::REC_NPCC] = boost::shared_ptr<Converter>(new ConvertNPCC());
|
||||||
converters[ESM::REC_CREC] = std::shared_ptr<Converter>(new ConvertCREC());
|
converters[ESM::REC_CREC] = boost::shared_ptr<Converter>(new ConvertCREC());
|
||||||
converters[recREFR ] = std::shared_ptr<Converter>(new ConvertREFR());
|
converters[recREFR ] = boost::shared_ptr<Converter>(new ConvertREFR());
|
||||||
converters[recPCDT ] = std::shared_ptr<Converter>(new ConvertPCDT());
|
converters[recPCDT ] = boost::shared_ptr<Converter>(new ConvertPCDT());
|
||||||
converters[recFMAP ] = std::shared_ptr<Converter>(new ConvertFMAP());
|
converters[recFMAP ] = boost::shared_ptr<Converter>(new ConvertFMAP());
|
||||||
converters[recKLST ] = std::shared_ptr<Converter>(new ConvertKLST());
|
converters[recKLST ] = boost::shared_ptr<Converter>(new ConvertKLST());
|
||||||
converters[recSTLN ] = std::shared_ptr<Converter>(new ConvertSTLN());
|
converters[recSTLN ] = boost::shared_ptr<Converter>(new ConvertSTLN());
|
||||||
converters[recGAME ] = std::shared_ptr<Converter>(new ConvertGAME());
|
converters[recGAME ] = boost::shared_ptr<Converter>(new ConvertGAME());
|
||||||
converters[ESM::REC_CELL] = std::shared_ptr<Converter>(new ConvertCell());
|
converters[ESM::REC_CELL] = boost::shared_ptr<Converter>(new ConvertCell());
|
||||||
converters[ESM::REC_ALCH] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Potion>());
|
converters[ESM::REC_ALCH] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Potion>());
|
||||||
converters[ESM::REC_CLAS] = std::shared_ptr<Converter>(new ConvertClass());
|
converters[ESM::REC_CLAS] = boost::shared_ptr<Converter>(new ConvertClass());
|
||||||
converters[ESM::REC_SPEL] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Spell>());
|
converters[ESM::REC_SPEL] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Spell>());
|
||||||
converters[ESM::REC_ARMO] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Armor>());
|
converters[ESM::REC_ARMO] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Armor>());
|
||||||
converters[ESM::REC_WEAP] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Weapon>());
|
converters[ESM::REC_WEAP] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Weapon>());
|
||||||
converters[ESM::REC_CLOT] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Clothing>());
|
converters[ESM::REC_CLOT] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Clothing>());
|
||||||
converters[ESM::REC_ENCH] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Enchantment>());
|
converters[ESM::REC_ENCH] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Enchantment>());
|
||||||
converters[ESM::REC_WEAP] = std::shared_ptr<Converter>(new DefaultConverter<ESM::Weapon>());
|
converters[ESM::REC_WEAP] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Weapon>());
|
||||||
converters[ESM::REC_LEVC] = std::shared_ptr<Converter>(new DefaultConverter<ESM::CreatureLevList>());
|
converters[ESM::REC_LEVC] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::CreatureLevList>());
|
||||||
converters[ESM::REC_LEVI] = std::shared_ptr<Converter>(new DefaultConverter<ESM::ItemLevList>());
|
converters[ESM::REC_LEVI] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::ItemLevList>());
|
||||||
converters[ESM::REC_CNTC] = std::shared_ptr<Converter>(new ConvertCNTC());
|
converters[ESM::REC_CNTC] = boost::shared_ptr<Converter>(new ConvertCNTC());
|
||||||
converters[ESM::REC_FACT] = std::shared_ptr<Converter>(new ConvertFACT());
|
converters[ESM::REC_FACT] = boost::shared_ptr<Converter>(new ConvertFACT());
|
||||||
converters[ESM::REC_INFO] = std::shared_ptr<Converter>(new ConvertINFO());
|
converters[ESM::REC_INFO] = boost::shared_ptr<Converter>(new ConvertINFO());
|
||||||
converters[ESM::REC_DIAL] = std::shared_ptr<Converter>(new ConvertDIAL());
|
converters[ESM::REC_DIAL] = boost::shared_ptr<Converter>(new ConvertDIAL());
|
||||||
converters[ESM::REC_QUES] = std::shared_ptr<Converter>(new ConvertQUES());
|
converters[ESM::REC_QUES] = boost::shared_ptr<Converter>(new ConvertQUES());
|
||||||
converters[recJOUR ] = std::shared_ptr<Converter>(new ConvertJOUR());
|
converters[recJOUR ] = boost::shared_ptr<Converter>(new ConvertJOUR());
|
||||||
converters[ESM::REC_SCPT] = std::shared_ptr<Converter>(new ConvertSCPT());
|
converters[ESM::REC_SCPT] = boost::shared_ptr<Converter>(new ConvertSCPT());
|
||||||
converters[ESM::REC_PROJ] = std::shared_ptr<Converter>(new ConvertPROJ());
|
|
||||||
converters[recSPLM] = std::shared_ptr<Converter>(new ConvertSPLM());
|
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// - REGN (weather in certain regions?)
|
// - REGN (weather in certain regions?)
|
||||||
// - VFXM
|
// - VFXM
|
||||||
// - SPLM (active spell effects)
|
// - SPLM (active spell effects)
|
||||||
|
// - PROJ (magic projectiles in air)
|
||||||
|
|
||||||
std::set<unsigned int> unknownRecords;
|
std::set<unsigned int> unknownRecords;
|
||||||
|
|
||||||
for (std::map<unsigned int, std::shared_ptr<Converter> >::const_iterator it = converters.begin();
|
for (std::map<unsigned int, boost::shared_ptr<Converter> >::const_iterator it = converters.begin();
|
||||||
it != converters.end(); ++it)
|
it != converters.end(); ++it)
|
||||||
{
|
{
|
||||||
it->second->setContext(context);
|
it->second->setContext(context);
|
||||||
|
@ -325,17 +322,17 @@ namespace ESSImport
|
||||||
ESM::NAME n = esm.getRecName();
|
ESM::NAME n = esm.getRecName();
|
||||||
esm.getRecHeader();
|
esm.getRecHeader();
|
||||||
|
|
||||||
std::map<unsigned int, std::shared_ptr<Converter> >::iterator it = converters.find(n.intval);
|
std::map<unsigned int, boost::shared_ptr<Converter> >::iterator it = converters.find(n.val);
|
||||||
if (it != converters.end())
|
if (it != converters.end())
|
||||||
{
|
{
|
||||||
it->second->read(esm);
|
it->second->read(esm);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (unknownRecords.insert(n.intval).second)
|
if (unknownRecords.insert(n.val).second)
|
||||||
{
|
{
|
||||||
std::ios::fmtflags f(std::cerr.flags());
|
std::ios::fmtflags f(std::cerr.flags());
|
||||||
std::cerr << "Error: unknown record " << n.toString() << " (0x" << std::hex << esm.getFileOffset() << ")" << std::endl;
|
std::cerr << "unknown record " << n.toString() << " (0x" << std::hex << esm.getFileOffset() << ")" << std::endl;
|
||||||
std::cerr.flags(f);
|
std::cerr.flags(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,7 +374,7 @@ namespace ESSImport
|
||||||
profile.mPlayerClassName = context.mCustomPlayerClassName;
|
profile.mPlayerClassName = context.mCustomPlayerClassName;
|
||||||
else
|
else
|
||||||
profile.mPlayerClassId = context.mPlayerBase.mClass;
|
profile.mPlayerClassId = context.mPlayerBase.mClass;
|
||||||
profile.mPlayerLevel = context.mPlayerBase.mNpdt.mLevel;
|
profile.mPlayerLevel = context.mPlayerBase.mNpdt52.mLevel;
|
||||||
profile.mPlayerName = header.mGameData.mPlayerName.toString();
|
profile.mPlayerName = header.mGameData.mPlayerName.toString();
|
||||||
|
|
||||||
writeScreenshot(header, profile);
|
writeScreenshot(header, profile);
|
||||||
|
@ -388,7 +385,7 @@ namespace ESSImport
|
||||||
|
|
||||||
// Writing order should be Dynamic Store -> Cells -> Player,
|
// Writing order should be Dynamic Store -> Cells -> Player,
|
||||||
// so that references to dynamic records can be recognized when loading
|
// so that references to dynamic records can be recognized when loading
|
||||||
for (std::map<unsigned int, std::shared_ptr<Converter> >::const_iterator it = converters.begin();
|
for (std::map<unsigned int, boost::shared_ptr<Converter> >::const_iterator it = converters.begin();
|
||||||
it != converters.end(); ++it)
|
it != converters.end(); ++it)
|
||||||
{
|
{
|
||||||
if (it->second->getStage() != 0)
|
if (it->second->getStage() != 0)
|
||||||
|
@ -401,7 +398,7 @@ namespace ESSImport
|
||||||
context.mPlayerBase.save(writer);
|
context.mPlayerBase.save(writer);
|
||||||
writer.endRecord(ESM::REC_NPC_);
|
writer.endRecord(ESM::REC_NPC_);
|
||||||
|
|
||||||
for (std::map<unsigned int, std::shared_ptr<Converter> >::const_iterator it = converters.begin();
|
for (std::map<unsigned int, boost::shared_ptr<Converter> >::const_iterator it = converters.begin();
|
||||||
it != converters.end(); ++it)
|
it != converters.end(); ++it)
|
||||||
{
|
{
|
||||||
if (it->second->getStage() != 1)
|
if (it->second->getStage() != 1)
|
||||||
|
@ -422,26 +419,9 @@ namespace ESSImport
|
||||||
context.mPlayer.save(writer);
|
context.mPlayer.save(writer);
|
||||||
writer.endRecord(ESM::REC_PLAY);
|
writer.endRecord(ESM::REC_PLAY);
|
||||||
|
|
||||||
writer.startRecord(ESM::REC_ACTC);
|
|
||||||
writer.writeHNT("COUN", context.mNextActorId);
|
|
||||||
writer.endRecord(ESM::REC_ACTC);
|
|
||||||
|
|
||||||
// Stage 2 requires cell references to be written / actors IDs assigned
|
|
||||||
for (std::map<unsigned int, std::shared_ptr<Converter> >::const_iterator it = converters.begin();
|
|
||||||
it != converters.end(); ++it)
|
|
||||||
{
|
|
||||||
if (it->second->getStage() != 2)
|
|
||||||
continue;
|
|
||||||
it->second->write(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.startRecord (ESM::REC_DIAS);
|
writer.startRecord (ESM::REC_DIAS);
|
||||||
context.mDialogueState.save(writer);
|
context.mDialogueState.save(writer);
|
||||||
writer.endRecord(ESM::REC_DIAS);
|
writer.endRecord(ESM::REC_DIAS);
|
||||||
|
|
||||||
writer.startRecord(ESM::REC_INPU);
|
|
||||||
context.mControlsState.save(writer);
|
|
||||||
writer.endRecord(ESM::REC_INPU);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,13 +9,12 @@
|
||||||
#include <components/esm/globalmap.hpp>
|
#include <components/esm/globalmap.hpp>
|
||||||
#include <components/esm/loadcrea.hpp>
|
#include <components/esm/loadcrea.hpp>
|
||||||
#include <components/esm/loadnpc.hpp>
|
#include <components/esm/loadnpc.hpp>
|
||||||
#include <components/esm/controlsstate.hpp>
|
|
||||||
|
|
||||||
#include "importnpcc.hpp"
|
#include "importnpcc.hpp"
|
||||||
#include "importcrec.hpp"
|
#include "importcrec.hpp"
|
||||||
#include "importcntc.hpp"
|
#include "importcntc.hpp"
|
||||||
#include "importplayer.hpp"
|
#include "importplayer.hpp"
|
||||||
#include "importsplm.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,8 +32,6 @@ namespace ESSImport
|
||||||
|
|
||||||
ESM::DialogueState mDialogueState;
|
ESM::DialogueState mDialogueState;
|
||||||
|
|
||||||
ESM::ControlsState mControlsState;
|
|
||||||
|
|
||||||
// cells which should show an explored overlay on the global map
|
// cells which should show an explored overlay on the global map
|
||||||
std::set<std::pair<int, int> > mExploredCells;
|
std::set<std::pair<int, int> > mExploredCells;
|
||||||
|
|
||||||
|
@ -48,48 +45,31 @@ namespace ESSImport
|
||||||
std::map<std::pair<int, std::string>, NPCC> mNpcChanges;
|
std::map<std::pair<int, std::string>, NPCC> mNpcChanges;
|
||||||
std::map<std::pair<int, std::string>, CNTC> mContainerChanges;
|
std::map<std::pair<int, std::string>, CNTC> mContainerChanges;
|
||||||
|
|
||||||
std::map<std::pair<int, std::string>, int> mActorIdMap;
|
|
||||||
int mNextActorId;
|
|
||||||
|
|
||||||
std::map<std::string, ESM::Creature> mCreatures;
|
std::map<std::string, ESM::Creature> mCreatures;
|
||||||
std::map<std::string, ESM::NPC> mNpcs;
|
std::map<std::string, ESM::NPC> mNpcs;
|
||||||
|
|
||||||
std::vector<SPLM::ActiveSpell> mActiveSpells;
|
|
||||||
|
|
||||||
Context()
|
Context()
|
||||||
: mDay(0)
|
: mDay(0)
|
||||||
, mMonth(0)
|
, mMonth(0)
|
||||||
, mYear(0)
|
, mYear(0)
|
||||||
, mHour(0.f)
|
, mHour(0.f)
|
||||||
, mNextActorId(0)
|
|
||||||
{
|
{
|
||||||
mPlayer.mAutoMove = 0;
|
mPlayer.mAutoMove = 0;
|
||||||
ESM::CellId playerCellId;
|
ESM::CellId playerCellId;
|
||||||
playerCellId.mPaged = true;
|
playerCellId.mPaged = true;
|
||||||
playerCellId.mIndex.mX = playerCellId.mIndex.mY = 0;
|
playerCellId.mIndex.mX = playerCellId.mIndex.mY = 0;
|
||||||
mPlayer.mCellId = playerCellId;
|
mPlayer.mCellId = playerCellId;
|
||||||
mPlayer.mLastKnownExteriorPosition[0]
|
//mPlayer.mLastKnownExteriorPosition
|
||||||
= mPlayer.mLastKnownExteriorPosition[1]
|
mPlayer.mHasMark = 0; // TODO
|
||||||
= mPlayer.mLastKnownExteriorPosition[2]
|
mPlayer.mCurrentCrimeId = 0; // TODO
|
||||||
= 0.0f;
|
|
||||||
mPlayer.mHasMark = 0;
|
|
||||||
mPlayer.mCurrentCrimeId = -1; // TODO
|
|
||||||
mPlayer.mPaidCrimeId = -1;
|
|
||||||
mPlayer.mObject.blank();
|
mPlayer.mObject.blank();
|
||||||
mPlayer.mObject.mEnabled = true;
|
|
||||||
mPlayer.mObject.mRef.mRefID = "player"; // REFR.mRefID would be PlayerSaveGame
|
mPlayer.mObject.mRef.mRefID = "player"; // REFR.mRefID would be PlayerSaveGame
|
||||||
mPlayer.mObject.mCreatureStats.mActorId = generateActorId();
|
|
||||||
|
|
||||||
mGlobalMapState.mBounds.mMinX = 0;
|
mGlobalMapState.mBounds.mMinX = 0;
|
||||||
mGlobalMapState.mBounds.mMaxX = 0;
|
mGlobalMapState.mBounds.mMaxX = 0;
|
||||||
mGlobalMapState.mBounds.mMinY = 0;
|
mGlobalMapState.mBounds.mMinY = 0;
|
||||||
mGlobalMapState.mBounds.mMaxY = 0;
|
mGlobalMapState.mBounds.mMaxY = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int generateActorId()
|
|
||||||
{
|
|
||||||
return mNextActorId++;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,20 +20,14 @@ namespace ESSImport
|
||||||
item.mId = contItem.mItem.toString();
|
item.mId = contItem.mItem.toString();
|
||||||
item.mCount = contItem.mCount;
|
item.mCount = contItem.mCount;
|
||||||
item.mRelativeEquipmentSlot = -1;
|
item.mRelativeEquipmentSlot = -1;
|
||||||
item.mLockLevel = 0;
|
|
||||||
|
|
||||||
unsigned int itemCount = std::abs(item.mCount);
|
// seems that a stack of items can have a set of subrecords for each item? rings0000.ess
|
||||||
bool separateStacks = false;
|
// doesn't make any sense to me, if the values were different then the items shouldn't stack in the first place?
|
||||||
for (unsigned int i=0;i<itemCount;++i)
|
// I guess we should double check the stacking logic in OpenMW
|
||||||
|
for (int i=0;i<std::abs(item.mCount);++i)
|
||||||
{
|
{
|
||||||
bool newStack = esm.isNextSub("XIDX");
|
if (esm.isNextSub("XIDX")) // index in the stack?
|
||||||
if (newStack)
|
esm.skipHSub();
|
||||||
{
|
|
||||||
unsigned int idx;
|
|
||||||
esm.getHT(idx);
|
|
||||||
separateStacks = true;
|
|
||||||
item.mCount = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
item.mSCRI.load(esm);
|
item.mSCRI.load(esm);
|
||||||
|
|
||||||
|
@ -44,13 +38,9 @@ namespace ESSImport
|
||||||
int charge=-1;
|
int charge=-1;
|
||||||
esm.getHNOT(charge, "XHLT");
|
esm.getHNOT(charge, "XHLT");
|
||||||
item.mChargeInt = charge;
|
item.mChargeInt = charge;
|
||||||
|
|
||||||
if (newStack)
|
|
||||||
mItems.push_back(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!separateStacks)
|
mItems.push_back(item);
|
||||||
mItems.push_back(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// equipped items
|
// equipped items
|
||||||
|
|
|
@ -23,12 +23,9 @@ namespace ESSImport
|
||||||
mKnownDialogueTopics.push_back(esm.getHString());
|
mKnownDialogueTopics.push_back(esm.getHString());
|
||||||
}
|
}
|
||||||
|
|
||||||
mHasMark = false;
|
|
||||||
if (esm.isNextSub("MNAM"))
|
if (esm.isNextSub("MNAM"))
|
||||||
{
|
esm.skipHSub(); // If this field is here it seems to specify the interior cell the player is in,
|
||||||
mHasMark = true;
|
// but it's not always here, so it's kinda useless
|
||||||
mMNAM = esm.getHString();
|
|
||||||
}
|
|
||||||
|
|
||||||
esm.getHNT(mPNAM, "PNAM");
|
esm.getHNT(mPNAM, "PNAM");
|
||||||
|
|
||||||
|
@ -37,14 +34,6 @@ namespace ESSImport
|
||||||
if (esm.isNextSub("NAM9"))
|
if (esm.isNextSub("NAM9"))
|
||||||
esm.skipHSub();
|
esm.skipHSub();
|
||||||
|
|
||||||
// Rest state. You shouldn't even be able to save during rest, but skip just in case.
|
|
||||||
if (esm.isNextSub("RNAM"))
|
|
||||||
/*
|
|
||||||
int hoursLeft;
|
|
||||||
float x, y, z; // resting position
|
|
||||||
*/
|
|
||||||
esm.skipHSub(); // 16 bytes
|
|
||||||
|
|
||||||
mBounty = 0;
|
mBounty = 0;
|
||||||
esm.getHNOT(mBounty, "CNAM");
|
esm.getHNOT(mBounty, "CNAM");
|
||||||
|
|
||||||
|
@ -61,12 +50,8 @@ namespace ESSImport
|
||||||
if (esm.isNextSub("NAM3"))
|
if (esm.isNextSub("NAM3"))
|
||||||
esm.skipHSub();
|
esm.skipHSub();
|
||||||
|
|
||||||
mHasENAM = false;
|
|
||||||
if (esm.isNextSub("ENAM"))
|
if (esm.isNextSub("ENAM"))
|
||||||
{
|
esm.skipHSub();
|
||||||
mHasENAM = true;
|
|
||||||
esm.getHT(mENAM);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (esm.isNextSub("LNAM"))
|
if (esm.isNextSub("LNAM"))
|
||||||
esm.skipHSub();
|
esm.skipHSub();
|
||||||
|
@ -78,19 +63,12 @@ namespace ESSImport
|
||||||
mFactions.push_back(fnam);
|
mFactions.push_back(fnam);
|
||||||
}
|
}
|
||||||
|
|
||||||
mHasAADT = false;
|
if (esm.isNextSub("AADT"))
|
||||||
if (esm.isNextSub("AADT")) // Attack animation data?
|
esm.skipHSub(); // 44 bytes, no clue
|
||||||
{
|
|
||||||
mHasAADT = true;
|
|
||||||
esm.getHT(mAADT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (esm.isNextSub("KNAM"))
|
if (esm.isNextSub("KNAM"))
|
||||||
esm.skipHSub(); // assigned Quick Keys, I think
|
esm.skipHSub(); // assigned Quick Keys, I think
|
||||||
|
|
||||||
if (esm.isNextSub("ANIS"))
|
|
||||||
esm.skipHSub(); // 16 bytes
|
|
||||||
|
|
||||||
if (esm.isNextSub("WERE"))
|
if (esm.isNextSub("WERE"))
|
||||||
{
|
{
|
||||||
// some werewolf data, 152 bytes
|
// some werewolf data, 152 bytes
|
||||||
|
@ -98,6 +76,10 @@ namespace ESSImport
|
||||||
esm.getSubHeader();
|
esm.getSubHeader();
|
||||||
esm.skip(152);
|
esm.skip(152);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unsure if before or after WERE
|
||||||
|
if (esm.isNextSub("ANIS"))
|
||||||
|
esm.skipHSub();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,23 +38,15 @@ struct PCDT
|
||||||
|
|
||||||
std::vector<std::string> mKnownDialogueTopics;
|
std::vector<std::string> mKnownDialogueTopics;
|
||||||
|
|
||||||
enum PlayerFlags
|
enum DrawState_
|
||||||
{
|
{
|
||||||
PlayerFlags_ViewSwitchDisabled = 0x1,
|
DrawState_Weapon = 0x80,
|
||||||
PlayerFlags_ControlsDisabled = 0x4,
|
DrawState_Spell = 0x100
|
||||||
PlayerFlags_Sleeping = 0x10,
|
};
|
||||||
PlayerFlags_Waiting = 0x40,
|
enum CameraState
|
||||||
PlayerFlags_WeaponDrawn = 0x80,
|
{
|
||||||
PlayerFlags_SpellDrawn = 0x100,
|
CameraState_FirstPerson = 0x8,
|
||||||
PlayerFlags_InJail = 0x200,
|
CameraState_ThirdPerson = 0xa
|
||||||
PlayerFlags_JumpingDisabled = 0x1000,
|
|
||||||
PlayerFlags_LookingDisabled = 0x2000,
|
|
||||||
PlayerFlags_VanityModeDisabled = 0x4000,
|
|
||||||
PlayerFlags_WeaponDrawingDisabled = 0x8000,
|
|
||||||
PlayerFlags_SpellDrawingDisabled = 0x10000,
|
|
||||||
PlayerFlags_ThirdPerson = 0x20000,
|
|
||||||
PlayerFlags_TeleportingDisabled = 0x40000,
|
|
||||||
PlayerFlags_LevitationDisabled = 0x80000
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma pack(push)
|
#pragma pack(push)
|
||||||
|
@ -71,53 +63,18 @@ struct PCDT
|
||||||
|
|
||||||
struct PNAM
|
struct PNAM
|
||||||
{
|
{
|
||||||
struct MarkLocation
|
short mDrawState; // DrawState
|
||||||
{
|
short mCameraState; // CameraState
|
||||||
float mX, mY, mZ; // worldspace position
|
|
||||||
float mRotZ; // Z angle in radians
|
|
||||||
int mCellX, mCellY; // grid coordinates; for interior cells this is always (0, 0)
|
|
||||||
};
|
|
||||||
|
|
||||||
int mPlayerFlags; // controls, camera and draw state
|
|
||||||
unsigned int mLevelProgress;
|
unsigned int mLevelProgress;
|
||||||
float mSkillProgress[27]; // skill progress, non-uniform scaled
|
float mSkillProgress[27]; // skill progress, non-uniform scaled
|
||||||
unsigned char mSkillIncreases[8]; // number of skill increases for each attribute
|
unsigned char mSkillIncreases[8]; // number of skill increases for each attribute
|
||||||
int mTelekinesisRangeBonus; // in units; seems redundant
|
unsigned char mUnknown3[88];
|
||||||
float mVisionBonus; // range: <0.0, 1.0>; affected by light spells and Get/Mod/SetPCVisionBonus
|
|
||||||
int mDetectKeyMagnitude; // seems redundant
|
|
||||||
int mDetectEnchantmentMagnitude; // seems redundant
|
|
||||||
int mDetectAnimalMagnitude; // seems redundant
|
|
||||||
MarkLocation mMarkLocation;
|
|
||||||
unsigned char mUnknown3[40];
|
|
||||||
unsigned char mSpecIncreases[3]; // number of skill increases for each specialization
|
|
||||||
unsigned char mUnknown4;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ENAM
|
|
||||||
{
|
|
||||||
int mCellX;
|
|
||||||
int mCellY;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AADT // 44 bytes
|
|
||||||
{
|
|
||||||
int animGroupIndex; // See convertANIS() for the mapping.
|
|
||||||
unsigned char mUnknown5[40];
|
|
||||||
};
|
};
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
std::vector<FNAM> mFactions;
|
std::vector<FNAM> mFactions;
|
||||||
PNAM mPNAM;
|
PNAM mPNAM;
|
||||||
|
|
||||||
bool mHasMark;
|
|
||||||
std::string mMNAM; // mark cell name; can also be sDefaultCellname or region name
|
|
||||||
|
|
||||||
bool mHasENAM;
|
|
||||||
ENAM mENAM; // last exterior cell
|
|
||||||
|
|
||||||
bool mHasAADT;
|
|
||||||
AADT mAADT;
|
|
||||||
|
|
||||||
void load(ESM::ESMReader& esm);
|
void load(ESM::ESMReader& esm);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
#include "importproj.h"
|
|
||||||
|
|
||||||
#include <components/esm/esmreader.hpp>
|
|
||||||
|
|
||||||
namespace ESSImport
|
|
||||||
{
|
|
||||||
|
|
||||||
void ESSImport::PROJ::load(ESM::ESMReader& esm)
|
|
||||||
{
|
|
||||||
while (esm.isNextSub("PNAM"))
|
|
||||||
{
|
|
||||||
PNAM pnam;
|
|
||||||
esm.getHT(pnam);
|
|
||||||
mProjectiles.push_back(pnam);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
#ifndef OPENMW_ESSIMPORT_IMPORTPROJ_H
|
|
||||||
#define OPENMW_ESSIMPORT_IMPORTPROJ_H
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <components/esm/esmcommon.hpp>
|
|
||||||
#include <components/esm/util.hpp>
|
|
||||||
|
|
||||||
namespace ESM
|
|
||||||
{
|
|
||||||
class ESMReader;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace ESSImport
|
|
||||||
{
|
|
||||||
|
|
||||||
struct PROJ
|
|
||||||
{
|
|
||||||
|
|
||||||
#pragma pack(push)
|
|
||||||
#pragma pack(1)
|
|
||||||
struct PNAM // 184 bytes
|
|
||||||
{
|
|
||||||
float mAttackStrength;
|
|
||||||
float mSpeed;
|
|
||||||
unsigned char mUnknown[4*2];
|
|
||||||
float mFlightTime;
|
|
||||||
int mSplmIndex; // reference to a SPLM record (0 for ballistic projectiles)
|
|
||||||
unsigned char mUnknown2[4];
|
|
||||||
ESM::Vector3 mVelocity;
|
|
||||||
ESM::Vector3 mPosition;
|
|
||||||
unsigned char mUnknown3[4*9];
|
|
||||||
ESM::NAME32 mActorId; // indexed refID (with the exception of "PlayerSaveGame")
|
|
||||||
ESM::NAME32 mArrowId;
|
|
||||||
ESM::NAME32 mBowId;
|
|
||||||
|
|
||||||
bool isMagic() const { return mSplmIndex != 0; }
|
|
||||||
};
|
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
std::vector<PNAM> mProjectiles;
|
|
||||||
|
|
||||||
void load(ESM::ESMReader& esm);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -13,7 +13,7 @@ namespace ESM
|
||||||
namespace ESSImport
|
namespace ESSImport
|
||||||
{
|
{
|
||||||
|
|
||||||
/// Local variable assignments for a running script
|
/// Local variable assigments for a running script
|
||||||
struct SCRI
|
struct SCRI
|
||||||
{
|
{
|
||||||
std::string mScript;
|
std::string mScript;
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
#include "importsplm.h"
|
|
||||||
|
|
||||||
#include <components/esm/esmreader.hpp>
|
|
||||||
|
|
||||||
namespace ESSImport
|
|
||||||
{
|
|
||||||
|
|
||||||
void SPLM::load(ESM::ESMReader& esm)
|
|
||||||
{
|
|
||||||
while (esm.isNextSub("NAME"))
|
|
||||||
{
|
|
||||||
ActiveSpell spell;
|
|
||||||
esm.getHT(spell.mIndex);
|
|
||||||
esm.getHNT(spell.mSPDT, "SPDT");
|
|
||||||
spell.mTarget = esm.getHNOString("TNAM");
|
|
||||||
|
|
||||||
while (esm.isNextSub("NPDT"))
|
|
||||||
{
|
|
||||||
ActiveEffect effect;
|
|
||||||
esm.getHT(effect.mNPDT);
|
|
||||||
|
|
||||||
// Effect-specific subrecords can follow:
|
|
||||||
// - INAM for disintegration and bound effects
|
|
||||||
// - CNAM for summoning and command effects
|
|
||||||
// - VNAM for vampirism
|
|
||||||
// NOTE: There can be multiple INAMs per effect.
|
|
||||||
// TODO: Needs more research.
|
|
||||||
|
|
||||||
esm.skipHSubUntil("NAM0"); // sentinel
|
|
||||||
esm.getSubName();
|
|
||||||
esm.skipHSub();
|
|
||||||
|
|
||||||
spell.mActiveEffects.push_back(effect);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char xnam; // sentinel
|
|
||||||
esm.getHNT(xnam, "XNAM");
|
|
||||||
|
|
||||||
mActiveSpells.push_back(spell);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
#ifndef OPENMW_ESSIMPORT_IMPORTSPLM_H
|
|
||||||
#define OPENMW_ESSIMPORT_IMPORTSPLM_H
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <components/esm/esmcommon.hpp>
|
|
||||||
#include <components/esm/util.hpp>
|
|
||||||
|
|
||||||
namespace ESM
|
|
||||||
{
|
|
||||||
class ESMReader;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace ESSImport
|
|
||||||
{
|
|
||||||
|
|
||||||
struct SPLM
|
|
||||||
{
|
|
||||||
|
|
||||||
#pragma pack(push)
|
|
||||||
#pragma pack(1)
|
|
||||||
struct SPDT // 160 bytes
|
|
||||||
{
|
|
||||||
int mType; // 1 = spell, 2 = enchantment, 3 = potion
|
|
||||||
ESM::NAME32 mId; // base ID of a spell/enchantment/potion
|
|
||||||
unsigned char mUnknown[4*4];
|
|
||||||
ESM::NAME32 mCasterId;
|
|
||||||
ESM::NAME32 mSourceId; // empty for spells
|
|
||||||
unsigned char mUnknown2[4*11];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct NPDT // 56 bytes
|
|
||||||
{
|
|
||||||
ESM::NAME32 mAffectedActorId;
|
|
||||||
unsigned char mUnknown[4*2];
|
|
||||||
int mMagnitude;
|
|
||||||
float mSecondsActive;
|
|
||||||
unsigned char mUnknown2[4*2];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct INAM // 40 bytes
|
|
||||||
{
|
|
||||||
int mUnknown;
|
|
||||||
unsigned char mUnknown2;
|
|
||||||
ESM::FIXED_STRING<35> mItemId; // disintegrated item / bound item / item to re-equip after expiration
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CNAM // 36 bytes
|
|
||||||
{
|
|
||||||
int mUnknown; // seems to always be 0
|
|
||||||
ESM::NAME32 mSummonedOrCommandedActor[32];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VNAM // 4 bytes
|
|
||||||
{
|
|
||||||
int mUnknown;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
struct ActiveEffect
|
|
||||||
{
|
|
||||||
NPDT mNPDT;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ActiveSpell
|
|
||||||
{
|
|
||||||
int mIndex;
|
|
||||||
SPDT mSPDT;
|
|
||||||
std::string mTarget;
|
|
||||||
std::vector<ActiveEffect> mActiveEffects;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<ActiveSpell> mActiveSpells;
|
|
||||||
|
|
||||||
void load(ESM::ESMReader& esm);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include <boost/program_options.hpp>
|
#include <boost/program_options.hpp>
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <boost/filesystem/path.hpp>
|
||||||
|
#include <boost/filesystem/fstream.hpp>
|
||||||
|
|
||||||
#include <components/files/configurationmanager.hpp>
|
#include <components/files/configurationmanager.hpp>
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,7 @@ set(LAUNCHER
|
||||||
playpage.cpp
|
playpage.cpp
|
||||||
textslotmsgbox.cpp
|
textslotmsgbox.cpp
|
||||||
settingspage.cpp
|
settingspage.cpp
|
||||||
advancedpage.cpp
|
|
||||||
|
|
||||||
utils/cellnameloader.cpp
|
|
||||||
utils/profilescombobox.cpp
|
utils/profilescombobox.cpp
|
||||||
utils/textinputdialog.cpp
|
utils/textinputdialog.cpp
|
||||||
utils/lineedit.cpp
|
utils/lineedit.cpp
|
||||||
|
@ -23,9 +21,7 @@ set(LAUNCHER_HEADER
|
||||||
playpage.hpp
|
playpage.hpp
|
||||||
textslotmsgbox.hpp
|
textslotmsgbox.hpp
|
||||||
settingspage.hpp
|
settingspage.hpp
|
||||||
advancedpage.hpp
|
|
||||||
|
|
||||||
utils/cellnameloader.hpp
|
|
||||||
utils/profilescombobox.hpp
|
utils/profilescombobox.hpp
|
||||||
utils/textinputdialog.hpp
|
utils/textinputdialog.hpp
|
||||||
utils/lineedit.hpp
|
utils/lineedit.hpp
|
||||||
|
@ -39,9 +35,7 @@ set(LAUNCHER_HEADER_MOC
|
||||||
playpage.hpp
|
playpage.hpp
|
||||||
textslotmsgbox.hpp
|
textslotmsgbox.hpp
|
||||||
settingspage.hpp
|
settingspage.hpp
|
||||||
advancedpage.hpp
|
|
||||||
|
|
||||||
utils/cellnameloader.hpp
|
|
||||||
utils/textinputdialog.hpp
|
utils/textinputdialog.hpp
|
||||||
utils/profilescombobox.hpp
|
utils/profilescombobox.hpp
|
||||||
utils/lineedit.hpp
|
utils/lineedit.hpp
|
||||||
|
@ -55,7 +49,6 @@ set(LAUNCHER_UI
|
||||||
${CMAKE_SOURCE_DIR}/files/ui/playpage.ui
|
${CMAKE_SOURCE_DIR}/files/ui/playpage.ui
|
||||||
${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui
|
${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui
|
||||||
${CMAKE_SOURCE_DIR}/files/ui/settingspage.ui
|
${CMAKE_SOURCE_DIR}/files/ui/settingspage.ui
|
||||||
${CMAKE_SOURCE_DIR}/files/ui/advancedpage.ui
|
|
||||||
)
|
)
|
||||||
|
|
||||||
source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER})
|
source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER})
|
||||||
|
@ -85,7 +78,7 @@ if(NOT WIN32)
|
||||||
endif(NOT WIN32)
|
endif(NOT WIN32)
|
||||||
|
|
||||||
# Main executable
|
# Main executable
|
||||||
openmw_add_executable(openmw-launcher
|
add_executable(openmw-launcher
|
||||||
${GUI_TYPE}
|
${GUI_TYPE}
|
||||||
${LAUNCHER}
|
${LAUNCHER}
|
||||||
${LAUNCHER_HEADER}
|
${LAUNCHER_HEADER}
|
||||||
|
@ -94,10 +87,6 @@ openmw_add_executable(openmw-launcher
|
||||||
${UI_HDRS}
|
${UI_HDRS}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (WIN32)
|
|
||||||
INSTALL(TARGETS openmw-launcher RUNTIME DESTINATION ".")
|
|
||||||
endif (WIN32)
|
|
||||||
|
|
||||||
target_link_libraries(openmw-launcher
|
target_link_libraries(openmw-launcher
|
||||||
${SDL2_LIBRARY_ONLY}
|
${SDL2_LIBRARY_ONLY}
|
||||||
components
|
components
|
||||||
|
@ -109,7 +98,7 @@ if (DESIRED_QT_VERSION MATCHES 4)
|
||||||
target_link_libraries(openmw-launcher ${QT_QTMAIN_LIBRARY})
|
target_link_libraries(openmw-launcher ${QT_QTMAIN_LIBRARY})
|
||||||
endif(WIN32)
|
endif(WIN32)
|
||||||
else()
|
else()
|
||||||
target_link_libraries(openmw-launcher Qt5::Widgets Qt5::Core)
|
qt5_use_modules(openmw-launcher Widgets Core)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (BUILD_WITH_CODE_COVERAGE)
|
if (BUILD_WITH_CODE_COVERAGE)
|
||||||
|
|
|
@ -1,176 +0,0 @@
|
||||||
#include "advancedpage.hpp"
|
|
||||||
|
|
||||||
#include <components/config/gamesettings.hpp>
|
|
||||||
#include <components/config/launchersettings.hpp>
|
|
||||||
#include <QFileDialog>
|
|
||||||
#include <QCompleter>
|
|
||||||
#include <components/contentselector/view/contentselector.hpp>
|
|
||||||
#include <components/contentselector/model/esmfile.hpp>
|
|
||||||
|
|
||||||
Launcher::AdvancedPage::AdvancedPage(Files::ConfigurationManager &cfg,
|
|
||||||
Config::GameSettings &gameSettings,
|
|
||||||
Settings::Manager &engineSettings, QWidget *parent)
|
|
||||||
: QWidget(parent)
|
|
||||||
, mCfgMgr(cfg)
|
|
||||||
, mGameSettings(gameSettings)
|
|
||||||
, mEngineSettings(engineSettings)
|
|
||||||
{
|
|
||||||
setObjectName ("AdvancedPage");
|
|
||||||
setupUi(this);
|
|
||||||
|
|
||||||
loadSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Launcher::AdvancedPage::loadCellsForAutocomplete(QStringList cellNames) {
|
|
||||||
// Set up an auto-completer for the "Start default character at" field
|
|
||||||
auto *completer = new QCompleter(cellNames);
|
|
||||||
completer->setCompletionMode(QCompleter::PopupCompletion);
|
|
||||||
completer->setCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);
|
|
||||||
startDefaultCharacterAtField->setCompleter(completer);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void Launcher::AdvancedPage::on_skipMenuCheckBox_stateChanged(int state) {
|
|
||||||
startDefaultCharacterAtLabel->setEnabled(state == Qt::Checked);
|
|
||||||
startDefaultCharacterAtField->setEnabled(state == Qt::Checked);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Launcher::AdvancedPage::on_runScriptAfterStartupBrowseButton_clicked()
|
|
||||||
{
|
|
||||||
QString scriptFile = QFileDialog::getOpenFileName(
|
|
||||||
this,
|
|
||||||
QObject::tr("Select script file"),
|
|
||||||
QDir::currentPath(),
|
|
||||||
QString(tr("Text file (*.txt)")));
|
|
||||||
|
|
||||||
if (scriptFile.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QFileInfo info(scriptFile);
|
|
||||||
|
|
||||||
if (!info.exists() || !info.isReadable())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const QString path(QDir::toNativeSeparators(info.absoluteFilePath()));
|
|
||||||
runScriptAfterStartupField->setText(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Launcher::AdvancedPage::loadSettings()
|
|
||||||
{
|
|
||||||
// Testing
|
|
||||||
bool skipMenu = mGameSettings.value("skip-menu").toInt() == 1;
|
|
||||||
if (skipMenu) {
|
|
||||||
skipMenuCheckBox->setCheckState(Qt::Checked);
|
|
||||||
}
|
|
||||||
startDefaultCharacterAtLabel->setEnabled(skipMenu);
|
|
||||||
startDefaultCharacterAtField->setEnabled(skipMenu);
|
|
||||||
|
|
||||||
startDefaultCharacterAtField->setText(mGameSettings.value("start"));
|
|
||||||
runScriptAfterStartupField->setText(mGameSettings.value("script-run"));
|
|
||||||
|
|
||||||
// Game Settings
|
|
||||||
loadSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
|
|
||||||
loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
|
|
||||||
loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
|
|
||||||
loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
|
|
||||||
loadSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game");
|
|
||||||
loadSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game");
|
|
||||||
loadSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game");
|
|
||||||
|
|
||||||
// Input Settings
|
|
||||||
loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");
|
|
||||||
loadSettingBool(grabCursorCheckBox, "grab cursor", "Input");
|
|
||||||
loadSettingBool(toggleSneakCheckBox, "toggle sneak", "Input");
|
|
||||||
|
|
||||||
// Saves Settings
|
|
||||||
loadSettingBool(timePlayedCheckbox, "timeplayed", "Saves");
|
|
||||||
maximumQuicksavesComboBox->setValue(mEngineSettings.getInt("max quicksaves", "Saves"));
|
|
||||||
|
|
||||||
// User Interface Settings
|
|
||||||
loadSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
|
|
||||||
loadSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
|
|
||||||
loadSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
|
|
||||||
loadSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
|
|
||||||
int showOwnedIndex = mEngineSettings.getInt("show owned", "Game");
|
|
||||||
// Match the index with the option (only 0, 1, 2, or 3 are valid). Will default to 0 if invalid.
|
|
||||||
if (showOwnedIndex >= 0 && showOwnedIndex <= 3)
|
|
||||||
showOwnedComboBox->setCurrentIndex(showOwnedIndex);
|
|
||||||
|
|
||||||
// Other Settings
|
|
||||||
QString screenshotFormatString = QString::fromStdString(mEngineSettings.getString("screenshot format", "General")).toUpper();
|
|
||||||
if (screenshotFormatComboBox->findText(screenshotFormatString) == -1)
|
|
||||||
screenshotFormatComboBox->addItem(screenshotFormatString);
|
|
||||||
screenshotFormatComboBox->setCurrentIndex(screenshotFormatComboBox->findText(screenshotFormatString));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Launcher::AdvancedPage::saveSettings()
|
|
||||||
{
|
|
||||||
// Ensure we only set the new settings if they changed. This is to avoid cluttering the
|
|
||||||
// user settings file (which by definition should only contain settings the user has touched)
|
|
||||||
|
|
||||||
// Testing
|
|
||||||
int skipMenu = skipMenuCheckBox->checkState() == Qt::Checked;
|
|
||||||
if (skipMenu != mGameSettings.value("skip-menu").toInt())
|
|
||||||
mGameSettings.setValue("skip-menu", QString::number(skipMenu));
|
|
||||||
|
|
||||||
QString startCell = startDefaultCharacterAtField->text();
|
|
||||||
if (startCell != mGameSettings.value("start")) {
|
|
||||||
mGameSettings.setValue("start", startCell);
|
|
||||||
}
|
|
||||||
QString scriptRun = runScriptAfterStartupField->text();
|
|
||||||
if (scriptRun != mGameSettings.value("script-run"))
|
|
||||||
mGameSettings.setValue("script-run", scriptRun);
|
|
||||||
|
|
||||||
// Game Settings
|
|
||||||
saveSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
|
|
||||||
saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
|
|
||||||
saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
|
|
||||||
saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
|
|
||||||
saveSettingBool(chargeForEveryFollowerCheckBox, "charge for every follower travelling", "Game");
|
|
||||||
saveSettingBool(enchantedWeaponsMagicalCheckBox, "enchanted weapons are magical", "Game");
|
|
||||||
saveSettingBool(permanentBarterDispositionChangeCheckBox, "barter disposition change is permanent", "Game");
|
|
||||||
|
|
||||||
// Input Settings
|
|
||||||
saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");
|
|
||||||
saveSettingBool(grabCursorCheckBox, "grab cursor", "Input");
|
|
||||||
saveSettingBool(toggleSneakCheckBox, "toggle sneak", "Input");
|
|
||||||
|
|
||||||
// Saves Settings
|
|
||||||
saveSettingBool(timePlayedCheckbox, "timeplayed", "Saves");
|
|
||||||
int maximumQuicksaves = maximumQuicksavesComboBox->value();
|
|
||||||
if (maximumQuicksaves != mEngineSettings.getInt("max quicksaves", "Saves")) {
|
|
||||||
mEngineSettings.setInt("max quicksaves", "Saves", maximumQuicksaves);
|
|
||||||
}
|
|
||||||
|
|
||||||
// User Interface Settings
|
|
||||||
saveSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
|
|
||||||
saveSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
|
|
||||||
saveSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
|
|
||||||
saveSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
|
|
||||||
int showOwnedCurrentIndex = showOwnedComboBox->currentIndex();
|
|
||||||
if (showOwnedCurrentIndex != mEngineSettings.getInt("show owned", "Game"))
|
|
||||||
mEngineSettings.setInt("show owned", "Game", showOwnedCurrentIndex);
|
|
||||||
|
|
||||||
// Other Settings
|
|
||||||
std::string screenshotFormatString = screenshotFormatComboBox->currentText().toLower().toStdString();
|
|
||||||
if (screenshotFormatString != mEngineSettings.getString("screenshot format", "General"))
|
|
||||||
mEngineSettings.setString("screenshot format", "General", screenshotFormatString);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Launcher::AdvancedPage::loadSettingBool(QCheckBox *checkbox, const std::string &setting, const std::string &group) {
|
|
||||||
if (mEngineSettings.getBool(setting, group))
|
|
||||||
checkbox->setCheckState(Qt::Checked);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Launcher::AdvancedPage::saveSettingBool(QCheckBox *checkbox, const std::string &setting, const std::string &group) {
|
|
||||||
bool cValue = checkbox->checkState();
|
|
||||||
if (cValue != mEngineSettings.getBool(setting, group))
|
|
||||||
mEngineSettings.setBool(setting, group, cValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Launcher::AdvancedPage::slotLoadedCellsChanged(QStringList cellNames)
|
|
||||||
{
|
|
||||||
loadCellsForAutocomplete(cellNames);
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
#ifndef ADVANCEDPAGE_H
|
|
||||||
#define ADVANCEDPAGE_H
|
|
||||||
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
#include "ui_advancedpage.h"
|
|
||||||
|
|
||||||
#include <components/settings/settings.hpp>
|
|
||||||
|
|
||||||
namespace Files { struct ConfigurationManager; }
|
|
||||||
namespace Config { class GameSettings; }
|
|
||||||
|
|
||||||
namespace Launcher
|
|
||||||
{
|
|
||||||
class AdvancedPage : public QWidget, private Ui::AdvancedPage
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
AdvancedPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings,
|
|
||||||
Settings::Manager &engineSettings, QWidget *parent = 0);
|
|
||||||
|
|
||||||
bool loadSettings();
|
|
||||||
void saveSettings();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void slotLoadedCellsChanged(QStringList cellNames);
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void on_skipMenuCheckBox_stateChanged(int state);
|
|
||||||
void on_runScriptAfterStartupBrowseButton_clicked();
|
|
||||||
|
|
||||||
private:
|
|
||||||
Files::ConfigurationManager &mCfgMgr;
|
|
||||||
Config::GameSettings &mGameSettings;
|
|
||||||
Settings::Manager &mEngineSettings;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load the cells associated with the given content files for use in autocomplete
|
|
||||||
* @param filePaths the file paths of the content files to be examined
|
|
||||||
*/
|
|
||||||
void loadCellsForAutocomplete(QStringList filePaths);
|
|
||||||
void loadSettingBool(QCheckBox *checkbox, const std::string& setting, const std::string& group);
|
|
||||||
void saveSettingBool(QCheckBox *checkbox, const std::string& setting, const std::string& group);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif
|
|
|
@ -7,10 +7,7 @@
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
#include <apps/launcher/utils/cellnameloader.hpp>
|
|
||||||
#include <components/files/configurationmanager.hpp>
|
#include <components/files/configurationmanager.hpp>
|
||||||
|
|
||||||
#include <components/contentselector/model/esmfile.hpp>
|
#include <components/contentselector/model/esmfile.hpp>
|
||||||
|
@ -19,7 +16,6 @@
|
||||||
|
|
||||||
#include <components/config/gamesettings.hpp>
|
#include <components/config/gamesettings.hpp>
|
||||||
#include <components/config/launchersettings.hpp>
|
#include <components/config/launchersettings.hpp>
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include "utils/textinputdialog.hpp"
|
#include "utils/textinputdialog.hpp"
|
||||||
#include "utils/profilescombobox.hpp"
|
#include "utils/profilescombobox.hpp"
|
||||||
|
@ -44,13 +40,6 @@ Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config:
|
||||||
|
|
||||||
buildView();
|
buildView();
|
||||||
loadSettings();
|
loadSettings();
|
||||||
|
|
||||||
// Connect signal and slot after the settings have been loaded. We only care about the user changing
|
|
||||||
// the addons and don't want to get signals of the system doing it during startup.
|
|
||||||
connect(mSelector, SIGNAL(signalAddonDataChanged(QModelIndex,QModelIndex)),
|
|
||||||
this, SLOT(slotAddonDataChanged()));
|
|
||||||
// Call manually to indicate all changes to addon data during startup.
|
|
||||||
slotAddonDataChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Launcher::DataFilesPage::buildView()
|
void Launcher::DataFilesPage::buildView()
|
||||||
|
@ -153,17 +142,6 @@ void Launcher::DataFilesPage::saveSettings(const QString &profile)
|
||||||
mGameSettings.setContentList(fileNames);
|
mGameSettings.setContentList(fileNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList Launcher::DataFilesPage::selectedFilePaths()
|
|
||||||
{
|
|
||||||
//retrieve the files selected for the profile
|
|
||||||
ContentSelectorModel::ContentFileList items = mSelector->selectedFiles();
|
|
||||||
QStringList filePaths;
|
|
||||||
foreach(const ContentSelectorModel::EsmFile *item, items) {
|
|
||||||
filePaths.append(item->filePath());
|
|
||||||
}
|
|
||||||
return filePaths;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Launcher::DataFilesPage::removeProfile(const QString &profile)
|
void Launcher::DataFilesPage::removeProfile(const QString &profile)
|
||||||
{
|
{
|
||||||
mLauncherSettings.removeContentList(profile);
|
mLauncherSettings.removeContentList(profile);
|
||||||
|
@ -330,31 +308,3 @@ bool Launcher::DataFilesPage::showDeleteMessageBox (const QString &text)
|
||||||
|
|
||||||
return (msgBox.clickedButton() == deleteButton);
|
return (msgBox.clickedButton() == deleteButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Launcher::DataFilesPage::slotAddonDataChanged()
|
|
||||||
{
|
|
||||||
QStringList selectedFiles = selectedFilePaths();
|
|
||||||
if (previousSelectedFiles != selectedFiles) {
|
|
||||||
previousSelectedFiles = selectedFiles;
|
|
||||||
// Loading cells for core Morrowind + Expansions takes about 0.2 seconds, which is enough to cause a
|
|
||||||
// barely perceptible UI lag. Splitting into its own thread to alleviate that.
|
|
||||||
std::thread loadCellsThread(&DataFilesPage::reloadCells, this, selectedFiles);
|
|
||||||
loadCellsThread.detach();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mutex lock to run reloadCells synchronously.
|
|
||||||
std::mutex _reloadCellsMutex;
|
|
||||||
|
|
||||||
void Launcher::DataFilesPage::reloadCells(QStringList selectedFiles)
|
|
||||||
{
|
|
||||||
// Use a mutex lock so that we can prevent two threads from executing the rest of this code at the same time
|
|
||||||
// Based on https://stackoverflow.com/a/5429695/531762
|
|
||||||
std::unique_lock<std::mutex> lock(_reloadCellsMutex);
|
|
||||||
|
|
||||||
// The following code will run only if there is not another thread currently running it
|
|
||||||
CellNameLoader cellNameLoader;
|
|
||||||
QStringList cellNamesList = QStringList::fromSet(cellNameLoader.getCellNames(selectedFiles));
|
|
||||||
std::sort(cellNamesList.begin(), cellNamesList.end());
|
|
||||||
emit signalLoadedCellsChanged(cellNamesList);
|
|
||||||
}
|
|
|
@ -7,7 +7,6 @@
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QStringList>
|
|
||||||
|
|
||||||
class QSortFilterProxyModel;
|
class QSortFilterProxyModel;
|
||||||
class QAbstractItemModel;
|
class QAbstractItemModel;
|
||||||
|
@ -42,15 +41,8 @@ namespace Launcher
|
||||||
void saveSettings(const QString &profile = "");
|
void saveSettings(const QString &profile = "");
|
||||||
bool loadSettings();
|
bool loadSettings();
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the file paths of all selected content files
|
|
||||||
* @return the file paths of all selected content files
|
|
||||||
*/
|
|
||||||
QStringList selectedFilePaths();
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void signalProfileChanged (int index);
|
void signalProfileChanged (int index);
|
||||||
void signalLoadedCellsChanged(QStringList selectedFiles);
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void slotProfileChanged (int index);
|
void slotProfileChanged (int index);
|
||||||
|
@ -60,7 +52,6 @@ namespace Launcher
|
||||||
void slotProfileChangedByUser(const QString &previous, const QString ¤t);
|
void slotProfileChangedByUser(const QString &previous, const QString ¤t);
|
||||||
void slotProfileRenamed(const QString &previous, const QString ¤t);
|
void slotProfileRenamed(const QString &previous, const QString ¤t);
|
||||||
void slotProfileDeleted(const QString &item);
|
void slotProfileDeleted(const QString &item);
|
||||||
void slotAddonDataChanged ();
|
|
||||||
|
|
||||||
void updateOkButton(const QString &text);
|
void updateOkButton(const QString &text);
|
||||||
|
|
||||||
|
@ -81,7 +72,7 @@ namespace Launcher
|
||||||
Config::LauncherSettings &mLauncherSettings;
|
Config::LauncherSettings &mLauncherSettings;
|
||||||
|
|
||||||
QString mPreviousProfile;
|
QString mPreviousProfile;
|
||||||
QStringList previousSelectedFiles;
|
|
||||||
QString mDataLocal;
|
QString mDataLocal;
|
||||||
|
|
||||||
void setPluginsCheckstates(Qt::CheckState state);
|
void setPluginsCheckstates(Qt::CheckState state);
|
||||||
|
@ -96,7 +87,6 @@ namespace Launcher
|
||||||
void addProfile (const QString &profile, bool setAsCurrent);
|
void addProfile (const QString &profile, bool setAsCurrent);
|
||||||
void checkForDefaultProfile();
|
void checkForDefaultProfile();
|
||||||
void populateFileViews(const QString& contentModelName);
|
void populateFileViews(const QString& contentModelName);
|
||||||
void reloadCells(QStringList selectedFiles);
|
|
||||||
|
|
||||||
class PathIterator
|
class PathIterator
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include "graphicspage.hpp"
|
#include "graphicspage.hpp"
|
||||||
|
|
||||||
#include <boost/math/common_factor.hpp>
|
|
||||||
#include <csignal>
|
|
||||||
#include <QDesktopWidget>
|
#include <QDesktopWidget>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
@ -12,11 +10,16 @@
|
||||||
#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
|
#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
|
||||||
#endif // MAC_OS_X_VERSION_MIN_REQUIRED
|
#endif // MAC_OS_X_VERSION_MIN_REQUIRED
|
||||||
|
|
||||||
#include <SDL.h>
|
|
||||||
#include <SDL_video.h>
|
#include <SDL_video.h>
|
||||||
|
|
||||||
|
#include <boost/math/common_factor.hpp>
|
||||||
|
|
||||||
#include <components/files/configurationmanager.hpp>
|
#include <components/files/configurationmanager.hpp>
|
||||||
|
|
||||||
|
#include <components/contentselector/model/naturalsort.hpp>
|
||||||
|
|
||||||
|
#include <components/settings/settings.hpp>
|
||||||
|
|
||||||
QString getAspect(int x, int y)
|
QString getAspect(int x, int y)
|
||||||
{
|
{
|
||||||
int gcd = boost::math::gcd (x, y);
|
int gcd = boost::math::gcd (x, y);
|
||||||
|
@ -48,28 +51,8 @@ Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, Settings:
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Launcher::GraphicsPage::connectToSdl() {
|
|
||||||
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software");
|
|
||||||
SDL_SetMainReady();
|
|
||||||
// Required for determining screen resolution and such on the Graphics tab
|
|
||||||
if (SDL_Init(SDL_INIT_VIDEO) != 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
signal(SIGINT, SIG_DFL); // We don't want to use the SDL event loop in the launcher,
|
|
||||||
// so reset SIGINT which SDL wants to redirect to an SDL_Quit event.
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Launcher::GraphicsPage::setupSDL()
|
bool Launcher::GraphicsPage::setupSDL()
|
||||||
{
|
{
|
||||||
bool sdlConnectSuccessful = connectToSdl();
|
|
||||||
if (!sdlConnectSuccessful)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int displays = SDL_GetNumVideoDisplays();
|
int displays = SDL_GetNumVideoDisplays();
|
||||||
|
|
||||||
if (displays < 0)
|
if (displays < 0)
|
||||||
|
@ -89,9 +72,6 @@ bool Launcher::GraphicsPage::setupSDL()
|
||||||
screenComboBox->addItem(QString(tr("Screen ")) + QString::number(i + 1));
|
screenComboBox->addItem(QString(tr("Screen ")) + QString::number(i + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disconnect from SDL processes
|
|
||||||
SDL_Quit();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,11 +37,6 @@ namespace Launcher
|
||||||
QStringList getAvailableResolutions(int screen);
|
QStringList getAvailableResolutions(int screen);
|
||||||
QRect getMaximumResolution();
|
QRect getMaximumResolution();
|
||||||
|
|
||||||
/**
|
|
||||||
* Connect to the SDL so that we can use it to determine graphics
|
|
||||||
* @return whether or not connecting to SDL is successful
|
|
||||||
*/
|
|
||||||
bool connectToSdl();
|
|
||||||
bool setupSDL();
|
bool setupSDL();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <csignal>
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QTextCodec>
|
#include <QTextCodec>
|
||||||
|
@ -11,17 +12,37 @@
|
||||||
#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
|
#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
|
||||||
#endif // MAC_OS_X_VERSION_MIN_REQUIRED
|
#endif // MAC_OS_X_VERSION_MIN_REQUIRED
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
#include "maindialog.hpp"
|
#include "maindialog.hpp"
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software");
|
||||||
|
SDL_SetMainReady();
|
||||||
|
if (SDL_Init(SDL_INIT_VIDEO) != 0)
|
||||||
|
{
|
||||||
|
qDebug() << "SDL_Init failed: " << QString::fromUtf8(SDL_GetError());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
signal(SIGINT, SIG_DFL); // We don't want to use the SDL event loop in the launcher,
|
||||||
|
// so reset SIGINT which SDL wants to redirect to an SDL_Quit event.
|
||||||
|
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
// Now we make sure the current dir is set to application path
|
// Now we make sure the current dir is set to application path
|
||||||
QDir dir(QCoreApplication::applicationDirPath());
|
QDir dir(QCoreApplication::applicationDirPath());
|
||||||
|
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
if (dir.dirName() == "MacOS") {
|
||||||
|
dir.cdUp();
|
||||||
|
dir.cdUp();
|
||||||
|
dir.cdUp();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
QDir::setCurrent(dir.absolutePath());
|
QDir::setCurrent(dir.absolutePath());
|
||||||
|
|
||||||
Launcher::MainDialog mainWin;
|
Launcher::MainDialog mainWin;
|
||||||
|
@ -33,7 +54,9 @@ int main(int argc, char *argv[])
|
||||||
if (result == Launcher::FirstRunDialogResultContinue)
|
if (result == Launcher::FirstRunDialogResultContinue)
|
||||||
mainWin.show();
|
mainWin.show();
|
||||||
|
|
||||||
return app.exec();
|
int returnValue = app.exec();
|
||||||
|
SDL_Quit();
|
||||||
|
return returnValue;
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (std::exception& e)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
|
|
||||||
#include <components/version/version.hpp>
|
#include <components/version/version.hpp>
|
||||||
|
|
||||||
|
#include <QLabel>
|
||||||
#include <QDate>
|
#include <QDate>
|
||||||
|
#include <QTime>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QFontDatabase>
|
#include <QFontDatabase>
|
||||||
|
@ -10,6 +12,8 @@
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QCloseEvent>
|
#include <QCloseEvent>
|
||||||
#include <QTextCodec>
|
#include <QTextCodec>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
|
@ -17,7 +21,6 @@
|
||||||
#include "graphicspage.hpp"
|
#include "graphicspage.hpp"
|
||||||
#include "datafilespage.hpp"
|
#include "datafilespage.hpp"
|
||||||
#include "settingspage.hpp"
|
#include "settingspage.hpp"
|
||||||
#include "advancedpage.hpp"
|
|
||||||
|
|
||||||
using namespace Process;
|
using namespace Process;
|
||||||
|
|
||||||
|
@ -90,23 +93,17 @@ void Launcher::MainDialog::createIcons()
|
||||||
dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||||
|
|
||||||
QListWidgetItem *graphicsButton = new QListWidgetItem(iconWidget);
|
QListWidgetItem *graphicsButton = new QListWidgetItem(iconWidget);
|
||||||
graphicsButton->setIcon(QIcon(":/images/preferences-video.png"));
|
graphicsButton->setIcon(QIcon::fromTheme("video-display"));
|
||||||
graphicsButton->setText(tr("Graphics"));
|
graphicsButton->setText(tr("Graphics"));
|
||||||
graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute);
|
graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute);
|
||||||
graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||||
|
|
||||||
QListWidgetItem *settingsButton = new QListWidgetItem(iconWidget);
|
QListWidgetItem *settingsButton = new QListWidgetItem(iconWidget);
|
||||||
settingsButton->setIcon(QIcon(":/images/preferences.png"));
|
settingsButton->setIcon(QIcon::fromTheme("preferences-system"));
|
||||||
settingsButton->setText(tr("Settings"));
|
settingsButton->setText(tr("Settings"));
|
||||||
settingsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
|
settingsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
|
||||||
settingsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
settingsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||||
|
|
||||||
QListWidgetItem *advancedButton = new QListWidgetItem(iconWidget);
|
|
||||||
advancedButton->setIcon(QIcon(":/images/preferences-advanced.png"));
|
|
||||||
advancedButton->setText(tr("Advanced"));
|
|
||||||
advancedButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
|
|
||||||
advancedButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
|
||||||
|
|
||||||
connect(iconWidget,
|
connect(iconWidget,
|
||||||
SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
|
SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
|
||||||
this, SLOT(changePage(QListWidgetItem*,QListWidgetItem*)));
|
this, SLOT(changePage(QListWidgetItem*,QListWidgetItem*)));
|
||||||
|
@ -119,7 +116,6 @@ void Launcher::MainDialog::createPages()
|
||||||
mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
|
mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
|
||||||
mGraphicsPage = new GraphicsPage(mCfgMgr, mEngineSettings, this);
|
mGraphicsPage = new GraphicsPage(mCfgMgr, mEngineSettings, this);
|
||||||
mSettingsPage = new SettingsPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
|
mSettingsPage = new SettingsPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
|
||||||
mAdvancedPage = new AdvancedPage(mCfgMgr, mGameSettings, mEngineSettings, this);
|
|
||||||
|
|
||||||
// Set the combobox of the play page to imitate the combobox on the datafilespage
|
// Set the combobox of the play page to imitate the combobox on the datafilespage
|
||||||
mPlayPage->setProfilesModel(mDataFilesPage->profilesModel());
|
mPlayPage->setProfilesModel(mDataFilesPage->profilesModel());
|
||||||
|
@ -130,7 +126,6 @@ void Launcher::MainDialog::createPages()
|
||||||
pagesWidget->addWidget(mDataFilesPage);
|
pagesWidget->addWidget(mDataFilesPage);
|
||||||
pagesWidget->addWidget(mGraphicsPage);
|
pagesWidget->addWidget(mGraphicsPage);
|
||||||
pagesWidget->addWidget(mSettingsPage);
|
pagesWidget->addWidget(mSettingsPage);
|
||||||
pagesWidget->addWidget(mAdvancedPage);
|
|
||||||
|
|
||||||
// Select the first page
|
// Select the first page
|
||||||
iconWidget->setCurrentItem(iconWidget->item(0), QItemSelectionModel::Select);
|
iconWidget->setCurrentItem(iconWidget->item(0), QItemSelectionModel::Select);
|
||||||
|
@ -139,8 +134,6 @@ void Launcher::MainDialog::createPages()
|
||||||
|
|
||||||
connect(mPlayPage, SIGNAL(signalProfileChanged(int)), mDataFilesPage, SLOT(slotProfileChanged(int)));
|
connect(mPlayPage, SIGNAL(signalProfileChanged(int)), mDataFilesPage, SLOT(slotProfileChanged(int)));
|
||||||
connect(mDataFilesPage, SIGNAL(signalProfileChanged(int)), mPlayPage, SLOT(setProfilesIndex(int)));
|
connect(mDataFilesPage, SIGNAL(signalProfileChanged(int)), mPlayPage, SLOT(setProfilesIndex(int)));
|
||||||
// Using Qt::QueuedConnection because signal is emitted in a subthread and slot is in the main thread
|
|
||||||
connect(mDataFilesPage, SIGNAL(signalLoadedCellsChanged(QStringList)), mAdvancedPage, SLOT(slotLoadedCellsChanged(QStringList)), Qt::QueuedConnection);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,10 +172,7 @@ Launcher::FirstRunDialogResult Launcher::MainDialog::showFirstRunDialog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!setup() || !setupGameData()) {
|
return setup() ? FirstRunDialogResultContinue : FirstRunDialogResultFailure;
|
||||||
return FirstRunDialogResultFailure;
|
|
||||||
}
|
|
||||||
return FirstRunDialogResultContinue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Launcher::MainDialog::setVersionLabel()
|
void Launcher::MainDialog::setVersionLabel()
|
||||||
|
@ -252,9 +242,6 @@ bool Launcher::MainDialog::reloadSettings()
|
||||||
if (!mGraphicsPage->loadSettings())
|
if (!mGraphicsPage->loadSettings())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!mAdvancedPage->loadSettings())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,7 +293,6 @@ bool Launcher::MainDialog::setupGameSettings()
|
||||||
{
|
{
|
||||||
mGameSettings.clear();
|
mGameSettings.clear();
|
||||||
|
|
||||||
QString localPath = QString::fromUtf8(mCfgMgr.getLocalPath().string().c_str());
|
|
||||||
QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str());
|
QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str());
|
||||||
QString globalPath = QString::fromUtf8(mCfgMgr.getGlobalPath().string().c_str());
|
QString globalPath = QString::fromUtf8(mCfgMgr.getGlobalPath().string().c_str());
|
||||||
|
|
||||||
|
@ -334,13 +320,13 @@ bool Launcher::MainDialog::setupGameSettings()
|
||||||
// Now the rest - priority: user > local > global
|
// Now the rest - priority: user > local > global
|
||||||
QStringList paths;
|
QStringList paths;
|
||||||
paths.append(globalPath + QString("openmw.cfg"));
|
paths.append(globalPath + QString("openmw.cfg"));
|
||||||
paths.append(localPath + QString("openmw.cfg"));
|
paths.append(QString("openmw.cfg"));
|
||||||
paths.append(userPath + QString("openmw.cfg"));
|
paths.append(userPath + QString("openmw.cfg"));
|
||||||
|
|
||||||
foreach (const QString &path2, paths) {
|
foreach (const QString &path, paths) {
|
||||||
qDebug() << "Loading config file:" << path2.toUtf8().constData();
|
qDebug() << "Loading config file:" << path.toUtf8().constData();
|
||||||
|
|
||||||
file.setFileName(path2);
|
QFile file(path);
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
cfgError(tr("Error opening OpenMW configuration file"),
|
cfgError(tr("Error opening OpenMW configuration file"),
|
||||||
|
@ -357,21 +343,16 @@ bool Launcher::MainDialog::setupGameSettings()
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Launcher::MainDialog::setupGameData()
|
|
||||||
{
|
|
||||||
QStringList dataDirs;
|
QStringList dataDirs;
|
||||||
|
|
||||||
// Check if the paths actually contain data files
|
// Check if the paths actually contain data files
|
||||||
foreach (const QString path3, mGameSettings.getDataDirs()) {
|
foreach (const QString path, mGameSettings.getDataDirs()) {
|
||||||
QDir dir(path3);
|
QDir dir(path);
|
||||||
QStringList filters;
|
QStringList filters;
|
||||||
filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon";
|
filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon";
|
||||||
|
|
||||||
if (!dir.entryList(filters).isEmpty())
|
if (!dir.entryList(filters).isEmpty())
|
||||||
dataDirs.append(path3);
|
dataDirs.append(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dataDirs.isEmpty())
|
if (dataDirs.isEmpty())
|
||||||
|
@ -493,7 +474,6 @@ bool Launcher::MainDialog::writeSettings()
|
||||||
mDataFilesPage->saveSettings();
|
mDataFilesPage->saveSettings();
|
||||||
mGraphicsPage->saveSettings();
|
mGraphicsPage->saveSettings();
|
||||||
mSettingsPage->saveSettings();
|
mSettingsPage->saveSettings();
|
||||||
mAdvancedPage->saveSettings();
|
|
||||||
|
|
||||||
QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str());
|
QString userPath = QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str());
|
||||||
QDir dir(userPath);
|
QDir dir(userPath);
|
||||||
|
@ -599,6 +579,6 @@ void Launcher::MainDialog::play()
|
||||||
|
|
||||||
// Launch the game detached
|
// Launch the game detached
|
||||||
|
|
||||||
if (mGameInvoker->startProcess(QLatin1String("tes3mp-browser"), true))
|
if (mGameInvoker->startProcess(QLatin1String("openmw"), true))
|
||||||
return qApp->quit();
|
return qApp->quit();
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,6 @@ namespace Launcher
|
||||||
class DataFilesPage;
|
class DataFilesPage;
|
||||||
class UnshieldThread;
|
class UnshieldThread;
|
||||||
class SettingsPage;
|
class SettingsPage;
|
||||||
class AdvancedPage;
|
|
||||||
|
|
||||||
enum FirstRunDialogResult
|
enum FirstRunDialogResult
|
||||||
{
|
{
|
||||||
|
@ -73,7 +72,6 @@ namespace Launcher
|
||||||
bool setupLauncherSettings();
|
bool setupLauncherSettings();
|
||||||
bool setupGameSettings();
|
bool setupGameSettings();
|
||||||
bool setupGraphicsSettings();
|
bool setupGraphicsSettings();
|
||||||
bool setupGameData();
|
|
||||||
|
|
||||||
void setVersionLabel();
|
void setVersionLabel();
|
||||||
|
|
||||||
|
@ -89,7 +87,6 @@ namespace Launcher
|
||||||
GraphicsPage *mGraphicsPage;
|
GraphicsPage *mGraphicsPage;
|
||||||
DataFilesPage *mDataFilesPage;
|
DataFilesPage *mDataFilesPage;
|
||||||
SettingsPage *mSettingsPage;
|
SettingsPage *mSettingsPage;
|
||||||
AdvancedPage *mAdvancedPage;
|
|
||||||
|
|
||||||
Process::ProcessInvoker *mGameInvoker;
|
Process::ProcessInvoker *mGameInvoker;
|
||||||
Process::ProcessInvoker *mWizardInvoker;
|
Process::ProcessInvoker *mWizardInvoker;
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
#include "cellnameloader.hpp"
|
|
||||||
|
|
||||||
#include <components/esm/loadcell.hpp>
|
|
||||||
#include <components/contentselector/view/contentselector.hpp>
|
|
||||||
|
|
||||||
QSet<QString> CellNameLoader::getCellNames(QStringList &contentPaths)
|
|
||||||
{
|
|
||||||
QSet<QString> cellNames;
|
|
||||||
ESM::ESMReader esmReader;
|
|
||||||
|
|
||||||
// Loop through all content files
|
|
||||||
for (auto &contentPath : contentPaths) {
|
|
||||||
esmReader.open(contentPath.toStdString());
|
|
||||||
|
|
||||||
// Loop through all records
|
|
||||||
while(esmReader.hasMoreRecs())
|
|
||||||
{
|
|
||||||
ESM::NAME recordName = esmReader.getRecName();
|
|
||||||
esmReader.getRecHeader();
|
|
||||||
|
|
||||||
if (isCellRecord(recordName)) {
|
|
||||||
QString cellName = getCellName(esmReader);
|
|
||||||
if (!cellName.isEmpty()) {
|
|
||||||
cellNames.insert(cellName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop loading content for this record and continue to the next
|
|
||||||
esmReader.skipRecord();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cellNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CellNameLoader::isCellRecord(ESM::NAME &recordName)
|
|
||||||
{
|
|
||||||
return recordName.intval == ESM::REC_CELL;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString CellNameLoader::getCellName(ESM::ESMReader &esmReader)
|
|
||||||
{
|
|
||||||
ESM::Cell cell;
|
|
||||||
bool isDeleted = false;
|
|
||||||
cell.loadNameAndData(esmReader, isDeleted);
|
|
||||||
|
|
||||||
return QString::fromStdString(cell.mName);
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
#ifndef OPENMW_CELLNAMELOADER_H
|
|
||||||
#define OPENMW_CELLNAMELOADER_H
|
|
||||||
|
|
||||||
#include <QComboBox>
|
|
||||||
#include <QSet>
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
#include <components/esm/esmreader.hpp>
|
|
||||||
|
|
||||||
namespace ESM {class ESMReader; struct Cell;}
|
|
||||||
namespace ContentSelectorView {class ContentSelector;}
|
|
||||||
|
|
||||||
class CellNameLoader {
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the names of all cells contained within the given content files
|
|
||||||
* @param contentPaths the file paths of each content file to be examined
|
|
||||||
* @return the names of all cells
|
|
||||||
*/
|
|
||||||
QSet<QString> getCellNames(QStringList &contentPaths);
|
|
||||||
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* Returns whether or not the given record is of type "Cell"
|
|
||||||
* @param name The name associated with the record
|
|
||||||
* @return whether or not the given record is of type "Cell"
|
|
||||||
*/
|
|
||||||
bool isCellRecord(ESM::NAME &name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the name of the cell
|
|
||||||
* @param esmReader the reader currently pointed to a loaded cell
|
|
||||||
* @return the name of the cell
|
|
||||||
*/
|
|
||||||
QString getCellName(ESM::ESMReader &esmReader);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //OPENMW_CELLNAMELOADER_H
|
|
|
@ -1,32 +0,0 @@
|
||||||
project(masterserver)
|
|
||||||
|
|
||||||
#set(CMAKE_CXX_STANDARD 14)
|
|
||||||
add_definitions(-std=gnu++14)
|
|
||||||
|
|
||||||
include_directories("./")
|
|
||||||
|
|
||||||
set(SOURCE_FILES main.cpp MasterServer.cpp MasterServer.hpp RestServer.cpp RestServer.hpp)
|
|
||||||
|
|
||||||
add_executable(masterserver ${SOURCE_FILES})
|
|
||||||
target_link_libraries(masterserver ${RakNet_LIBRARY} components)
|
|
||||||
|
|
||||||
option(BUILD_MASTER_TEST "build master server test program" OFF)
|
|
||||||
|
|
||||||
if(BUILD_MASTER_TEST)
|
|
||||||
add_executable(ServerTest ServerTest.cpp)
|
|
||||||
target_link_libraries(ServerTest ${RakNet_LIBRARY} components)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (UNIX)
|
|
||||||
# Fix for not visible pthreads functions for linker with glibc 2.15
|
|
||||||
if(NOT APPLE)
|
|
||||||
target_link_libraries(masterserver ${CMAKE_THREAD_LIBS_INIT})
|
|
||||||
if(BUILD_MASTER_TEST)
|
|
||||||
target_link_libraries(ServerTest ${CMAKE_THREAD_LIBS_INIT})
|
|
||||||
endif()
|
|
||||||
endif(NOT APPLE)
|
|
||||||
endif(UNIX)
|
|
||||||
|
|
||||||
if(WIN32)
|
|
||||||
target_link_libraries(masterserver wsock32)
|
|
||||||
endif(WIN32)
|
|
|
@ -1,236 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 21.04.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <RakPeerInterface.h>
|
|
||||||
#include <RakSleep.h>
|
|
||||||
#include <BitStream.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include "MasterServer.hpp"
|
|
||||||
|
|
||||||
#include <components/openmw-mp/Master/PacketMasterQuery.hpp>
|
|
||||||
#include <components/openmw-mp/Master/PacketMasterUpdate.hpp>
|
|
||||||
#include <components/openmw-mp/Master/PacketMasterAnnounce.hpp>
|
|
||||||
#include <components/openmw-mp/Version.hpp>
|
|
||||||
|
|
||||||
using namespace RakNet;
|
|
||||||
using namespace std;
|
|
||||||
using namespace mwmp;
|
|
||||||
using namespace chrono;
|
|
||||||
|
|
||||||
MasterServer::MasterServer(unsigned short maxConnections, unsigned short port)
|
|
||||||
{
|
|
||||||
peer = RakPeerInterface::GetInstance();
|
|
||||||
sockdescr = SocketDescriptor(port, 0);
|
|
||||||
peer->Startup(maxConnections, &sockdescr, 1, 1000);
|
|
||||||
|
|
||||||
peer->SetMaximumIncomingConnections(maxConnections);
|
|
||||||
peer->SetIncomingPassword(TES3MP_MASTERSERVER_PASSW, (int) strlen(TES3MP_MASTERSERVER_PASSW));
|
|
||||||
run = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
MasterServer::~MasterServer()
|
|
||||||
{
|
|
||||||
Stop(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
using namespace chrono;
|
|
||||||
|
|
||||||
void MasterServer::Thread()
|
|
||||||
{
|
|
||||||
unsigned char packetId = 0;
|
|
||||||
|
|
||||||
auto startTime = chrono::steady_clock::now();
|
|
||||||
|
|
||||||
BitStream send;
|
|
||||||
PacketMasterQuery pmq(peer);
|
|
||||||
pmq.SetSendStream(&send);
|
|
||||||
|
|
||||||
PacketMasterUpdate pmu(peer);
|
|
||||||
pmu.SetSendStream(&send);
|
|
||||||
|
|
||||||
PacketMasterAnnounce pma(peer);
|
|
||||||
pma.SetSendStream(&send);
|
|
||||||
|
|
||||||
while (run)
|
|
||||||
{
|
|
||||||
Packet *packet = peer->Receive();
|
|
||||||
|
|
||||||
auto now = steady_clock::now();
|
|
||||||
if (now - startTime >= 60s)
|
|
||||||
{
|
|
||||||
startTime = steady_clock::now();
|
|
||||||
for (auto it = servers.begin(); it != servers.end();)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (it->second.lastUpdate + 60s <= now)
|
|
||||||
servers.erase(it++);
|
|
||||||
else ++it;
|
|
||||||
}
|
|
||||||
for(auto id = pendingACKs.begin(); id != pendingACKs.end();)
|
|
||||||
{
|
|
||||||
if(now - id->second >= 30s)
|
|
||||||
{
|
|
||||||
cout << "timeout: " << peer->GetSystemAddressFromGuid(id->first).ToString() << endl;
|
|
||||||
peer->CloseConnection(id->first, true);
|
|
||||||
id = pendingACKs.erase(id);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
++id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet == nullptr)
|
|
||||||
RakSleep(10);
|
|
||||||
else
|
|
||||||
for (; packet; peer->DeallocatePacket(packet), packet = peer->Receive())
|
|
||||||
{
|
|
||||||
BitStream data(packet->data, packet->length, false);
|
|
||||||
data.Read(packetId);
|
|
||||||
switch (packetId)
|
|
||||||
{
|
|
||||||
case ID_NEW_INCOMING_CONNECTION:
|
|
||||||
cout << "New incoming connection: " << packet->systemAddress.ToString() << endl;
|
|
||||||
break;
|
|
||||||
case ID_DISCONNECTION_NOTIFICATION:
|
|
||||||
cout << "Disconnected: " << packet->systemAddress.ToString() << endl;
|
|
||||||
break;
|
|
||||||
case ID_CONNECTION_LOST:
|
|
||||||
cout << "Connection lost: " << packet->systemAddress.ToString() << endl;
|
|
||||||
break;
|
|
||||||
case ID_MASTER_QUERY:
|
|
||||||
{
|
|
||||||
pmq.SetServers(reinterpret_cast<map<SystemAddress, QueryData> *>(&servers));
|
|
||||||
pmq.Send(packet->systemAddress);
|
|
||||||
pendingACKs[packet->guid] = steady_clock::now();
|
|
||||||
|
|
||||||
cout << "Sent info about all " << servers.size() << " servers to "
|
|
||||||
<< packet->systemAddress.ToString() << endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ID_MASTER_UPDATE:
|
|
||||||
{
|
|
||||||
SystemAddress addr;
|
|
||||||
data.Read(addr); // update 1 server
|
|
||||||
|
|
||||||
ServerIter it = servers.find(addr);
|
|
||||||
if (it != servers.end())
|
|
||||||
{
|
|
||||||
pair<SystemAddress, QueryData> pairPtr(it->first, static_cast<QueryData>(it->second));
|
|
||||||
pmu.SetServer(&pairPtr);
|
|
||||||
pmu.Send(packet->systemAddress);
|
|
||||||
pendingACKs[packet->guid] = steady_clock::now();
|
|
||||||
cout << "Sent info about " << addr.ToString() << " to " << packet->systemAddress.ToString()
|
|
||||||
<< endl;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ID_MASTER_ANNOUNCE:
|
|
||||||
{
|
|
||||||
ServerIter iter = servers.find(packet->systemAddress);
|
|
||||||
|
|
||||||
pma.SetReadStream(&data);
|
|
||||||
SServer server;
|
|
||||||
pma.SetServer(&server);
|
|
||||||
pma.Read();
|
|
||||||
|
|
||||||
auto keepAliveFunc = [&]() {
|
|
||||||
iter->second.lastUpdate = now;
|
|
||||||
pma.SetFunc(PacketMasterAnnounce::FUNCTION_KEEP);
|
|
||||||
pma.Send(packet->systemAddress);
|
|
||||||
pendingACKs[packet->guid] = steady_clock::now();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (iter != servers.end())
|
|
||||||
{
|
|
||||||
if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_DELETE)
|
|
||||||
{
|
|
||||||
servers.erase(iter);
|
|
||||||
cout << "Deleted";
|
|
||||||
pma.Send(packet->systemAddress);
|
|
||||||
pendingACKs[packet->guid] = steady_clock::now();
|
|
||||||
}
|
|
||||||
else if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_ANNOUNCE)
|
|
||||||
{
|
|
||||||
cout << "Updated";
|
|
||||||
iter->second = server;
|
|
||||||
keepAliveFunc();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cout << "Keeping alive";
|
|
||||||
keepAliveFunc();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_ANNOUNCE)
|
|
||||||
{
|
|
||||||
cout << "Added";
|
|
||||||
iter = servers.insert({packet->systemAddress, server}).first;
|
|
||||||
keepAliveFunc();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cout << "Unknown";
|
|
||||||
pma.SetFunc(PacketMasterAnnounce::FUNCTION_DELETE);
|
|
||||||
pma.Send(packet->systemAddress);
|
|
||||||
pendingACKs[packet->guid] = steady_clock::now();
|
|
||||||
}
|
|
||||||
cout << " server " << packet->systemAddress.ToString() << endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ID_SND_RECEIPT_ACKED:
|
|
||||||
uint32_t num;
|
|
||||||
memcpy(&num, packet->data+1, 4);
|
|
||||||
cout << "Packet with id " << num << " was delivered." << endl;
|
|
||||||
pendingACKs.erase(packet->guid);
|
|
||||||
peer->CloseConnection(packet->systemAddress, true);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
cout << "Wrong packet. id " << (unsigned) packet->data[0] << " packet length " << packet->length << " from " << packet->systemAddress.ToString() << endl;
|
|
||||||
peer->CloseConnection(packet->systemAddress, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
peer->Shutdown(1000);
|
|
||||||
RakPeerInterface::DestroyInstance(peer);
|
|
||||||
cout << "Server thread stopped" << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MasterServer::Start()
|
|
||||||
{
|
|
||||||
if (!run)
|
|
||||||
{
|
|
||||||
run = true;
|
|
||||||
tMasterThread = thread(&MasterServer::Thread, this);
|
|
||||||
cout << "Started" << endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MasterServer::Stop(bool wait)
|
|
||||||
{
|
|
||||||
if (run)
|
|
||||||
{
|
|
||||||
run = false;
|
|
||||||
if (wait && tMasterThread.joinable())
|
|
||||||
tMasterThread.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MasterServer::isRunning()
|
|
||||||
{
|
|
||||||
return run;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MasterServer::Wait()
|
|
||||||
{
|
|
||||||
if (run)
|
|
||||||
{
|
|
||||||
if (tMasterThread.joinable())
|
|
||||||
tMasterThread.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MasterServer::ServerMap *MasterServer::GetServers()
|
|
||||||
{
|
|
||||||
return &servers;
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 21.04.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef NEWMASTERPROTO_MASTERSERVER_HPP
|
|
||||||
#define NEWMASTERPROTO_MASTERSERVER_HPP
|
|
||||||
|
|
||||||
#include <thread>
|
|
||||||
#include <chrono>
|
|
||||||
#include <RakPeerInterface.h>
|
|
||||||
#include <components/openmw-mp/Master/MasterData.hpp>
|
|
||||||
|
|
||||||
class MasterServer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
struct Ban
|
|
||||||
{
|
|
||||||
RakNet::SystemAddress sa;
|
|
||||||
bool permanent;
|
|
||||||
struct Date
|
|
||||||
{
|
|
||||||
} date;
|
|
||||||
};
|
|
||||||
struct SServer : QueryData
|
|
||||||
{
|
|
||||||
std::chrono::steady_clock::time_point lastUpdate;
|
|
||||||
};
|
|
||||||
typedef std::map<RakNet::SystemAddress, SServer> ServerMap;
|
|
||||||
//typedef ServerMap::const_iterator ServerCIter;
|
|
||||||
typedef ServerMap::iterator ServerIter;
|
|
||||||
|
|
||||||
MasterServer(unsigned short maxConnections, unsigned short port);
|
|
||||||
~MasterServer();
|
|
||||||
|
|
||||||
void Start();
|
|
||||||
void Stop(bool wait = false);
|
|
||||||
bool isRunning();
|
|
||||||
void Wait();
|
|
||||||
|
|
||||||
ServerMap* GetServers();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void Thread();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::thread tMasterThread;
|
|
||||||
RakNet::RakPeerInterface* peer;
|
|
||||||
RakNet::SocketDescriptor sockdescr;
|
|
||||||
ServerMap servers;
|
|
||||||
bool run;
|
|
||||||
std::map<RakNet::RakNetGUID, std::chrono::steady_clock::time_point> pendingACKs;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //NEWMASTERPROTO_MASTERSERVER_HPP
|
|
|
@ -1,192 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 13.05.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "RestServer.hpp"
|
|
||||||
|
|
||||||
#include <boost/property_tree/ptree.hpp>
|
|
||||||
#include <boost/property_tree/json_parser.hpp>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
using namespace chrono;
|
|
||||||
using namespace boost::property_tree;
|
|
||||||
|
|
||||||
static string response201 = "HTTP/1.1 201 Created\r\nContent-Length: 7\r\n\r\nCreated";
|
|
||||||
static string response202 = "HTTP/1.1 202 Accepted\r\nContent-Length: 8\r\n\r\nAccepted";
|
|
||||||
static string response400 = "HTTP/1.1 400 Bad Request\r\nContent-Length: 11\r\n\r\nbad request";
|
|
||||||
|
|
||||||
inline void ResponseStr(HttpServer::Response &response, string content, string type = "", string code = "200 OK")
|
|
||||||
{
|
|
||||||
response << "HTTP/1.1 " << code << "\r\n";
|
|
||||||
if (!type.empty())
|
|
||||||
response << "Content-Type: " << type <<"\r\n";
|
|
||||||
response << "Content-Length: " << content.length() << "\r\n\r\n" << content;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void ptreeToServer(boost::property_tree::ptree &pt, MasterServer::SServer &server)
|
|
||||||
{
|
|
||||||
server.SetName(pt.get<string>("hostname").c_str());
|
|
||||||
server.SetGameMode(pt.get<string>("modname").c_str());
|
|
||||||
server.SetVersion(pt.get<string>("version").c_str());
|
|
||||||
server.SetPassword(pt.get<bool>("passw"));
|
|
||||||
//server.query_port = pt.get<unsigned short>("query_port");
|
|
||||||
server.SetPlayers(pt.get<unsigned>("players"));
|
|
||||||
server.SetMaxPlayers(pt.get<unsigned>("max_players"));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void queryToStringStream(stringstream &ss, string addr, MasterServer::SServer &query)
|
|
||||||
{
|
|
||||||
ss <<"\"" << addr << "\":{";
|
|
||||||
ss << "\"modname\": \"" << query.GetGameMode() << "\"" << ", ";
|
|
||||||
ss << "\"passw\": " << (query.GetPassword() ? "true" : "false") << ", ";
|
|
||||||
ss << "\"hostname\": \"" << query.GetName() << "\"" << ", ";
|
|
||||||
ss << "\"query_port\": " << 0 << ", ";
|
|
||||||
ss << "\"last_update\": " << duration_cast<seconds>(steady_clock::now() - query.lastUpdate).count() << ", ";
|
|
||||||
ss << "\"players\": " << query.GetPlayers() << ", ";
|
|
||||||
ss << "\"version\": \"" << query.GetVersion() << "\"" << ", ";
|
|
||||||
ss << "\"max_players\": " << query.GetMaxPlayers();
|
|
||||||
ss << "}";
|
|
||||||
}
|
|
||||||
|
|
||||||
RestServer::RestServer(unsigned short port, MasterServer::ServerMap *pMap) : serverMap(pMap)
|
|
||||||
{
|
|
||||||
httpServer.config.port = port;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RestServer::start()
|
|
||||||
{
|
|
||||||
static const string ValidIpAddressRegex = "(?:[0-9]{1,3}\\.){3}[0-9]{1,3}";
|
|
||||||
static const string ValidPortRegex = "(?:[0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$";
|
|
||||||
static const string ServersRegex = "^/api/servers(?:/(" + ValidIpAddressRegex + "\\:" + ValidPortRegex + "))?";
|
|
||||||
|
|
||||||
httpServer.resource[ServersRegex]["GET"] = [this](auto response, auto request) {
|
|
||||||
if (request->path_match[1].length() > 0)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
stringstream ss;
|
|
||||||
ss << "{";
|
|
||||||
auto addr = request->path_match[1].str();
|
|
||||||
auto port = (unsigned short)stoi(&(addr[addr.find(':')+1]));
|
|
||||||
queryToStringStream(ss, "server", serverMap->at(RakNet::SystemAddress(addr.c_str(), port)));
|
|
||||||
ss << "}";
|
|
||||||
ResponseStr(*response, ss.str(), "application/json");
|
|
||||||
}
|
|
||||||
catch(out_of_range e)
|
|
||||||
{
|
|
||||||
*response << response400;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
static string str;
|
|
||||||
|
|
||||||
//if (updatedCache)
|
|
||||||
{
|
|
||||||
stringstream ss;
|
|
||||||
ss << "{";
|
|
||||||
ss << "\"list servers\":{";
|
|
||||||
for (auto query = serverMap->begin(); query != serverMap->end(); query++)
|
|
||||||
{
|
|
||||||
queryToStringStream(ss, query->first.ToString(true, ':'), query->second);
|
|
||||||
if (next(query) != serverMap->end())
|
|
||||||
ss << ", ";
|
|
||||||
}
|
|
||||||
ss << "}}";
|
|
||||||
ResponseStr(*response, ss.str(), "application/json");
|
|
||||||
updatedCache = false;
|
|
||||||
}
|
|
||||||
*response << str;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//Add query for < 0.6 servers
|
|
||||||
httpServer.resource[ServersRegex]["POST"] = [this](auto response, auto request) {
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ptree pt;
|
|
||||||
read_json(request->content, pt);
|
|
||||||
|
|
||||||
MasterServer::SServer server;
|
|
||||||
ptreeToServer(pt, server);
|
|
||||||
|
|
||||||
unsigned short port = pt.get<unsigned short>("port");
|
|
||||||
server.lastUpdate = steady_clock::now();
|
|
||||||
serverMap->insert({RakNet::SystemAddress(request->remote_endpoint_address.c_str(), port), server});
|
|
||||||
updatedCache = true;
|
|
||||||
|
|
||||||
*response << response201;
|
|
||||||
}
|
|
||||||
catch (exception& e)
|
|
||||||
{
|
|
||||||
cout << e.what() << endl;
|
|
||||||
*response << response400;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//Update query for < 0.6 servers
|
|
||||||
httpServer.resource[ServersRegex]["PUT"] = [this](auto response, auto request) {
|
|
||||||
auto addr = request->path_match[1].str();
|
|
||||||
auto port = (unsigned short)stoi(&(addr[addr.find(':')+1]));
|
|
||||||
|
|
||||||
auto query = serverMap->find(RakNet::SystemAddress(request->remote_endpoint_address.c_str(), port));
|
|
||||||
|
|
||||||
if (query == serverMap->end())
|
|
||||||
{
|
|
||||||
cout << request->remote_endpoint_address + ": Trying to update a non-existent server or without permissions." << endl;
|
|
||||||
*response << response400;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request->content.size() != 0)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ptree pt;
|
|
||||||
read_json(request->content, pt);
|
|
||||||
|
|
||||||
ptreeToServer(pt, query->second);
|
|
||||||
|
|
||||||
updatedCache = true;
|
|
||||||
}
|
|
||||||
catch(exception &e)
|
|
||||||
{
|
|
||||||
cout << e.what() << endl;
|
|
||||||
*response << response400;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
query->second.lastUpdate = steady_clock::now();
|
|
||||||
|
|
||||||
*response << response202;
|
|
||||||
};
|
|
||||||
|
|
||||||
httpServer.resource["/api/servers/info"]["GET"] = [this](auto response, auto /*request*/) {
|
|
||||||
stringstream ss;
|
|
||||||
ss << '{';
|
|
||||||
ss << "\"servers\": " << serverMap->size();
|
|
||||||
unsigned int players = 0;
|
|
||||||
for (auto s : *serverMap)
|
|
||||||
players += s.second.GetPlayers();
|
|
||||||
ss << ", \"players\": " << players;
|
|
||||||
ss << "}";
|
|
||||||
|
|
||||||
ResponseStr(*response, ss.str(), "application/json");
|
|
||||||
};
|
|
||||||
|
|
||||||
httpServer.default_resource["GET"]=[](auto response, auto /*request*/) {
|
|
||||||
*response << response400;
|
|
||||||
};
|
|
||||||
|
|
||||||
httpServer.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RestServer::cacheUpdated()
|
|
||||||
{
|
|
||||||
updatedCache = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RestServer::stop()
|
|
||||||
{
|
|
||||||
httpServer.stop();
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 13.05.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef NEWRESTAPI_RESTSERVER_HPP
|
|
||||||
#define NEWRESTAPI_RESTSERVER_HPP
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include "MasterServer.hpp"
|
|
||||||
#include "SimpleWeb/http_server.hpp"
|
|
||||||
|
|
||||||
typedef SimpleWeb::Server<SimpleWeb::HTTP> HttpServer;
|
|
||||||
|
|
||||||
class RestServer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
RestServer(unsigned short port, MasterServer::ServerMap *pMap);
|
|
||||||
void start();
|
|
||||||
void stop();
|
|
||||||
void cacheUpdated();
|
|
||||||
|
|
||||||
private:
|
|
||||||
HttpServer httpServer;
|
|
||||||
MasterServer::ServerMap *serverMap;
|
|
||||||
bool updatedCache = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //NEWRESTAPI_RESTSERVER_HPP
|
|
|
@ -1,186 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 21.04.17.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <RakPeerInterface.h>
|
|
||||||
#include <RakSleep.h>
|
|
||||||
#include <BitStream.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <Kbhit.h>
|
|
||||||
#include <Gets.h>
|
|
||||||
#include <components/openmw-mp/Master/MasterData.hpp>
|
|
||||||
#include <components/openmw-mp/Master/PacketMasterAnnounce.hpp>
|
|
||||||
#include <components/openmw-mp/Master/PacketMasterUpdate.hpp>
|
|
||||||
#include <components/openmw-mp/Master/PacketMasterQuery.hpp>
|
|
||||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
using namespace RakNet;
|
|
||||||
using namespace mwmp;
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
cout << "Server test" << endl;
|
|
||||||
|
|
||||||
SystemAddress masterAddr("127.0.0.1", 25560);
|
|
||||||
|
|
||||||
RakPeerInterface *peer = RakNet::RakPeerInterface::GetInstance();
|
|
||||||
|
|
||||||
RakNet::SocketDescriptor sd(25565, 0);
|
|
||||||
peer->Startup(8, &sd, 1);
|
|
||||||
|
|
||||||
ConnectionAttemptResult result = peer->Connect(masterAddr.ToString(false), masterAddr.GetPort(), "pass",
|
|
||||||
(int)(strlen("pass")), 0, 0, 5, 500);
|
|
||||||
|
|
||||||
assert(result == RakNet::CONNECTION_ATTEMPT_STARTED);
|
|
||||||
|
|
||||||
char message[2048];
|
|
||||||
BitStream send;
|
|
||||||
|
|
||||||
PacketMasterQuery pmq(peer);
|
|
||||||
pmq.SetSendStream(&send);
|
|
||||||
|
|
||||||
PacketMasterAnnounce pma(peer);
|
|
||||||
pma.SetSendStream(&send);
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
RakSleep(30);
|
|
||||||
|
|
||||||
if (kbhit())
|
|
||||||
{
|
|
||||||
Gets(message, sizeof(message));
|
|
||||||
|
|
||||||
if (strcmp(message, "quit") == 0)
|
|
||||||
{
|
|
||||||
puts("Quitting.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (strcmp(message, "send") == 0)
|
|
||||||
{
|
|
||||||
puts("Sending data about server");
|
|
||||||
QueryData server;
|
|
||||||
server.SetName("Super Server");
|
|
||||||
server.SetPlayers(0);
|
|
||||||
server.SetMaxPlayers(0);
|
|
||||||
|
|
||||||
pma.SetServer(&server);
|
|
||||||
pma.SetFunc(PacketMasterAnnounce::FUNCTION_ANNOUNCE);
|
|
||||||
pma.Send(masterAddr);
|
|
||||||
}
|
|
||||||
else if (strcmp(message, "get") == 0)
|
|
||||||
{
|
|
||||||
puts("Request query info");
|
|
||||||
send.Reset();
|
|
||||||
send.Write((unsigned char) (ID_MASTER_QUERY));
|
|
||||||
peer->Send(&send, HIGH_PRIORITY, RELIABLE_ORDERED, CHANNEL_MASTER, masterAddr, false);
|
|
||||||
}
|
|
||||||
else if (strcmp(message, "getme") == 0)
|
|
||||||
{
|
|
||||||
send.Reset();
|
|
||||||
send.Write((unsigned char) (ID_MASTER_UPDATE));
|
|
||||||
send.Write(SystemAddress("127.0.0.1", 25565));
|
|
||||||
peer->Send(&send, HIGH_PRIORITY, RELIABLE_ORDERED, CHANNEL_MASTER, masterAddr, false);
|
|
||||||
}
|
|
||||||
else if (strcmp(message, "status") == 0)
|
|
||||||
{
|
|
||||||
cout << (peer->GetConnectionState(masterAddr) == IS_CONNECTED ? "Connected" : "Not connected") << endl;
|
|
||||||
}
|
|
||||||
else if (strcmp(message, "keep") == 0)
|
|
||||||
{
|
|
||||||
cout << "Sending keep alive" << endl;
|
|
||||||
pma.SetFunc(PacketMasterAnnounce::FUNCTION_KEEP);
|
|
||||||
pma.Send(masterAddr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (RakNet::Packet *packet = peer->Receive(); packet; peer->DeallocatePacket(packet), packet = peer->Receive())
|
|
||||||
{
|
|
||||||
BitStream data(packet->data, packet->length, false);
|
|
||||||
unsigned char packetID;
|
|
||||||
data.Read(packetID);
|
|
||||||
switch (packetID)
|
|
||||||
{
|
|
||||||
case ID_DISCONNECTION_NOTIFICATION:
|
|
||||||
// Connection lost normally
|
|
||||||
printf("ID_DISCONNECTION_NOTIFICATION\n");
|
|
||||||
break;
|
|
||||||
case ID_ALREADY_CONNECTED:
|
|
||||||
// Connection lost normally
|
|
||||||
printf("ID_ALREADY_CONNECTED with guid %lu\n", packet->guid.g);
|
|
||||||
break;
|
|
||||||
case ID_INCOMPATIBLE_PROTOCOL_VERSION:
|
|
||||||
printf("ID_INCOMPATIBLE_PROTOCOL_VERSION\n");
|
|
||||||
break;
|
|
||||||
case ID_REMOTE_DISCONNECTION_NOTIFICATION: // Server telling the clients of another client disconnecting gracefully. You can manually broadcast this in a peer to peer enviroment if you want.
|
|
||||||
printf("ID_REMOTE_DISCONNECTION_NOTIFICATION\n");
|
|
||||||
break;
|
|
||||||
case ID_REMOTE_CONNECTION_LOST: // Server telling the clients of another client disconnecting forcefully. You can manually broadcast this in a peer to peer enviroment if you want.
|
|
||||||
printf("ID_REMOTE_CONNECTION_LOST\n");
|
|
||||||
break;
|
|
||||||
case ID_REMOTE_NEW_INCOMING_CONNECTION: // Server telling the clients of another client connecting. You can manually broadcast this in a peer to peer enviroment if you want.
|
|
||||||
printf("ID_REMOTE_NEW_INCOMING_CONNECTION\n");
|
|
||||||
break;
|
|
||||||
case ID_CONNECTION_BANNED: // Banned from this server
|
|
||||||
printf("We are banned from this server.\n");
|
|
||||||
break;
|
|
||||||
case ID_CONNECTION_ATTEMPT_FAILED:
|
|
||||||
printf("Connection attempt failed\n");
|
|
||||||
break;
|
|
||||||
case ID_NO_FREE_INCOMING_CONNECTIONS:
|
|
||||||
// Sorry, the server is full. I don't do anything here but
|
|
||||||
// A real app should tell the user
|
|
||||||
printf("ID_NO_FREE_INCOMING_CONNECTIONS\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ID_INVALID_PASSWORD:
|
|
||||||
printf("ID_INVALID_PASSWORD\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ID_CONNECTION_LOST:
|
|
||||||
// Couldn't deliver a reliable packet - i.e. the other system was abnormally
|
|
||||||
// terminated
|
|
||||||
printf("ID_CONNECTION_LOST\n");
|
|
||||||
return 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ID_CONNECTION_REQUEST_ACCEPTED:
|
|
||||||
// This tells the client they have connected
|
|
||||||
printf("ID_CONNECTION_REQUEST_ACCEPTED to %s with GUID %s\n", packet->systemAddress.ToString(true),
|
|
||||||
packet->guid.ToString());
|
|
||||||
printf("My external address is %s\n", peer->GetExternalID(packet->systemAddress).ToString(true));
|
|
||||||
break;
|
|
||||||
case ID_MASTER_QUERY:
|
|
||||||
{
|
|
||||||
map<SystemAddress, QueryData> servers;
|
|
||||||
|
|
||||||
pmq.SetReadStream(&data);
|
|
||||||
pmq.SetServers(&servers);
|
|
||||||
pmq.Read();
|
|
||||||
|
|
||||||
cout << "Received query data about " << servers.size() << " servers" << endl;
|
|
||||||
|
|
||||||
for (auto serv : servers)
|
|
||||||
cout << serv.second.GetName() << endl;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ID_MASTER_UPDATE:
|
|
||||||
{
|
|
||||||
pair<SystemAddress, QueryData> serverPair;
|
|
||||||
PacketMasterUpdate pmu(peer);
|
|
||||||
pmu.SetReadStream(&data);
|
|
||||||
pmu.SetServer(&serverPair);
|
|
||||||
pmu.Read();
|
|
||||||
cout << "Received info about " << serverPair.first.ToString() << endl;
|
|
||||||
cout << serverPair.second.GetName() << endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
cout << "Wrong packet" << endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
peer->Shutdown(1000);
|
|
||||||
RakPeerInterface::DestroyInstance(peer);
|
|
||||||
}
|
|
|
@ -1,511 +0,0 @@
|
||||||
#ifndef BASE_SERVER_HPP
|
|
||||||
#define BASE_SERVER_HPP
|
|
||||||
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
|
||||||
#include <boost/functional/hash.hpp>
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <thread>
|
|
||||||
#include <functional>
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <regex>
|
|
||||||
|
|
||||||
#ifndef CASE_INSENSITIVE_EQUALS_AND_HASH
|
|
||||||
#define CASE_INSENSITIVE_EQUALS_AND_HASH
|
|
||||||
|
|
||||||
//Based on http://www.boost.org/doc/libs/1_60_0/doc/html/unordered/hash_equality.html
|
|
||||||
struct case_insensitive_equals
|
|
||||||
{
|
|
||||||
bool operator()(const std::string &key1, const std::string &key2) const
|
|
||||||
{
|
|
||||||
return boost::algorithm::iequals(key1, key2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct case_insensitive_hash
|
|
||||||
{
|
|
||||||
size_t operator()(const std::string &key) const
|
|
||||||
{
|
|
||||||
std::size_t seed = 0;
|
|
||||||
for (auto &c: key)
|
|
||||||
boost::hash_combine(seed, std::tolower(c));
|
|
||||||
return seed;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace SimpleWeb
|
|
||||||
{
|
|
||||||
template<class socket_type>
|
|
||||||
class Server;
|
|
||||||
|
|
||||||
template<class socket_type>
|
|
||||||
class ServerBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~ServerBase()
|
|
||||||
{}
|
|
||||||
|
|
||||||
class Response : public std::ostream
|
|
||||||
{
|
|
||||||
friend class ServerBase<socket_type>;
|
|
||||||
|
|
||||||
boost::asio::streambuf streambuf;
|
|
||||||
|
|
||||||
std::shared_ptr<socket_type> socket;
|
|
||||||
|
|
||||||
Response(const std::shared_ptr<socket_type> &socket) : std::ostream(&streambuf), socket(socket)
|
|
||||||
{}
|
|
||||||
|
|
||||||
public:
|
|
||||||
size_t size()
|
|
||||||
{
|
|
||||||
return streambuf.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If true, force server to close the connection after the response have been sent.
|
|
||||||
///
|
|
||||||
/// This is useful when implementing a HTTP/1.0-server sending content
|
|
||||||
/// without specifying the content length.
|
|
||||||
bool close_connection_after_response = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Content : public std::istream
|
|
||||||
{
|
|
||||||
friend class ServerBase<socket_type>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
size_t size()
|
|
||||||
{
|
|
||||||
return streambuf.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string string()
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << rdbuf();
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
boost::asio::streambuf &streambuf;
|
|
||||||
|
|
||||||
Content(boost::asio::streambuf &streambuf) : std::istream(&streambuf), streambuf(streambuf)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Request
|
|
||||||
{
|
|
||||||
friend class ServerBase<socket_type>;
|
|
||||||
|
|
||||||
friend class Server<socket_type>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
std::string method, path, http_version;
|
|
||||||
|
|
||||||
Content content;
|
|
||||||
|
|
||||||
std::unordered_multimap<std::string, std::string, case_insensitive_hash, case_insensitive_equals> header;
|
|
||||||
|
|
||||||
std::smatch path_match;
|
|
||||||
|
|
||||||
std::string remote_endpoint_address;
|
|
||||||
unsigned short remote_endpoint_port;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Request(const socket_type &socket) : content(streambuf)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
remote_endpoint_address = socket.lowest_layer().remote_endpoint().address().to_string();
|
|
||||||
remote_endpoint_port = socket.lowest_layer().remote_endpoint().port();
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{}
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::asio::streambuf streambuf;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Config
|
|
||||||
{
|
|
||||||
friend class ServerBase<socket_type>;
|
|
||||||
|
|
||||||
Config(unsigned short port) : port(port)
|
|
||||||
{}
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// Port number to use. Defaults to 80 for HTTP and 443 for HTTPS.
|
|
||||||
unsigned short port;
|
|
||||||
/// Number of threads that the server will use when start() is called. Defaults to 1 thread.
|
|
||||||
size_t thread_pool_size = 1;
|
|
||||||
/// Timeout on request handling. Defaults to 5 seconds.
|
|
||||||
size_t timeout_request = 5;
|
|
||||||
/// Timeout on content handling. Defaults to 300 seconds.
|
|
||||||
size_t timeout_content = 300;
|
|
||||||
/// IPv4 address in dotted decimal form or IPv6 address in hexadecimal notation.
|
|
||||||
/// If empty, the address will be any address.
|
|
||||||
std::string address;
|
|
||||||
/// Set to false to avoid binding the socket to an address that is already in use. Defaults to true.
|
|
||||||
bool reuse_address = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
///Set before calling start().
|
|
||||||
Config config;
|
|
||||||
|
|
||||||
private:
|
|
||||||
class regex_orderable : public std::regex
|
|
||||||
{
|
|
||||||
std::string str;
|
|
||||||
public:
|
|
||||||
regex_orderable(const char *regex_cstr) : std::regex(regex_cstr), str(regex_cstr)
|
|
||||||
{}
|
|
||||||
|
|
||||||
regex_orderable(const std::string ®ex_str) : std::regex(regex_str), str(regex_str)
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool operator<(const regex_orderable &rhs) const
|
|
||||||
{
|
|
||||||
return str < rhs.str;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// Warning: do not add or remove resources after start() is called
|
|
||||||
std::map<regex_orderable, std::map<std::string,
|
|
||||||
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>,
|
|
||||||
std::shared_ptr<typename ServerBase<socket_type>::Request>)>>>
|
|
||||||
resource;
|
|
||||||
|
|
||||||
std::map<std::string,
|
|
||||||
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>,
|
|
||||||
std::shared_ptr<typename ServerBase<socket_type>::Request>)>> default_resource;
|
|
||||||
|
|
||||||
std::function<
|
|
||||||
void(std::shared_ptr<typename ServerBase<socket_type>::Request>,
|
|
||||||
const boost::system::error_code &)>
|
|
||||||
on_error;
|
|
||||||
|
|
||||||
std::function<void(std::shared_ptr<socket_type> socket,
|
|
||||||
std::shared_ptr<typename ServerBase<socket_type>::Request>)> on_upgrade;
|
|
||||||
|
|
||||||
virtual void start()
|
|
||||||
{
|
|
||||||
if (!io_service)
|
|
||||||
io_service = std::make_shared<boost::asio::io_service>();
|
|
||||||
|
|
||||||
if (io_service->stopped())
|
|
||||||
io_service->reset();
|
|
||||||
|
|
||||||
boost::asio::ip::tcp::endpoint endpoint;
|
|
||||||
if (config.address.size() > 0)
|
|
||||||
endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(config.address),
|
|
||||||
config.port);
|
|
||||||
else
|
|
||||||
endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), config.port);
|
|
||||||
|
|
||||||
if (!acceptor)
|
|
||||||
acceptor = std::unique_ptr<boost::asio::ip::tcp::acceptor>(
|
|
||||||
new boost::asio::ip::tcp::acceptor(*io_service));
|
|
||||||
acceptor->open(endpoint.protocol());
|
|
||||||
acceptor->set_option(boost::asio::socket_base::reuse_address(config.reuse_address));
|
|
||||||
acceptor->bind(endpoint);
|
|
||||||
acceptor->listen();
|
|
||||||
|
|
||||||
accept();
|
|
||||||
|
|
||||||
//If thread_pool_size>1, start m_io_service.run() in (thread_pool_size-1) threads for thread-pooling
|
|
||||||
threads.clear();
|
|
||||||
for (size_t c = 1; c < config.thread_pool_size; c++)
|
|
||||||
{
|
|
||||||
threads.emplace_back([this]()
|
|
||||||
{
|
|
||||||
io_service->run();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//Main thread
|
|
||||||
if (config.thread_pool_size > 0)
|
|
||||||
io_service->run();
|
|
||||||
|
|
||||||
//Wait for the rest of the threads, if any, to finish as well
|
|
||||||
for (auto &t: threads)
|
|
||||||
{
|
|
||||||
t.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void stop()
|
|
||||||
{
|
|
||||||
acceptor->close();
|
|
||||||
if (config.thread_pool_size > 0)
|
|
||||||
io_service->stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
///Use this function if you need to recursively send parts of a longer message
|
|
||||||
void send(const std::shared_ptr<Response> &response,
|
|
||||||
const std::function<void(const boost::system::error_code &)> &callback = nullptr) const
|
|
||||||
{
|
|
||||||
boost::asio::async_write(*response->socket, response->streambuf, [this, response, callback]
|
|
||||||
(const boost::system::error_code &ec, size_t /*bytes_transferred*/)
|
|
||||||
{
|
|
||||||
if (callback)
|
|
||||||
callback(ec);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If you have your own boost::asio::io_service, store its pointer here before running start().
|
|
||||||
/// You might also want to set config.thread_pool_size to 0.
|
|
||||||
std::shared_ptr<boost::asio::io_service> io_service;
|
|
||||||
protected:
|
|
||||||
std::unique_ptr<boost::asio::ip::tcp::acceptor> acceptor;
|
|
||||||
std::vector<std::thread> threads;
|
|
||||||
|
|
||||||
ServerBase(unsigned short port) : config(port)
|
|
||||||
{}
|
|
||||||
|
|
||||||
virtual void accept()=0;
|
|
||||||
|
|
||||||
std::shared_ptr<boost::asio::deadline_timer>
|
|
||||||
get_timeout_timer(const std::shared_ptr<socket_type> &socket, long seconds)
|
|
||||||
{
|
|
||||||
if (seconds == 0)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
auto timer = std::make_shared<boost::asio::deadline_timer>(*io_service);
|
|
||||||
timer->expires_from_now(boost::posix_time::seconds(seconds));
|
|
||||||
timer->async_wait([socket](const boost::system::error_code &ec)
|
|
||||||
{
|
|
||||||
if (!ec)
|
|
||||||
{
|
|
||||||
boost::system::error_code ec;
|
|
||||||
socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
|
|
||||||
socket->lowest_layer().close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return timer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void read_request_and_content(const std::shared_ptr<socket_type> &socket)
|
|
||||||
{
|
|
||||||
//Create new streambuf (Request::streambuf) for async_read_until()
|
|
||||||
//shared_ptr is used to pass temporary objects to the asynchronous functions
|
|
||||||
std::shared_ptr<Request> request(new Request(*socket));
|
|
||||||
|
|
||||||
//Set timeout on the following boost::asio::async-read or write function
|
|
||||||
auto timer = this->get_timeout_timer(socket, config.timeout_request);
|
|
||||||
|
|
||||||
boost::asio::async_read_until(*socket, request->streambuf, "\r\n\r\n", [this, socket, request, timer]
|
|
||||||
(const boost::system::error_code &ec,
|
|
||||||
size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (timer)
|
|
||||||
timer->cancel();
|
|
||||||
if (!ec)
|
|
||||||
{
|
|
||||||
//request->streambuf.size() is not necessarily the same as bytes_transferred, from Boost-docs:
|
|
||||||
//"After a successful async_read_until operation, the streambuf may contain additional data beyond the delimiter"
|
|
||||||
//The chosen solution is to extract lines from the stream directly when parsing the header. What is left of the
|
|
||||||
//streambuf (maybe some bytes of the content) is appended to in the async_read-function below (for retrieving content).
|
|
||||||
size_t num_additional_bytes =
|
|
||||||
request->streambuf.size() - bytes_transferred;
|
|
||||||
|
|
||||||
if (!this->parse_request(request))
|
|
||||||
return;
|
|
||||||
|
|
||||||
//If content, read that as well
|
|
||||||
auto it = request->header.find("Content-Length");
|
|
||||||
if (it != request->header.end())
|
|
||||||
{
|
|
||||||
unsigned long long content_length;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
content_length = stoull(it->second);
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
if (on_error)
|
|
||||||
on_error(request, boost::system::error_code(
|
|
||||||
boost::system::errc::protocol_error,
|
|
||||||
boost::system::generic_category()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (content_length > num_additional_bytes)
|
|
||||||
{
|
|
||||||
//Set timeout on the following boost::asio::async-read or write function
|
|
||||||
auto timer = this->get_timeout_timer(socket,
|
|
||||||
config.timeout_content);
|
|
||||||
boost::asio::async_read(*socket, request->streambuf,
|
|
||||||
boost::asio::transfer_exactly(
|
|
||||||
content_length -
|
|
||||||
num_additional_bytes),
|
|
||||||
[this, socket, request, timer]
|
|
||||||
(const boost::system::error_code &ec,
|
|
||||||
size_t /*bytes_transferred*/)
|
|
||||||
{
|
|
||||||
if (timer)
|
|
||||||
timer->cancel();
|
|
||||||
if (!ec)
|
|
||||||
this->find_resource(socket,
|
|
||||||
request);
|
|
||||||
else if (on_error)
|
|
||||||
on_error(request, ec);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
this->find_resource(socket, request);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
this->find_resource(socket, request);
|
|
||||||
}
|
|
||||||
else if (on_error)
|
|
||||||
on_error(request, ec);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool parse_request(const std::shared_ptr<Request> &request) const
|
|
||||||
{
|
|
||||||
std::string line;
|
|
||||||
getline(request->content, line);
|
|
||||||
size_t method_end;
|
|
||||||
if ((method_end = line.find(' ')) != std::string::npos)
|
|
||||||
{
|
|
||||||
size_t path_end;
|
|
||||||
if ((path_end = line.find(' ', method_end + 1)) != std::string::npos)
|
|
||||||
{
|
|
||||||
request->method = line.substr(0, method_end);
|
|
||||||
request->path = line.substr(method_end + 1, path_end - method_end - 1);
|
|
||||||
|
|
||||||
size_t protocol_end;
|
|
||||||
if ((protocol_end = line.find('/', path_end + 1)) != std::string::npos)
|
|
||||||
{
|
|
||||||
if (line.compare(path_end + 1, protocol_end - path_end - 1, "HTTP") != 0)
|
|
||||||
return false;
|
|
||||||
request->http_version = line.substr(protocol_end + 1, line.size() - protocol_end - 2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
|
|
||||||
getline(request->content, line);
|
|
||||||
size_t param_end;
|
|
||||||
while ((param_end = line.find(':')) != std::string::npos)
|
|
||||||
{
|
|
||||||
size_t value_start = param_end + 1;
|
|
||||||
if ((value_start) < line.size())
|
|
||||||
{
|
|
||||||
if (line[value_start] == ' ')
|
|
||||||
value_start++;
|
|
||||||
if (value_start < line.size())
|
|
||||||
request->header.emplace(line.substr(0, param_end),
|
|
||||||
line.substr(value_start, line.size() - value_start - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
getline(request->content, line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void find_resource(const std::shared_ptr<socket_type> &socket, const std::shared_ptr<Request> &request)
|
|
||||||
{
|
|
||||||
//Upgrade connection
|
|
||||||
if (on_upgrade)
|
|
||||||
{
|
|
||||||
auto it = request->header.find("Upgrade");
|
|
||||||
if (it != request->header.end())
|
|
||||||
{
|
|
||||||
on_upgrade(socket, request);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//Find path- and method-match, and call write_response
|
|
||||||
for (auto ®ex_method: resource)
|
|
||||||
{
|
|
||||||
auto it = regex_method.second.find(request->method);
|
|
||||||
if (it != regex_method.second.end())
|
|
||||||
{
|
|
||||||
std::smatch sm_res;
|
|
||||||
if (std::regex_match(request->path, sm_res, regex_method.first))
|
|
||||||
{
|
|
||||||
request->path_match = std::move(sm_res);
|
|
||||||
write_response(socket, request, it->second);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto it = default_resource.find(request->method);
|
|
||||||
if (it != default_resource.end())
|
|
||||||
{
|
|
||||||
write_response(socket, request, it->second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_response(const std::shared_ptr<socket_type> &socket, const std::shared_ptr<Request> &request,
|
|
||||||
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>,
|
|
||||||
std::shared_ptr<
|
|
||||||
typename ServerBase<socket_type>::Request>)> &resource_function)
|
|
||||||
{
|
|
||||||
//Set timeout on the following boost::asio::async-read or write function
|
|
||||||
auto timer = this->get_timeout_timer(socket, config.timeout_content);
|
|
||||||
|
|
||||||
auto response = std::shared_ptr<Response>(new Response(socket), [this, request, timer]
|
|
||||||
(Response *response_ptr)
|
|
||||||
{
|
|
||||||
auto response = std::shared_ptr<Response>(response_ptr);
|
|
||||||
this->send(response, [this, response, request, timer](
|
|
||||||
const boost::system::error_code &ec)
|
|
||||||
{
|
|
||||||
if (timer)
|
|
||||||
timer->cancel();
|
|
||||||
if (!ec)
|
|
||||||
{
|
|
||||||
if (response->close_connection_after_response)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto range = request->header.equal_range(
|
|
||||||
"Connection");
|
|
||||||
for (auto it = range.first; it != range.second; it++)
|
|
||||||
{
|
|
||||||
if (boost::iequals(it->second, "close"))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (boost::iequals(it->second, "keep-alive"))
|
|
||||||
{
|
|
||||||
this->read_request_and_content(
|
|
||||||
response->socket);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (request->http_version >= "1.1")
|
|
||||||
this->read_request_and_content(response->socket);
|
|
||||||
}
|
|
||||||
else if (on_error)
|
|
||||||
on_error(request, ec);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
resource_function(response, request);
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
if (on_error)
|
|
||||||
on_error(request, boost::system::error_code(boost::system::errc::operation_canceled,
|
|
||||||
boost::system::generic_category()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#endif //BASE_SERVER_HPP
|
|
|
@ -1,55 +0,0 @@
|
||||||
/*
|
|
||||||
* https://github.com/eidheim/Simple-Web-Server/
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
* Copyright (c) 2014-2016 Ole Christian Eidheim
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SERVER_HTTP_HPP
|
|
||||||
#define SERVER_HTTP_HPP
|
|
||||||
|
|
||||||
#include "base_server.hpp"
|
|
||||||
|
|
||||||
namespace SimpleWeb
|
|
||||||
{
|
|
||||||
|
|
||||||
template<class socket_type>
|
|
||||||
class Server : public ServerBase<socket_type> {};
|
|
||||||
|
|
||||||
typedef boost::asio::ip::tcp::socket HTTP;
|
|
||||||
|
|
||||||
template<>
|
|
||||||
class Server<HTTP> : public ServerBase<HTTP>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Server() : ServerBase<HTTP>::ServerBase(80)
|
|
||||||
{}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void accept()
|
|
||||||
{
|
|
||||||
//Create new socket for this connection
|
|
||||||
//Shared_ptr is used to pass temporary objects to the asynchronous functions
|
|
||||||
auto socket = std::make_shared<HTTP>(*io_service);
|
|
||||||
|
|
||||||
acceptor->async_accept(*socket, [this, socket](const boost::system::error_code &ec)
|
|
||||||
{
|
|
||||||
//Immediately start accepting a new connection (if io_service hasn't been stopped)
|
|
||||||
if (ec != boost::asio::error::operation_aborted)
|
|
||||||
accept();
|
|
||||||
|
|
||||||
if (!ec)
|
|
||||||
{
|
|
||||||
boost::asio::ip::tcp::no_delay option(true);
|
|
||||||
socket->set_option(option);
|
|
||||||
|
|
||||||
this->read_request_and_content(socket);
|
|
||||||
}
|
|
||||||
else if (on_error)
|
|
||||||
on_error(std::shared_ptr<Request>(new Request(*socket)), ec);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif //SERVER_HTTP_HPP
|
|
|
@ -1,91 +0,0 @@
|
||||||
#ifndef HTTPS_SERVER_HPP
|
|
||||||
#define HTTPS_SERVER_HPP
|
|
||||||
|
|
||||||
#include "base_server.hpp"
|
|
||||||
#include <boost/asio/ssl.hpp>
|
|
||||||
#include <openssl/ssl.h>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace SimpleWeb
|
|
||||||
{
|
|
||||||
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> HTTPS;
|
|
||||||
|
|
||||||
template<>
|
|
||||||
class Server<HTTPS> : public ServerBase<HTTPS>
|
|
||||||
{
|
|
||||||
std::string session_id_context;
|
|
||||||
bool set_session_id_context = false;
|
|
||||||
public:
|
|
||||||
Server(const std::string &cert_file, const std::string &private_key_file,
|
|
||||||
const std::string &verify_file = std::string()) : ServerBase<HTTPS>::ServerBase(443),
|
|
||||||
context(boost::asio::ssl::context::tlsv12)
|
|
||||||
{
|
|
||||||
context.use_certificate_chain_file(cert_file);
|
|
||||||
context.use_private_key_file(private_key_file, boost::asio::ssl::context::pem);
|
|
||||||
|
|
||||||
if (verify_file.size() > 0)
|
|
||||||
{
|
|
||||||
context.load_verify_file(verify_file);
|
|
||||||
context.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert |
|
|
||||||
boost::asio::ssl::verify_client_once);
|
|
||||||
set_session_id_context = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void start()
|
|
||||||
{
|
|
||||||
if (set_session_id_context)
|
|
||||||
{
|
|
||||||
// Creating session_id_context from address:port but reversed due to small SSL_MAX_SSL_SESSION_ID_LENGTH
|
|
||||||
session_id_context = std::to_string(config.port) + ':';
|
|
||||||
session_id_context.append(config.address.rbegin(), config.address.rend());
|
|
||||||
SSL_CTX_set_session_id_context(context.native_handle(),
|
|
||||||
reinterpret_cast<const unsigned char *>(session_id_context.data()),
|
|
||||||
std::min<size_t>(session_id_context.size(),
|
|
||||||
SSL_MAX_SSL_SESSION_ID_LENGTH));
|
|
||||||
}
|
|
||||||
ServerBase::start();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
boost::asio::ssl::context context;
|
|
||||||
|
|
||||||
virtual void accept()
|
|
||||||
{
|
|
||||||
//Create new socket for this connection
|
|
||||||
//Shared_ptr is used to pass temporary objects to the asynchronous functions
|
|
||||||
auto socket = std::make_shared<HTTPS>(*io_service, context);
|
|
||||||
|
|
||||||
acceptor->async_accept((*socket).lowest_layer(), [this, socket](const boost::system::error_code &ec)
|
|
||||||
{
|
|
||||||
//Immediately start accepting a new connection (if io_service hasn't been stopped)
|
|
||||||
if (ec != boost::asio::error::operation_aborted)
|
|
||||||
accept();
|
|
||||||
|
|
||||||
|
|
||||||
if (!ec)
|
|
||||||
{
|
|
||||||
boost::asio::ip::tcp::no_delay option(true);
|
|
||||||
socket->lowest_layer().set_option(option);
|
|
||||||
|
|
||||||
//Set timeout on the following boost::asio::ssl::stream::async_handshake
|
|
||||||
auto timer = get_timeout_timer(socket, config.timeout_request);
|
|
||||||
socket->async_handshake(boost::asio::ssl::stream_base::server, [this, socket, timer]
|
|
||||||
(const boost::system::error_code &ec)
|
|
||||||
{
|
|
||||||
if (timer)
|
|
||||||
timer->cancel();
|
|
||||||
if (!ec)
|
|
||||||
read_request_and_content(socket);
|
|
||||||
else if (on_error)
|
|
||||||
on_error(std::shared_ptr<Request>(new Request(*socket)), ec);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (on_error)
|
|
||||||
on_error(std::shared_ptr<Request>(new Request(*socket)), ec);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif //HTTPS_SERVER_HPP
|
|
|
@ -1,37 +0,0 @@
|
||||||
#include <iostream>
|
|
||||||
#include <Kbhit.h>
|
|
||||||
#include <RakSleep.h>
|
|
||||||
#include "MasterServer.hpp"
|
|
||||||
#include "RestServer.hpp"
|
|
||||||
|
|
||||||
using namespace RakNet;
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
unique_ptr<RestServer> restServer;
|
|
||||||
unique_ptr<MasterServer> masterServer;
|
|
||||||
bool run = true;
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
masterServer.reset(new MasterServer(2000, 25560));
|
|
||||||
restServer.reset(new RestServer(8080, masterServer->GetServers()));
|
|
||||||
|
|
||||||
auto onExit = [](int /*sig*/){
|
|
||||||
restServer->stop();
|
|
||||||
masterServer->Stop(false);
|
|
||||||
masterServer->Wait();
|
|
||||||
run = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
signal(SIGINT, onExit);
|
|
||||||
signal(SIGTERM, onExit);
|
|
||||||
|
|
||||||
masterServer->Start();
|
|
||||||
|
|
||||||
thread server_thread([]() { restServer->start(); });
|
|
||||||
|
|
||||||
server_thread.join();
|
|
||||||
masterServer->Wait();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -9,7 +9,7 @@ set(MWINIIMPORT_HEADER
|
||||||
|
|
||||||
source_group(launcher FILES ${MWINIIMPORT} ${MWINIIMPORT_HEADER})
|
source_group(launcher FILES ${MWINIIMPORT} ${MWINIIMPORT_HEADER})
|
||||||
|
|
||||||
openmw_add_executable(openmw-iniimporter
|
add_executable(openmw-iniimporter
|
||||||
${MWINIIMPORT}
|
${MWINIIMPORT}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,8 +22,7 @@ target_link_libraries(openmw-iniimporter
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
target_link_libraries(openmw-iniimporter
|
target_link_libraries(openmw-iniimporter
|
||||||
${Boost_LOCALE_LIBRARY})
|
${Boost_LOCALE_LIBRARY})
|
||||||
INSTALL(TARGETS openmw-iniimporter RUNTIME DESTINATION ".")
|
endif()
|
||||||
endif(WIN32)
|
|
||||||
|
|
||||||
if (MINGW)
|
if (MINGW)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -municode")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -municode")
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
#include "importer.hpp"
|
#include "importer.hpp"
|
||||||
|
|
||||||
|
#include <ctime>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <components/misc/stringops.hpp>
|
#include <components/misc/stringops.hpp>
|
||||||
#include <components/esm/esmreader.hpp>
|
|
||||||
|
|
||||||
|
#include <boost/version.hpp>
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/filesystem/fstream.hpp>
|
#include <boost/filesystem/fstream.hpp>
|
||||||
#include <boost/version.hpp>
|
|
||||||
|
|
||||||
namespace bfs = boost::filesystem;
|
namespace bfs = boost::filesystem;
|
||||||
|
|
||||||
|
@ -654,6 +658,12 @@ void MwIniImporter::setVerbose(bool verbose) {
|
||||||
mVerbose = verbose;
|
mVerbose = verbose;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string MwIniImporter::numberToString(int n) {
|
||||||
|
std::stringstream str;
|
||||||
|
str << n;
|
||||||
|
return str.str();
|
||||||
|
}
|
||||||
|
|
||||||
MwIniImporter::multistrmap MwIniImporter::loadIniFile(const boost::filesystem::path& filename) const {
|
MwIniImporter::multistrmap MwIniImporter::loadIniFile(const boost::filesystem::path& filename) const {
|
||||||
std::cout << "load ini file: " << filename << std::endl;
|
std::cout << "load ini file: " << filename << std::endl;
|
||||||
|
|
||||||
|
@ -795,7 +805,7 @@ void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) con
|
||||||
multistrmap::const_iterator it = ini.begin();
|
multistrmap::const_iterator it = ini.begin();
|
||||||
for(int i=0; it != ini.end(); i++) {
|
for(int i=0; it != ini.end(); i++) {
|
||||||
archive = baseArchive;
|
archive = baseArchive;
|
||||||
archive.append(std::to_string(i));
|
archive.append(this->numberToString(i));
|
||||||
|
|
||||||
it = ini.find(archive);
|
it = ini.find(archive);
|
||||||
if(it == ini.end()) {
|
if(it == ini.end()) {
|
||||||
|
@ -814,109 +824,38 @@ void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) con
|
||||||
// does not appears in the ini file
|
// does not appears in the ini file
|
||||||
cfg["fallback-archive"].push_back("Morrowind.bsa");
|
cfg["fallback-archive"].push_back("Morrowind.bsa");
|
||||||
|
|
||||||
for(std::vector<std::string>::const_iterator iter=archives.begin(); iter!=archives.end(); ++iter) {
|
for(std::vector<std::string>::const_iterator it=archives.begin(); it!=archives.end(); ++it) {
|
||||||
cfg["fallback-archive"].push_back(*iter);
|
cfg["fallback-archive"].push_back(*it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MwIniImporter::dependencySortStep(std::string& element, MwIniImporter::dependencyList& source, std::vector<std::string>& result)
|
void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, const boost::filesystem::path& iniFilename) const {
|
||||||
{
|
std::vector<std::pair<std::time_t, std::string> > contentFiles;
|
||||||
auto iter = std::find_if(
|
|
||||||
source.begin(),
|
|
||||||
source.end(),
|
|
||||||
[&element](std::pair< std::string, std::vector<std::string> >& sourceElement)
|
|
||||||
{
|
|
||||||
return sourceElement.first == element;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (iter != source.end())
|
|
||||||
{
|
|
||||||
auto foundElement = std::move(*iter);
|
|
||||||
source.erase(iter);
|
|
||||||
for (auto name : foundElement.second)
|
|
||||||
{
|
|
||||||
MwIniImporter::dependencySortStep(name, source, result);
|
|
||||||
}
|
|
||||||
result.push_back(std::move(foundElement.first));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> MwIniImporter::dependencySort(MwIniImporter::dependencyList source)
|
|
||||||
{
|
|
||||||
std::vector<std::string> result;
|
|
||||||
while (!source.empty())
|
|
||||||
{
|
|
||||||
MwIniImporter::dependencySortStep(source.begin()->first, source, result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string>::iterator MwIniImporter::findString(std::vector<std::string>& source, const std::string& string)
|
|
||||||
{
|
|
||||||
return std::find_if(source.begin(), source.end(), [&string](const std::string& sourceString)
|
|
||||||
{
|
|
||||||
return Misc::StringUtils::ciEqual(sourceString, string);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void MwIniImporter::addPaths(std::vector<boost::filesystem::path>& output, std::vector<std::string> input) {
|
|
||||||
for (auto& path : input) {
|
|
||||||
if (path.front() == '"')
|
|
||||||
{
|
|
||||||
path.erase(path.begin());
|
|
||||||
path.erase(path.end() - 1);
|
|
||||||
}
|
|
||||||
output.emplace_back(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, const boost::filesystem::path& iniFilename) const
|
|
||||||
{
|
|
||||||
std::vector<std::pair<std::time_t, boost::filesystem::path>> contentFiles;
|
|
||||||
std::string baseGameFile("Game Files:GameFile");
|
std::string baseGameFile("Game Files:GameFile");
|
||||||
|
std::string gameFile("");
|
||||||
std::time_t defaultTime = 0;
|
std::time_t defaultTime = 0;
|
||||||
ToUTF8::Utf8Encoder encoder(mEncoding);
|
|
||||||
|
|
||||||
std::vector<boost::filesystem::path> dataPaths;
|
// assume the Game Files are all in a "Data Files" directory under the directory holding Morrowind.ini
|
||||||
if (cfg.count("data"))
|
const boost::filesystem::path gameFilesDir(iniFilename.parent_path() /= "Data Files");
|
||||||
addPaths(dataPaths, cfg["data"]);
|
|
||||||
|
|
||||||
if (cfg.count("data-local"))
|
|
||||||
addPaths(dataPaths, cfg["data-local"]);
|
|
||||||
|
|
||||||
dataPaths.push_back(iniFilename.parent_path() /= "Data Files");
|
|
||||||
|
|
||||||
multistrmap::const_iterator it = ini.begin();
|
multistrmap::const_iterator it = ini.begin();
|
||||||
for (int i=0; it != ini.end(); i++)
|
for(int i=0; it != ini.end(); i++) {
|
||||||
{
|
gameFile = baseGameFile;
|
||||||
std::string gameFile = baseGameFile;
|
gameFile.append(this->numberToString(i));
|
||||||
gameFile.append(std::to_string(i));
|
|
||||||
|
|
||||||
it = ini.find(gameFile);
|
it = ini.find(gameFile);
|
||||||
if(it == ini.end())
|
if(it == ini.end()) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
for(std::vector<std::string>::const_iterator entry = it->second.begin(); entry!=it->second.end(); ++entry)
|
for(std::vector<std::string>::const_iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) {
|
||||||
{
|
|
||||||
std::string filetype(entry->substr(entry->length()-3));
|
std::string filetype(entry->substr(entry->length()-3));
|
||||||
Misc::StringUtils::lowerCaseInPlace(filetype);
|
Misc::StringUtils::lowerCaseInPlace(filetype);
|
||||||
|
|
||||||
if(filetype.compare("esm") == 0 || filetype.compare("esp") == 0)
|
if(filetype.compare("esm") == 0 || filetype.compare("esp") == 0) {
|
||||||
{
|
boost::filesystem::path filepath(gameFilesDir);
|
||||||
bool found = false;
|
filepath /= *entry;
|
||||||
for (auto & dataPath : dataPaths)
|
contentFiles.push_back(std::make_pair(lastWriteTime(filepath, defaultTime), *entry));
|
||||||
{
|
|
||||||
boost::filesystem::path path = dataPath / *entry;
|
|
||||||
std::time_t time = lastWriteTime(path, defaultTime);
|
|
||||||
if (time != defaultTime)
|
|
||||||
{
|
|
||||||
contentFiles.push_back({time, path});
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found)
|
|
||||||
std::cout << "Warning: " << *entry << " not found, ignoring" << std::endl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -924,46 +863,11 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, co
|
||||||
cfg.erase("content");
|
cfg.erase("content");
|
||||||
cfg.insert( std::make_pair("content", std::vector<std::string>() ) );
|
cfg.insert( std::make_pair("content", std::vector<std::string>() ) );
|
||||||
|
|
||||||
// sort by timestamp
|
// this will sort files by time order first, then alphabetical (maybe), I suspect non ASCII filenames will be stuffed.
|
||||||
sort(contentFiles.begin(), contentFiles.end());
|
sort(contentFiles.begin(), contentFiles.end());
|
||||||
|
for(std::vector<std::pair<std::time_t, std::string> >::const_iterator it=contentFiles.begin(); it!=contentFiles.end(); ++it) {
|
||||||
MwIniImporter::dependencyList unsortedFiles;
|
cfg["content"].push_back(it->second);
|
||||||
|
|
||||||
ESM::ESMReader reader;
|
|
||||||
reader.setEncoder(&encoder);
|
|
||||||
for (auto& file : contentFiles)
|
|
||||||
{
|
|
||||||
reader.open(file.second.string());
|
|
||||||
std::vector<std::string> dependencies;
|
|
||||||
for (auto& gameFile : reader.getGameFiles())
|
|
||||||
{
|
|
||||||
dependencies.push_back(gameFile.name);
|
|
||||||
}
|
|
||||||
unsortedFiles.emplace_back(boost::filesystem::path(reader.getName()).filename().string(), dependencies);
|
|
||||||
reader.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sortedFiles = dependencySort(unsortedFiles);
|
|
||||||
|
|
||||||
// hard-coded dependency Morrowind - Tribunal - Bloodmoon
|
|
||||||
if(findString(sortedFiles, "Morrowind.esm") != sortedFiles.end())
|
|
||||||
{
|
|
||||||
auto tribunalIter = findString(sortedFiles, "Tribunal.esm");
|
|
||||||
auto bloodmoonIter = findString(sortedFiles, "Bloodmoon.esm");
|
|
||||||
|
|
||||||
if (bloodmoonIter != sortedFiles.end() && tribunalIter != sortedFiles.end())
|
|
||||||
{
|
|
||||||
size_t bloodmoonIndex = std::distance(sortedFiles.begin(), bloodmoonIter);
|
|
||||||
size_t tribunalIndex = std::distance(sortedFiles.begin(), tribunalIter);
|
|
||||||
if (bloodmoonIndex < tribunalIndex)
|
|
||||||
tribunalIndex++;
|
|
||||||
sortedFiles.insert(bloodmoonIter, *tribunalIter);
|
|
||||||
sortedFiles.erase(sortedFiles.begin() + tribunalIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& file : sortedFiles)
|
|
||||||
cfg["content"].push_back(file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MwIniImporter::writeToFile(std::ostream &out, const multistrmap &cfg) {
|
void MwIniImporter::writeToFile(std::ostream &out, const multistrmap &cfg) {
|
||||||
|
@ -1002,5 +906,9 @@ std::time_t MwIniImporter::lastWriteTime(const boost::filesystem::path& filename
|
||||||
std::cout << "content file: " << resolved << " timestamp = (" << writeTime <<
|
std::cout << "content file: " << resolved << " timestamp = (" << writeTime <<
|
||||||
") " << timeStrBuffer << std::endl;
|
") " << timeStrBuffer << std::endl;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << "content file: " << filename << " not found" << std::endl;
|
||||||
|
}
|
||||||
return writeTime;
|
return writeTime;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ class MwIniImporter {
|
||||||
public:
|
public:
|
||||||
typedef std::map<std::string, std::string> strmap;
|
typedef std::map<std::string, std::string> strmap;
|
||||||
typedef std::map<std::string, std::vector<std::string> > multistrmap;
|
typedef std::map<std::string, std::vector<std::string> > multistrmap;
|
||||||
typedef std::vector< std::pair< std::string, std::vector<std::string> > > dependencyList;
|
|
||||||
|
|
||||||
MwIniImporter();
|
MwIniImporter();
|
||||||
void setInputEncoding(const ToUTF8::FromType& encoding);
|
void setInputEncoding(const ToUTF8::FromType& encoding);
|
||||||
|
@ -28,14 +27,9 @@ class MwIniImporter {
|
||||||
void importArchives(multistrmap &cfg, const multistrmap &ini) const;
|
void importArchives(multistrmap &cfg, const multistrmap &ini) const;
|
||||||
static void writeToFile(std::ostream &out, const multistrmap &cfg);
|
static void writeToFile(std::ostream &out, const multistrmap &cfg);
|
||||||
|
|
||||||
static std::vector<std::string> dependencySort(MwIniImporter::dependencyList source);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void dependencySortStep(std::string& element, MwIniImporter::dependencyList& source, std::vector<std::string>& result);
|
|
||||||
static std::vector<std::string>::iterator findString(std::vector<std::string>& source, const std::string& string);
|
|
||||||
|
|
||||||
static void insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value);
|
static void insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value);
|
||||||
static void addPaths(std::vector<boost::filesystem::path>& output, std::vector<std::string> input);
|
static std::string numberToString(int n);
|
||||||
|
|
||||||
/// \return file's "last modified time", used in original MW to determine plug-in load order
|
/// \return file's "last modified time", used in original MW to determine plug-in load order
|
||||||
static std::time_t lastWriteTime(const boost::filesystem::path& filename, std::time_t defaultTime);
|
static std::time_t lastWriteTime(const boost::filesystem::path& filename, std::time_t defaultTime);
|
||||||
|
@ -46,4 +40,5 @@ class MwIniImporter {
|
||||||
ToUTF8::FromType mEncoding;
|
ToUTF8::FromType mEncoding;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
#include "importer.hpp"
|
#include "importer.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <boost/program_options.hpp>
|
#include <boost/program_options.hpp>
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <boost/filesystem/path.hpp>
|
||||||
#include <boost/filesystem/fstream.hpp>
|
#include <boost/filesystem/fstream.hpp>
|
||||||
|
|
||||||
namespace bpo = boost::program_options;
|
namespace bpo = boost::program_options;
|
||||||
|
|
|
@ -4,7 +4,7 @@ set(NIFTEST
|
||||||
source_group(components\\nif\\tests FILES ${NIFTEST})
|
source_group(components\\nif\\tests FILES ${NIFTEST})
|
||||||
|
|
||||||
# Main executable
|
# Main executable
|
||||||
openmw_add_executable(niftest
|
add_executable(niftest
|
||||||
${NIFTEST}
|
${NIFTEST}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -33,12 +33,12 @@ bool hasExtension(std::string filename, std::string extensionToFind)
|
||||||
}
|
}
|
||||||
|
|
||||||
///See if the file has the "nif" extension.
|
///See if the file has the "nif" extension.
|
||||||
bool isNIF(const std::string & filename)
|
bool isNIF(std::string filename)
|
||||||
{
|
{
|
||||||
return hasExtension(filename,"nif");
|
return hasExtension(filename,"nif");
|
||||||
}
|
}
|
||||||
///See if the file has the "bsa" extension.
|
///See if the file has the "bsa" extension.
|
||||||
bool isBSA(const std::string & filename)
|
bool isBSA(std::string filename)
|
||||||
{
|
{
|
||||||
return hasExtension(filename,"bsa");
|
return hasExtension(filename,"bsa");
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ opencs_hdrs_noqt (model/doc
|
||||||
|
|
||||||
|
|
||||||
opencs_units (model/world
|
opencs_units (model/world
|
||||||
idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel landtexturetableproxymodel
|
idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,11 +66,10 @@ opencs_hdrs_noqt (view/doc
|
||||||
|
|
||||||
|
|
||||||
opencs_units (view/world
|
opencs_units (view/world
|
||||||
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator globalcreator
|
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
|
||||||
cellcreator pathgridcreator referenceablecreator startscriptcreator referencecreator scenesubview
|
cellcreator referenceablecreator startscriptcreator referencecreator scenesubview
|
||||||
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
|
infocreator scriptedit dialoguesubview previewsubview regionmap dragrecordtable nestedtable
|
||||||
dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator
|
dialoguespinbox recordbuttonbar tableeditidaction scripterrortable extendedcommandconfigurator
|
||||||
bodypartcreator landtexturecreator landcreator
|
|
||||||
)
|
)
|
||||||
|
|
||||||
opencs_units_noqt (view/world
|
opencs_units_noqt (view/world
|
||||||
|
@ -81,19 +80,17 @@ opencs_units_noqt (view/world
|
||||||
|
|
||||||
opencs_units (view/widget
|
opencs_units (view/widget
|
||||||
scenetoolbar scenetool scenetoolmode pushbutton scenetooltoggle scenetoolrun modebutton
|
scenetoolbar scenetool scenetoolmode pushbutton scenetooltoggle scenetoolrun modebutton
|
||||||
scenetooltoggle2 scenetooltexturebrush completerpopup coloreditor colorpickerpopup droplineedit
|
scenetooltoggle2 completerpopup coloreditor colorpickerpopup droplineedit
|
||||||
)
|
)
|
||||||
|
|
||||||
opencs_units (view/render
|
opencs_units (view/render
|
||||||
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
|
scenewidget worldspacewidget pagedworldspacewidget unpagedworldspacewidget
|
||||||
previewwidget editmode instancemode instanceselectionmode instancemovemode
|
previewwidget editmode instancemode instanceselectionmode instancemovemode
|
||||||
orbitcameramode pathgridmode selectionmode pathgridselectionmode cameracontroller
|
|
||||||
cellwater terraintexturemode
|
|
||||||
)
|
)
|
||||||
|
|
||||||
opencs_units_noqt (view/render
|
opencs_units_noqt (view/render
|
||||||
lighting lightingday lightingnight lightingbright object cell terrainstorage tagbase
|
lighting lightingday lightingnight
|
||||||
cellarrow cellmarker cellborder pathgrid
|
lightingbright object cell terrainstorage tagbase cellarrow cellmarker cellborder
|
||||||
)
|
)
|
||||||
|
|
||||||
opencs_hdrs_noqt (view/render
|
opencs_hdrs_noqt (view/render
|
||||||
|
@ -110,12 +107,11 @@ opencs_units_noqt (view/tools
|
||||||
)
|
)
|
||||||
|
|
||||||
opencs_units (view/prefs
|
opencs_units (view/prefs
|
||||||
dialogue pagebase page keybindingpage contextmenulist
|
dialogue pagebase page
|
||||||
)
|
)
|
||||||
|
|
||||||
opencs_units (model/prefs
|
opencs_units (model/prefs
|
||||||
state setting intsetting doublesetting boolsetting enumsetting coloursetting shortcut
|
state setting intsetting doublesetting boolsetting enumsetting coloursetting
|
||||||
shortcuteventhandler shortcutmanager shortcutsetting modifiersetting
|
|
||||||
)
|
)
|
||||||
|
|
||||||
opencs_units_noqt (model/prefs
|
opencs_units_noqt (model/prefs
|
||||||
|
@ -163,43 +159,26 @@ endif()
|
||||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
set (OPENCS_MAC_ICON "${CMAKE_SOURCE_DIR}/files/mac/openmw-cs.icns")
|
set (OPENCS_MAC_ICON ${CMAKE_SOURCE_DIR}/files/mac/openmw-cs.icns)
|
||||||
set (OPENCS_CFG "${OpenMW_BINARY_DIR}/openmw-cs.cfg")
|
|
||||||
set (OPENCS_DEFAULT_FILTERS_FILE "${OpenMW_BINARY_DIR}/resources/defaultfilters")
|
|
||||||
set (OPENCS_OPENMW_CFG "${OpenMW_BINARY_DIR}/openmw.cfg")
|
|
||||||
else()
|
else()
|
||||||
set (OPENCS_MAC_ICON "")
|
set (OPENCS_MAC_ICON "")
|
||||||
set (OPENCS_CFG "")
|
|
||||||
set (OPENCS_DEFAULT_FILTERS_FILE "")
|
|
||||||
set (OPENCS_OPENMW_CFG "")
|
|
||||||
endif(APPLE)
|
endif(APPLE)
|
||||||
|
|
||||||
openmw_add_executable(openmw-cs
|
add_executable(openmw-cs
|
||||||
MACOSX_BUNDLE
|
MACOSX_BUNDLE
|
||||||
${OPENCS_SRC}
|
${OPENCS_SRC}
|
||||||
${OPENCS_UI_HDR}
|
${OPENCS_UI_HDR}
|
||||||
${OPENCS_MOC_SRC}
|
${OPENCS_MOC_SRC}
|
||||||
${OPENCS_RES_SRC}
|
${OPENCS_RES_SRC}
|
||||||
${OPENCS_MAC_ICON}
|
${OPENCS_MAC_ICON}
|
||||||
${OPENCS_CFG}
|
|
||||||
${OPENCS_DEFAULT_FILTERS_FILE}
|
|
||||||
${OPENCS_OPENMW_CFG}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
set(OPENCS_BUNDLE_NAME "OpenMW-CS")
|
|
||||||
set(OPENCS_BUNDLE_RESOURCES_DIR "${OpenMW_BINARY_DIR}/${OPENCS_BUNDLE_NAME}.app/Contents/Resources")
|
|
||||||
|
|
||||||
set(OPENMW_MYGUI_FILES_ROOT ${OPENCS_BUNDLE_RESOURCES_DIR})
|
|
||||||
set(OPENMW_SHADERS_ROOT ${OPENCS_BUNDLE_RESOURCES_DIR})
|
|
||||||
|
|
||||||
add_subdirectory(../../files/ ${CMAKE_CURRENT_BINARY_DIR}/files)
|
|
||||||
|
|
||||||
set_target_properties(openmw-cs PROPERTIES
|
set_target_properties(openmw-cs PROPERTIES
|
||||||
RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}"
|
RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}"
|
||||||
OUTPUT_NAME ${OPENCS_BUNDLE_NAME}
|
OUTPUT_NAME "OpenMW-CS"
|
||||||
MACOSX_BUNDLE_ICON_FILE "openmw-cs.icns"
|
MACOSX_BUNDLE_ICON_FILE "openmw-cs.icns"
|
||||||
MACOSX_BUNDLE_BUNDLE_NAME "OpenMW-CS"
|
MACOSX_BUNDLE_BUNDLE_NAME "OpenCS"
|
||||||
MACOSX_BUNDLE_GUI_IDENTIFIER "org.openmw.opencs"
|
MACOSX_BUNDLE_GUI_IDENTIFIER "org.openmw.opencs"
|
||||||
MACOSX_BUNDLE_SHORT_VERSION_STRING ${OPENMW_VERSION}
|
MACOSX_BUNDLE_SHORT_VERSION_STRING ${OPENMW_VERSION}
|
||||||
MACOSX_BUNDLE_BUNDLE_VERSION ${OPENMW_VERSION}
|
MACOSX_BUNDLE_BUNDLE_VERSION ${OPENMW_VERSION}
|
||||||
|
@ -208,16 +187,6 @@ if(APPLE)
|
||||||
|
|
||||||
set_source_files_properties(${OPENCS_MAC_ICON} PROPERTIES
|
set_source_files_properties(${OPENCS_MAC_ICON} PROPERTIES
|
||||||
MACOSX_PACKAGE_LOCATION Resources)
|
MACOSX_PACKAGE_LOCATION Resources)
|
||||||
set_source_files_properties(${OPENCS_CFG} PROPERTIES
|
|
||||||
MACOSX_PACKAGE_LOCATION Resources)
|
|
||||||
set_source_files_properties(${OPENCS_DEFAULT_FILTERS_FILE} PROPERTIES
|
|
||||||
MACOSX_PACKAGE_LOCATION Resources/resources)
|
|
||||||
set_source_files_properties(${OPENCS_OPENMW_CFG} PROPERTIES
|
|
||||||
MACOSX_PACKAGE_LOCATION Resources)
|
|
||||||
|
|
||||||
add_custom_command(TARGET openmw-cs
|
|
||||||
POST_BUILD
|
|
||||||
COMMAND cp "${OpenMW_BINARY_DIR}/resources/version" "${OPENCS_BUNDLE_RESOURCES_DIR}/resources")
|
|
||||||
endif(APPLE)
|
endif(APPLE)
|
||||||
|
|
||||||
target_link_libraries(openmw-cs
|
target_link_libraries(openmw-cs
|
||||||
|
@ -246,24 +215,14 @@ if (DESIRED_QT_VERSION MATCHES 4)
|
||||||
target_link_libraries(openmw-cs ${QT_QTMAIN_LIBRARY})
|
target_link_libraries(openmw-cs ${QT_QTMAIN_LIBRARY})
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
target_link_libraries(openmw-cs Qt5::Widgets Qt5::Core Qt5::Network Qt5::OpenGL)
|
qt5_use_modules(openmw-cs Widgets Core Network OpenGL)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
target_link_libraries(openmw-cs ${Boost_LOCALE_LIBRARY})
|
target_link_libraries(openmw-cs ${Boost_LOCALE_LIBRARY})
|
||||||
INSTALL(TARGETS openmw-cs RUNTIME DESTINATION ".")
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Debug/openmw-cs.cfg" DESTINATION "." CONFIGURATIONS Debug)
|
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/Release/openmw-cs.cfg" DESTINATION "." CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (MSVC)
|
|
||||||
# Debug version needs increased number of sections beyond 2^16
|
|
||||||
if (CMAKE_CL_64)
|
|
||||||
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /bigobj")
|
|
||||||
endif (CMAKE_CL_64)
|
|
||||||
endif (MSVC)
|
|
||||||
|
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
INSTALL(TARGETS openmw-cs BUNDLE DESTINATION "." COMPONENT BUNDLE)
|
INSTALL(TARGETS openmw-cs BUNDLE DESTINATION OpenMW COMPONENT BUNDLE)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -1,90 +0,0 @@
|
||||||
#include "editor.hpp"
|
|
||||||
|
|
||||||
#include <exception>
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QIcon>
|
|
||||||
#include <QMetaType>
|
|
||||||
|
|
||||||
#include "model/doc/messages.hpp"
|
|
||||||
|
|
||||||
#include "model/world/universalid.hpp"
|
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
|
||||||
#include <QDir>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Q_DECLARE_METATYPE (std::string)
|
|
||||||
|
|
||||||
class Application : public QApplication
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
bool notify (QObject *receiver, QEvent *event)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return QApplication::notify (receiver, event);
|
|
||||||
}
|
|
||||||
catch (const std::exception& exception)
|
|
||||||
{
|
|
||||||
std::cerr << "An exception has been caught: " << exception.what() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Application (int& argc, char *argv[]) : QApplication (argc, argv) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
#ifdef Q_OS_MAC
|
|
||||||
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// To allow background thread drawing in OSG
|
|
||||||
QApplication::setAttribute(Qt::AA_X11InitThreads, true);
|
|
||||||
|
|
||||||
Q_INIT_RESOURCE (resources);
|
|
||||||
|
|
||||||
qRegisterMetaType<std::string> ("std::string");
|
|
||||||
qRegisterMetaType<CSMWorld::UniversalId> ("CSMWorld::UniversalId");
|
|
||||||
qRegisterMetaType<CSMDoc::Message> ("CSMDoc::Message");
|
|
||||||
|
|
||||||
Application application (argc, argv);
|
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
|
||||||
QDir dir(QCoreApplication::applicationDirPath());
|
|
||||||
if (dir.dirName() == "MacOS") {
|
|
||||||
dir.cdUp();
|
|
||||||
dir.cdUp();
|
|
||||||
dir.cdUp();
|
|
||||||
}
|
|
||||||
QDir::setCurrent(dir.absolutePath());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
application.setWindowIcon (QIcon (":./openmw-cs.png"));
|
|
||||||
|
|
||||||
CS::Editor editor;
|
|
||||||
|
|
||||||
if(!editor.makeIPCServer())
|
|
||||||
{
|
|
||||||
editor.connectToIPCServer();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return editor.run();
|
|
||||||
}
|
|
||||||
catch (std::exception& e)
|
|
||||||
{
|
|
||||||
std::cerr << "ERROR: " << e.what() << std::endl;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -5,8 +5,8 @@
|
||||||
#include <QLocalSocket>
|
#include <QLocalSocket>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
#include <components/vfs/manager.hpp>
|
||||||
#include <components/crashcatcher/crashcatcher.hpp>
|
#include <components/vfs/registerarchives.hpp>
|
||||||
|
|
||||||
#include <components/fallback/validate.hpp>
|
#include <components/fallback/validate.hpp>
|
||||||
|
|
||||||
|
@ -21,23 +21,23 @@
|
||||||
|
|
||||||
using namespace Fallback;
|
using namespace Fallback;
|
||||||
|
|
||||||
CS::Editor::Editor (int argc, char **argv)
|
CS::Editor::Editor ()
|
||||||
: mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr),
|
: mSettingsState (mCfgMgr), mDocumentManager (mCfgMgr),
|
||||||
mViewManager (mDocumentManager), mPid(""),
|
mViewManager (mDocumentManager), mPid(""),
|
||||||
mLock(), mMerge (mDocumentManager),
|
mLock(), mMerge (mDocumentManager),
|
||||||
mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL)
|
mIpcServerName ("org.openmw.OpenCS"), mServer(NULL), mClientSocket(NULL)
|
||||||
{
|
{
|
||||||
// install the crash handler as soon as possible. note that the log path
|
|
||||||
// does not depend on config being read.
|
|
||||||
crashCatcherInstall(argc, argv, (mCfgMgr.getLogPath() / "openmw-cs-crash.log").string());
|
|
||||||
|
|
||||||
std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig();
|
std::pair<Files::PathContainer, std::vector<std::string> > config = readConfig();
|
||||||
|
|
||||||
setupDataFiles (config.first);
|
setupDataFiles (config.first);
|
||||||
|
|
||||||
NifOsg::Loader::setShowMarkers(true);
|
NifOsg::Loader::setShowMarkers(true);
|
||||||
|
|
||||||
mDocumentManager.setFileData(mFsStrict, config.first, config.second);
|
mVFS.reset(new VFS::Manager(mFsStrict));
|
||||||
|
|
||||||
|
VFS::registerArchives(mVFS.get(), Files::Collections(config.first, !mFsStrict), config.second, true);
|
||||||
|
|
||||||
|
mDocumentManager.setVFS(mVFS.get());
|
||||||
|
|
||||||
mNewGame.setLocalData (mLocal);
|
mNewGame.setLocalData (mLocal);
|
||||||
mFileDialog.setLocalData (mLocal);
|
mFileDialog.setLocalData (mLocal);
|
||||||
|
@ -97,16 +97,16 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
|
||||||
boost::program_options::options_description desc("Syntax: openmw-cs <options>\nAllowed options");
|
boost::program_options::options_description desc("Syntax: openmw-cs <options>\nAllowed options");
|
||||||
|
|
||||||
desc.add_options()
|
desc.add_options()
|
||||||
("data", boost::program_options::value<Files::EscapePathContainer>()->default_value(Files::EscapePathContainer(), "data")->multitoken()->composing())
|
("data", boost::program_options::value<Files::PathContainer>()->default_value(Files::PathContainer(), "data")->multitoken()->composing())
|
||||||
("data-local", boost::program_options::value<Files::EscapeHashString>()->default_value(""))
|
("data-local", boost::program_options::value<std::string>()->default_value(""))
|
||||||
("fs-strict", boost::program_options::value<bool>()->implicit_value(true)->default_value(false))
|
("fs-strict", boost::program_options::value<bool>()->implicit_value(true)->default_value(false))
|
||||||
("encoding", boost::program_options::value<Files::EscapeHashString>()->default_value("win1252"))
|
("encoding", boost::program_options::value<std::string>()->default_value("win1252"))
|
||||||
("resources", boost::program_options::value<Files::EscapeHashString>()->default_value("resources"))
|
("resources", boost::program_options::value<std::string>()->default_value("resources"))
|
||||||
("fallback-archive", boost::program_options::value<Files::EscapeStringVector>()->
|
("fallback-archive", boost::program_options::value<std::vector<std::string> >()->
|
||||||
default_value(Files::EscapeStringVector(), "fallback-archive")->multitoken())
|
default_value(std::vector<std::string>(), "fallback-archive")->multitoken())
|
||||||
("fallback", boost::program_options::value<FallbackMap>()->default_value(FallbackMap(), "")
|
("fallback", boost::program_options::value<FallbackMap>()->default_value(FallbackMap(), "")
|
||||||
->multitoken()->composing(), "fallback values")
|
->multitoken()->composing(), "fallback values")
|
||||||
("script-blacklist", boost::program_options::value<Files::EscapeStringVector>()->default_value(Files::EscapeStringVector(), "")
|
("script-blacklist", boost::program_options::value<std::vector<std::string> >()->default_value(std::vector<std::string>(), "")
|
||||||
->multitoken(), "exclude specified script from the verifier (if the use of the blacklist is enabled)")
|
->multitoken(), "exclude specified script from the verifier (if the use of the blacklist is enabled)")
|
||||||
("script-blacklist-use", boost::program_options::value<bool>()->implicit_value(true)
|
("script-blacklist-use", boost::program_options::value<bool>()->implicit_value(true)
|
||||||
->default_value(true), "enable script blacklisting");
|
->default_value(true), "enable script blacklisting");
|
||||||
|
@ -116,29 +116,25 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
|
||||||
mCfgMgr.readConfiguration(variables, desc, quiet);
|
mCfgMgr.readConfiguration(variables, desc, quiet);
|
||||||
|
|
||||||
mDocumentManager.setEncoding (
|
mDocumentManager.setEncoding (
|
||||||
ToUTF8::calculateEncoding (variables["encoding"].as<Files::EscapeHashString>().toStdString()));
|
ToUTF8::calculateEncoding (variables["encoding"].as<std::string>()));
|
||||||
|
|
||||||
mDocumentManager.setResourceDir (mResources = variables["resources"].as<Files::EscapeHashString>().toStdString());
|
mDocumentManager.setResourceDir (mResources = variables["resources"].as<std::string>());
|
||||||
|
|
||||||
mDocumentManager.setFallbackMap (variables["fallback"].as<FallbackMap>().mMap);
|
mDocumentManager.setFallbackMap (variables["fallback"].as<FallbackMap>().mMap);
|
||||||
|
|
||||||
if (variables["script-blacklist-use"].as<bool>())
|
if (variables["script-blacklist-use"].as<bool>())
|
||||||
mDocumentManager.setBlacklistedScripts (
|
mDocumentManager.setBlacklistedScripts (
|
||||||
variables["script-blacklist"].as<Files::EscapeStringVector>().toStdStringVector());
|
variables["script-blacklist"].as<std::vector<std::string> >());
|
||||||
|
|
||||||
mFsStrict = variables["fs-strict"].as<bool>();
|
mFsStrict = variables["fs-strict"].as<bool>();
|
||||||
|
|
||||||
Files::PathContainer dataDirs, dataLocal;
|
Files::PathContainer dataDirs, dataLocal;
|
||||||
if (!variables["data"].empty()) {
|
if (!variables["data"].empty()) {
|
||||||
dataDirs = Files::PathContainer(Files::EscapePath::toPathContainer(variables["data"].as<Files::EscapePathContainer>()));
|
dataDirs = Files::PathContainer(variables["data"].as<Files::PathContainer>());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string local = variables["data-local"].as<Files::EscapeHashString>().toStdString();
|
std::string local = variables["data-local"].as<std::string>();
|
||||||
if (!local.empty())
|
if (!local.empty()) {
|
||||||
{
|
|
||||||
if (local.front() == '\"')
|
|
||||||
local = local.substr(1, local.length() - 2);
|
|
||||||
|
|
||||||
dataLocal.push_back(Files::PathContainer::value_type(local));
|
dataLocal.push_back(Files::PathContainer::value_type(local));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +164,7 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
|
||||||
mFileDialog.addFiles(path);
|
mFileDialog.addFiles(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_pair (dataDirs, variables["fallback-archive"].as<Files::EscapeStringVector>().toStdStringVector());
|
return std::make_pair (dataDirs, variables["fallback-archive"].as<std::vector<std::string> >());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CS::Editor::createGame()
|
void CS::Editor::createGame()
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef CS_EDITOR_H
|
#ifndef CS_EDITOR_H
|
||||||
#define CS_EDITOR_H
|
#define CS_EDITOR_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <boost/interprocess/sync/file_lock.hpp>
|
#include <boost/interprocess/sync/file_lock.hpp>
|
||||||
#include <boost/filesystem/fstream.hpp>
|
#include <boost/filesystem/fstream.hpp>
|
||||||
|
|
||||||
|
@ -28,6 +30,11 @@
|
||||||
|
|
||||||
#include "view/tools/merge.hpp"
|
#include "view/tools/merge.hpp"
|
||||||
|
|
||||||
|
namespace VFS
|
||||||
|
{
|
||||||
|
class Manager;
|
||||||
|
}
|
||||||
|
|
||||||
namespace CSMDoc
|
namespace CSMDoc
|
||||||
{
|
{
|
||||||
class Document;
|
class Document;
|
||||||
|
@ -39,6 +46,9 @@ namespace CS
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
// FIXME: should be moved to document, so we can have different resources for each opened project
|
||||||
|
std::auto_ptr<VFS::Manager> mVFS;
|
||||||
|
|
||||||
Files::ConfigurationManager mCfgMgr;
|
Files::ConfigurationManager mCfgMgr;
|
||||||
CSMPrefs::State mSettingsState;
|
CSMPrefs::State mSettingsState;
|
||||||
CSMDoc::DocumentManager mDocumentManager;
|
CSMDoc::DocumentManager mDocumentManager;
|
||||||
|
@ -66,7 +76,7 @@ namespace CS
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Editor (int argc, char **argv);
|
Editor ();
|
||||||
~Editor ();
|
~Editor ();
|
||||||
|
|
||||||
bool makeIPCServer();
|
bool makeIPCServer();
|
||||||
|
|
|
@ -8,9 +8,8 @@
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QMetaType>
|
#include <QMetaType>
|
||||||
|
|
||||||
#include <components/misc/debugging.hpp>
|
|
||||||
|
|
||||||
#include "model/doc/messages.hpp"
|
#include "model/doc/messages.hpp"
|
||||||
|
|
||||||
#include "model/world/universalid.hpp"
|
#include "model/world/universalid.hpp"
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
|
@ -42,43 +41,50 @@ class Application : public QApplication
|
||||||
Application (int& argc, char *argv[]) : QApplication (argc, argv) {}
|
Application (int& argc, char *argv[]) : QApplication (argc, argv) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
int runApplication(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
|
setenv("OSG_GL_TEXTURE_STORAGE", "OFF", 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// To allow background thread drawing in OSG
|
try
|
||||||
QApplication::setAttribute(Qt::AA_X11InitThreads, true);
|
|
||||||
|
|
||||||
Q_INIT_RESOURCE (resources);
|
|
||||||
|
|
||||||
qRegisterMetaType<std::string> ("std::string");
|
|
||||||
qRegisterMetaType<CSMWorld::UniversalId> ("CSMWorld::UniversalId");
|
|
||||||
qRegisterMetaType<CSMDoc::Message> ("CSMDoc::Message");
|
|
||||||
|
|
||||||
Application application (argc, argv);
|
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
|
||||||
QDir dir(QCoreApplication::applicationDirPath());
|
|
||||||
QDir::setCurrent(dir.absolutePath());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
application.setWindowIcon (QIcon (":./openmw-cs.png"));
|
|
||||||
|
|
||||||
CS::Editor editor(argc, argv);
|
|
||||||
|
|
||||||
if(!editor.makeIPCServer())
|
|
||||||
{
|
{
|
||||||
editor.connectToIPCServer();
|
// To allow background thread drawing in OSG
|
||||||
|
QApplication::setAttribute(Qt::AA_X11InitThreads, true);
|
||||||
|
|
||||||
|
Q_INIT_RESOURCE (resources);
|
||||||
|
|
||||||
|
qRegisterMetaType<std::string> ("std::string");
|
||||||
|
qRegisterMetaType<CSMWorld::UniversalId> ("CSMWorld::UniversalId");
|
||||||
|
qRegisterMetaType<CSMDoc::Message> ("CSMDoc::Message");
|
||||||
|
|
||||||
|
Application application (argc, argv);
|
||||||
|
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
QDir dir(QCoreApplication::applicationDirPath());
|
||||||
|
if (dir.dirName() == "MacOS") {
|
||||||
|
dir.cdUp();
|
||||||
|
dir.cdUp();
|
||||||
|
dir.cdUp();
|
||||||
|
}
|
||||||
|
QDir::setCurrent(dir.absolutePath());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
application.setWindowIcon (QIcon (":./openmw-cs.png"));
|
||||||
|
|
||||||
|
CS::Editor editor;
|
||||||
|
|
||||||
|
if(!editor.makeIPCServer())
|
||||||
|
{
|
||||||
|
editor.connectToIPCServer();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return editor.run();
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
std::cerr << "ERROR: " << e.what() << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return editor.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
return wrapApplication(&runApplication, argc, argv, "/openmw-cs.log");
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/filesystem/fstream.hpp>
|
|
||||||
|
|
||||||
#include "../world/defaultgmsts.hpp"
|
#include "../world/defaultgmsts.hpp"
|
||||||
|
|
||||||
|
@ -269,14 +268,13 @@ void CSMDoc::Document::createBase()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
|
CSMDoc::Document::Document (const VFS::Manager* vfs, const Files::ConfigurationManager& configuration,
|
||||||
const std::vector< boost::filesystem::path >& files,bool new_,
|
const std::vector< boost::filesystem::path >& files, bool new_,
|
||||||
const boost::filesystem::path& savePath, const boost::filesystem::path& resDir,
|
const boost::filesystem::path& savePath, const boost::filesystem::path& resDir,
|
||||||
const Fallback::Map* fallback,
|
const Fallback::Map* fallback,
|
||||||
ToUTF8::FromType encoding,
|
ToUTF8::FromType encoding, const CSMWorld::ResourcesManager& resourcesManager,
|
||||||
const std::vector<std::string>& blacklistedScripts,
|
const std::vector<std::string>& blacklistedScripts)
|
||||||
bool fsStrict, const Files::PathContainer& dataPaths, const std::vector<std::string>& archives)
|
: mVFS(vfs), mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, resourcesManager, fallback),
|
||||||
: mSavePath (savePath), mContentFiles (files), mNew (new_), mData (encoding, fsStrict, dataPaths, archives, fallback, resDir),
|
|
||||||
mTools (*this, encoding),
|
mTools (*this, encoding),
|
||||||
mProjectPath ((configuration.getUserDataPath() / "projects") /
|
mProjectPath ((configuration.getUserDataPath() / "projects") /
|
||||||
(savePath.filename().string() + ".project")),
|
(savePath.filename().string() + ".project")),
|
||||||
|
@ -288,7 +286,7 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
|
||||||
if (mContentFiles.empty())
|
if (mContentFiles.empty())
|
||||||
throw std::runtime_error ("Empty content file sequence");
|
throw std::runtime_error ("Empty content file sequence");
|
||||||
|
|
||||||
if (mNew || !boost::filesystem::exists (mProjectPath))
|
if (!boost::filesystem::exists (mProjectPath))
|
||||||
{
|
{
|
||||||
boost::filesystem::path customFiltersPath (configuration.getUserDataPath());
|
boost::filesystem::path customFiltersPath (configuration.getUserDataPath());
|
||||||
customFiltersPath /= "defaultfilters";
|
customFiltersPath /= "defaultfilters";
|
||||||
|
@ -320,13 +318,12 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
|
||||||
connect (&mUndoStack, SIGNAL (cleanChanged (bool)), this, SLOT (modificationStateChanged (bool)));
|
connect (&mUndoStack, SIGNAL (cleanChanged (bool)), this, SLOT (modificationStateChanged (bool)));
|
||||||
|
|
||||||
connect (&mTools, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
|
connect (&mTools, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
|
||||||
connect (&mTools, SIGNAL (done (int, bool)), this, SIGNAL (operationDone (int, bool)));
|
connect (&mTools, SIGNAL (done (int, bool)), this, SLOT (operationDone (int, bool)));
|
||||||
connect (&mTools, SIGNAL (done (int, bool)), this, SLOT (operationDone2 (int, bool)));
|
|
||||||
connect (&mTools, SIGNAL (mergeDone (CSMDoc::Document*)),
|
connect (&mTools, SIGNAL (mergeDone (CSMDoc::Document*)),
|
||||||
this, SIGNAL (mergeDone (CSMDoc::Document*)));
|
this, SIGNAL (mergeDone (CSMDoc::Document*)));
|
||||||
|
|
||||||
connect (&mSaving, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
|
connect (&mSaving, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
|
||||||
connect (&mSaving, SIGNAL (done (int, bool)), this, SLOT (operationDone2 (int, bool)));
|
connect (&mSaving, SIGNAL (done (int, bool)), this, SLOT (operationDone (int, bool)));
|
||||||
|
|
||||||
connect (
|
connect (
|
||||||
&mSaving, SIGNAL (reportMessage (const CSMDoc::Message&, int)),
|
&mSaving, SIGNAL (reportMessage (const CSMDoc::Message&, int)),
|
||||||
|
@ -339,6 +336,11 @@ CSMDoc::Document::~Document()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const VFS::Manager *CSMDoc::Document::getVFS() const
|
||||||
|
{
|
||||||
|
return mVFS;
|
||||||
|
}
|
||||||
|
|
||||||
QUndoStack& CSMDoc::Document::getUndoStack()
|
QUndoStack& CSMDoc::Document::getUndoStack()
|
||||||
{
|
{
|
||||||
return mUndoStack;
|
return mUndoStack;
|
||||||
|
@ -413,9 +415,9 @@ void CSMDoc::Document::runSearch (const CSMWorld::UniversalId& searchId, const C
|
||||||
emit stateChanged (getState(), this);
|
emit stateChanged (getState(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMDoc::Document::runMerge (std::unique_ptr<CSMDoc::Document> target)
|
void CSMDoc::Document::runMerge (std::auto_ptr<CSMDoc::Document> target)
|
||||||
{
|
{
|
||||||
mTools.runMerge (std::move(target));
|
mTools.runMerge (target);
|
||||||
emit stateChanged (getState(), this);
|
emit stateChanged (getState(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -438,7 +440,7 @@ void CSMDoc::Document::reportMessage (const CSMDoc::Message& message, int type)
|
||||||
std::cout << message.mMessage << std::endl;
|
std::cout << message.mMessage << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMDoc::Document::operationDone2 (int type, bool failed)
|
void CSMDoc::Document::operationDone (int type, bool failed)
|
||||||
{
|
{
|
||||||
if (type==CSMDoc::State_Saving && !failed)
|
if (type==CSMDoc::State_Saving && !failed)
|
||||||
mDirty = false;
|
mDirty = false;
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue