mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-07-27 10:44:06 +00:00
Compare commits
135 commits
0.7.0-alph
...
0.7.0
Author | SHA1 | Date | |
---|---|---|---|
|
ad9ee80641 | ||
|
57f84914c3 | ||
|
48ebdb38c7 | ||
|
7316c8aafd | ||
|
94a9292cc6 | ||
|
77952440f6 | ||
|
301fff7fe5 | ||
|
01804af100 | ||
|
49fa35a516 | ||
|
792fbfe2e1 | ||
|
f29bfb6b8e | ||
|
4d40df3ea8 | ||
|
abc4090a0f | ||
|
e84f85863e | ||
|
1f2349ef6e | ||
|
023ead937f | ||
|
6322ae081d | ||
|
3805edaf78 | ||
|
02e646e838 | ||
|
437854180c | ||
|
f6db7d4fc5 | ||
|
7523a73346 | ||
|
d8919dcec6 | ||
|
a2f110ae6d | ||
|
2531378d0b | ||
|
8aad93b904 | ||
|
3effd5f1ff | ||
|
4692f29b9d | ||
|
03d377ec54 | ||
|
8ff2d1b829 | ||
|
cb82318c36 | ||
|
3b2098382b | ||
|
cb5e24e6c5 | ||
|
91f82d845c | ||
|
d35026bbf5 | ||
|
bd677726bf | ||
|
9fc4c83858 | ||
|
ece39748de | ||
|
5c4d3df551 | ||
|
2cdabddc0e | ||
|
b46767de6e | ||
|
911079e0bc | ||
|
331fa86844 | ||
|
a0ec9dfd2e | ||
|
986528c67d | ||
|
552a94a0ca | ||
|
a508a0faf8 | ||
|
dcbc9d1831 | ||
|
828c52138f | ||
|
69e7d3f2a7 | ||
|
f3b8a5b909 | ||
|
a0ad0b29bc | ||
|
222837976c | ||
|
77386525f2 | ||
|
c058dce346 | ||
|
1df1515c7e | ||
|
999ce857c7 | ||
|
db7e09f441 | ||
|
0fa116b47d | ||
|
0df32accca | ||
|
fd40e8c971 | ||
|
6e47b65205 | ||
|
f481c85e07 | ||
|
8a99f215f6 | ||
|
db9c1b9882 | ||
|
799241e8c6 | ||
|
43f195f0c7 | ||
|
2e1d4a9449 | ||
|
81e2e48561 | ||
|
d83160523f | ||
|
433a69a588 | ||
|
e70fd2cf3a | ||
|
eb52babf29 | ||
|
e96091fd6b | ||
|
906d2a837d | ||
|
71679934a1 | ||
|
5d9893ee92 | ||
|
6e1504f0a1 | ||
|
42b5a8054f | ||
|
4ce0331f1b | ||
|
9343b8af2f | ||
|
c2230a8a21 | ||
|
a0e89208a0 | ||
|
55cea491ca | ||
|
6af2400752 | ||
|
b3456a8841 | ||
|
343dd8b5ea | ||
|
76ac905efc | ||
|
f853368641 | ||
|
5e38e8abdb | ||
|
9fe54aa8c6 | ||
|
fa1700e2ab | ||
|
da6b89c185 | ||
|
50714599d9 | ||
|
afd17e5a48 | ||
|
a6c6db89fc | ||
|
d05a82a734 | ||
|
b5b26c6685 | ||
|
35755eb1f1 | ||
|
b7090b2550 | ||
|
b39e3f518b | ||
|
d8ca268067 | ||
|
2933526995 | ||
|
ef80894c5c | ||
|
6b3f598837 | ||
|
eb3ae95f0e | ||
|
d3eb106c3b | ||
|
e834a4ec74 | ||
|
bc7bcae190 | ||
|
8f90f8a3b8 | ||
|
e162af0003 | ||
|
07a5f5296c | ||
|
20a7619a4a | ||
|
f1e8569291 | ||
|
efa362031e | ||
|
b83e4056a8 | ||
|
585557ad8a | ||
|
3101de5f02 | ||
|
e5e13b21ae | ||
|
c65d6c1328 | ||
|
1baf82db32 | ||
|
d9bc1abf48 | ||
|
a8cf1e02c4 | ||
|
99f8ef88a5 | ||
|
17f13872aa | ||
|
bfd7c83c4d | ||
|
d9dd7073cf | ||
|
66d666d60c | ||
|
bb834748c5 | ||
|
a3111fbcc1 | ||
|
3d40a162bc | ||
|
df1667b6e4 | ||
|
db2b3e95b8 | ||
|
1e171ad9fd | ||
|
e402a17757 |
130 changed files with 3291 additions and 4076 deletions
26
.travis.yml
26
.travis.yml
|
@ -4,7 +4,7 @@ os:
|
|||
osx_image: xcode9.4
|
||||
language: cpp
|
||||
sudo: required
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
@ -15,18 +15,18 @@ env:
|
|||
global:
|
||||
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
||||
# via the "travis encrypt" command using the project repo's public key
|
||||
- secure: NZmvVuA0O9NJXVQ12tXQZHDJC2mbFgYNFcsicw0DgW1It2Nk5hxIkF0pfu4/Z59mhQuOPgRVjl5b0FKy2Axh0gkWc1DJEXGwNaiW5lpTMNWR1LJG5rxa8LrDUpFkycpbzfAFuTUZu5z3iYVv64XzELvBuqNGhPMu1LeBnrlech0jFNjkR9p5qtJGWb8zYcPMCC57rig8a9g1ABoVYS6UXjrKpx0946ZLRsE5ukc9pXsypGwPmOMyfzZkxxzIqFaxoE5JIEdaJTWba/6Za315ozYYIi/N35ROI1YAv5GHRe/Iw9XAa4vQpbDzjM7ZSsZdTvvQsSU598gD2xC6jFUKSrpW6GZKwM2x236fZLGnOk5Uw7DUbG+AwpcEmxBwoy9PjBl9ZF3tJykI0gROewCy8MODhdsVMKr1HGIMVBIJySm/RnNqtoDbYV8mYnSl5b8rwJiCajoiR8Zuv4CIfGneeH1a3DOQDPH/qkDsU6ilzF4ANsBlMUUpgY653KBMBmTlNuVZSH527tnD7Fg6JgHVuSQkTbRa1vSkR7Zcre604RZcAoaEdbX3bhVDasPPghU/I742L0RH3oQNlR09pPBDZ8kG7ydl4aPHwpCWnvXNM1vgxtGvnYLztwrse7IoaRXRYiMFmrso78WhMWUDKgvY4wV9aeUu0DtnMezZVIQwCKg=
|
||||
- secure: 1QK0yVyoOB+gf2I7XzvhXu9w/5lq4stBXIwJbVCTjz4Q4XVHCosURaW1MAgKzMrPnbFEwjyn5uQ8BwsvvfkuN1AZD0YXITgc7gyI+J1wQ/p/ljxRxglakU6WEgsTs2J5z9UmGac4YTXg+quK7YP3rv+zuGim2I2rhzImejyzp0Ym3kRCnNcy+SGBsiRaevRJMe00Ch8zGAbEhduQGeSoS6W0rcu02DNlQKiq5NktWsXR+TWWWVfIeIlQR/lbPsCd0pdxMaMv2QCY0rVbwrYxWJwr/Qe45dAdWp+8/C3PbXpeMSGxlLa33nJNX4Lf/djxbjm8KWk6edaXPajrjR/0iwcpwq0jg2Jt6XfEdnJt35F1gpXlc04sxStjG45uloOKCFYT0wdhIO1Lq+hDP54wypQl+JInd5qC001O7pwhVxO36EgKWqo8HD+BqGDBwsNj2engy9Qcp3wO6G0rLBPB3CrZsk9wrHVv5cSiQSLMhId3Xviu3ZI2qEDA+kgTvxrKrsnMj4bILVCyG5Ka2Mj22wIDW9e8oIab9oTdujax3DTN1GkD6QuOAGzwDsNwGASsgfoeZ+FUhgM75RlBWGMilgkmnF7EJ0oAXLEpjtABnEr2d4qHv+y08kOuTDBLB9ExzCIj024dYYYNLZrqPKx0ncHuCMG2QNj2aJAJEZtj1rQ=
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: 'ppa:openmw/openmw'
|
||||
- sourceline: 'ppa:rakhimov/boost'
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-precise-3.8
|
||||
packages: [
|
||||
# Dev
|
||||
cmake, clang-3.8, libunshield-dev, libtinyxml-dev,
|
||||
g++-6,
|
||||
cmake, clang-6.0, libunshield-dev, libtinyxml-dev,
|
||||
g++-8,
|
||||
# Tests
|
||||
libgtest-dev, google-mock,
|
||||
# Boost
|
||||
|
@ -45,7 +45,7 @@ addons:
|
|||
project:
|
||||
name: "TES3MP/openmw-tes3mp"
|
||||
description: "<Your project description here>"
|
||||
notification_email: stas5978@gmail.com
|
||||
notification_email: koncord@tes3mp.com
|
||||
build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE -DBUILD_OPENCS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_LAUNCHER=FALSE"
|
||||
build_command: "make -j3"
|
||||
branch_pattern: coverity_scan
|
||||
|
@ -53,21 +53,21 @@ matrix:
|
|||
include:
|
||||
- os: linux
|
||||
env:
|
||||
- ANALYZE="scan-build-3.8 --use-cc clang-3.8 --use-c++ clang++-3.8 "
|
||||
- MATRIX_CC="CC=clang-3.8 && CXX=clang++-3.8"
|
||||
- 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"
|
||||
compiler: clang
|
||||
- os: linux
|
||||
env:
|
||||
- MATRIX_CC="CC=gcc-6 && CXX=g++-6"
|
||||
- MATRIX_CC="CC=gcc-8 && CXX=g++-8"
|
||||
- os: linux
|
||||
env:
|
||||
- MATRIX_CC="CC=clang-3.8 && CXX=clang++-3.8"
|
||||
- MATRIX_CC="CC=clang-6.0 && CXX=clang++-6.0"
|
||||
allow_failures:
|
||||
- env:
|
||||
- MATRIX_CC="CC=clang-3.8 && CXX=clang++-3.8"
|
||||
- MATRIX_CC="CC=clang-6.0 && CXX=clang++-6.0"
|
||||
- env:
|
||||
- ANALYZE="scan-build-3.8 --use-cc clang-3.8 --use-c++ clang++-3.8 "
|
||||
- MATRIX_CC="CC=clang-3.8 && CXX=clang++-3.8"
|
||||
- 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:
|
||||
- ./CI/before_install.${TRAVIS_OS_NAME}.sh
|
||||
|
|
|
@ -15,19 +15,8 @@ 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
|
||||
|
||||
cd ~/
|
||||
git clone https://github.com/TES3MP/RakNet
|
||||
cd RakNet
|
||||
cmake . -DRAKNET_ENABLE_DLL=OFF -DRAKNET_ENABLE_SAMPLES=OFF -DCMAKE_BUILD_TYPE=Release
|
||||
git clone https://github.com/TES3MP/CrabNet
|
||||
cd CrabNet
|
||||
cmake . -DCRABNET_ENABLE_DLL=OFF -DCRABNET_ENABLE_SAMPLES=OFF -DCMAKE_BUILD_TYPE=Release
|
||||
make -j3
|
||||
|
||||
cd ~/
|
||||
git clone https://github.com/Koncord/CallFF
|
||||
cd CallFF
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ../
|
||||
make -j3
|
||||
|
||||
cd ~/
|
||||
wget https://github.com/zdevito/terra/releases/download/release-2016-03-25/terra-Linux-x86_64-332a506.zip
|
||||
unzip terra-Linux-x86_64-332a506.zip
|
||||
|
|
|
@ -9,8 +9,7 @@ if [ ! -z "${MATRIX_CC}" ]; then
|
|||
eval "${MATRIX_CC}"
|
||||
fi
|
||||
|
||||
export RAKNET_ROOT=~/RakNet
|
||||
export Terra_ROOT=~/terra-Linux-x86_64-332a506
|
||||
export RAKNET_ROOT=~/CrabNet
|
||||
|
||||
export CODE_COVERAGE=0
|
||||
if [ ! -z "${ANALYZE}" ]; then
|
||||
|
@ -36,7 +35,5 @@ ${ANALYZE}cmake .. \
|
|||
-DBINDIR=/usr/games \
|
||||
-DCMAKE_BUILD_TYPE="None" \
|
||||
-DUSE_SYSTEM_TINYXML=TRUE \
|
||||
-DRakNet_LIBRARY_RELEASE=~/RakNet/lib/libRakNetLibStatic.a \
|
||||
-DRakNet_LIBRARY_DEBUG=~/RakNet/lib/libRakNetLibStatic.a \
|
||||
-DCallFF_INCLUDES=~/CallFF/include \
|
||||
-DCallFF_LIBRARY=~/CallFF/build/src/libcallff.a
|
||||
-DRakNet_LIBRARY_RELEASE=~/CrabNet/lib/libRakNetLibStatic.a \
|
||||
-DRakNet_LIBRARY_DEBUG=~/CrabNet/lib/libRakNetLibStatic.a
|
||||
|
|
|
@ -736,7 +736,10 @@ if (WIN32)
|
|||
endforeach(d)
|
||||
|
||||
set_target_properties(components PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||
|
||||
if (BUILD_OPENMW)
|
||||
set_target_properties(osg-ffmpeg-videoplayer PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||
endif()
|
||||
|
||||
if (BUILD_BSATOOL)
|
||||
set_target_properties(bsatool PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||
|
|
51
README.md
51
README.md
|
@ -2,11 +2,11 @@ TES3MP
|
|||
======
|
||||
|
||||
Copyright (c) 2008-2015, OpenMW Team
|
||||
Copyright (c) 2016-2018, TES3MP Team
|
||||
Copyright (c) 2016-2019, Stanislav Zhukov & David Cernat
|
||||
|
||||
[](https://travis-ci.org/TES3MP/openmw-tes3mp)
|
||||
[](https://travis-ci.org/TES3MP/openmw-tes3mp)
|
||||
|
||||
TES3MP is a project aiming to add 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.
|
||||
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.
|
||||
|
||||
* TES3MP version: 0.7.0-alpha
|
||||
* OpenMW version: 0.44.0
|
||||
|
@ -15,34 +15,35 @@ TES3MP is a project aiming to add multiplayer functionality to [OpenMW](https://
|
|||
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)
|
||||
|
||||
Project Status
|
||||
Project status
|
||||
--------------
|
||||
|
||||
[Version changelog](https://github.com/TES3MP/openmw-tes3mp/blob/master/tes3mp-changelog.md)
|
||||
|
||||
TES3MP is now playable in most respects. Player and NPC movement, animations, combat and spell casting are properly synchronized with small exceptions, as is picking up and dropping items in the world, using doors and levers, and adding and removing items from containers. Journal entries, faction stats and dialogue topics are also synchronized, allowing the majority of quests to work fine.
|
||||
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).
|
||||
|
||||
[Serverside Lua scripts](https://github.com/TES3MP/CoreScripts) are used to save and load the state of most of the aforementioned.
|
||||
|
||||
Contributing
|
||||
--------------
|
||||
|
||||
Development has been relatively fast, but any contribution regarding [code](https://github.com/TES3MP/openmw-tes3mp/blob/master/CONTRIBUTING.md), documentation, bug hunting or video showcases is greatly appreciated.
|
||||
|
||||
Test sessions are often advertised on [our Discord server](https://discord.gg/ECJk293) or in [our Steam group](https://steamcommunity.com/groups/mwmulti).
|
||||
|
||||
Feel free to contact the [team members](https://github.com/TES3MP/openmw-tes3mp/blob/master/tes3mp-credits.md) for any questions you might have.
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
* [Quickstart guide](https://github.com/TES3MP/openmw-tes3mp/wiki/Quickstart-guide)
|
||||
* [Steam group](https://steamcommunity.com/groups/mwmulti) and its [detailed FAQ](https://steamcommunity.com/groups/mwmulti/discussions/1/353916184342480541/)
|
||||
* [TES3MP section on OpenMW forums](https://forum.openmw.org/viewforum.php?f=44)
|
||||
* [Subreddit](https://www.reddit.com/r/tes3mp)
|
||||
* [Known issues and bug reports](https://github.com/TES3MP/openmw-tes3mp/issues)
|
||||
Remaining gameplay problems mostly relate to AI and the synchronization of clientside script variables.
|
||||
|
||||
Donations
|
||||
---------------
|
||||
|
||||
You can benefit the project by contributing to the Patreon pages of our two developers, [Koncord](https://www.patreon.com/Koncord) and [David Cernat](https://www.patreon.com/davidcernat), as well as by supporting [OpenMW](https://openmw.org).
|
||||
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).
|
||||
|
||||
Contributing
|
||||
---------------
|
||||
|
||||
Helping us with documentation, bug hunting and video showcases is always greatly appreciated.
|
||||
|
||||
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.
|
||||
|
||||
Feel free to contact the [team members](https://github.com/TES3MP/openmw-tes3mp/blob/master/tes3mp-credits.md) for any questions you might have.
|
||||
|
||||
Getting started
|
||||
---------------
|
||||
|
||||
* [Quickstart guide](https://github.com/TES3MP/openmw-tes3mp/wiki/Quickstart-guide)
|
||||
* [Steam group](https://steamcommunity.com/groups/mwmulti) and its [detailed FAQ](https://steamcommunity.com/groups/mwmulti/discussions/1/353916184342480541/)
|
||||
* [TES3MP section on OpenMW forums](https://forum.openmw.org/viewforum.php?f=45)
|
||||
* [Discord server](https://discord.gg/ECJk293)
|
||||
* [Subreddit](https://www.reddit.com/r/tes3mp)
|
||||
* [Known issues and bug reports](https://github.com/TES3MP/openmw-tes3mp/issues)
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
project(tes3mp-server)
|
||||
|
||||
if(UNIX) #temporarily disabled for non-unix
|
||||
if(NOT (${CMAKE_CXX_COMPILER} MATCHES "aarch64" OR ${CMAKE_CXX_COMPILER} MATCHES "arm")) #temporarily disabled for arm
|
||||
find_package(CallFF REQUIRED)
|
||||
include_directories(${CallFF_INCLUDES})
|
||||
endif(NOT (${CMAKE_CXX_COMPILER} MATCHES "aarch64" OR ${CMAKE_CXX_COMPILER} MATCHES "arm"))
|
||||
endif(UNIX)
|
||||
|
||||
option(ENABLE_BREAKPAD "Enable Google Breakpad for Crash reporting" OFF)
|
||||
|
||||
if(ENABLE_BREAKPAD)
|
||||
|
@ -36,7 +29,7 @@ if(BUILD_WITH_LUA)
|
|||
Script/LangLua/LangLua.hpp)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DENABLE_LUA")
|
||||
include_directories(${LuaJit_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/extern/LuaBridge)
|
||||
include_directories(SYSTEM ${LuaJit_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/extern/LuaBridge)
|
||||
endif(BUILD_WITH_LUA)
|
||||
|
||||
set(NativeScript_Sources
|
||||
|
@ -80,7 +73,6 @@ set(SERVER_HEADER
|
|||
Script/ScriptFunctions.hpp Script/API/TimerAPI.hpp Script/API/PublicFnAPI.hpp
|
||||
${LuaScript_Headers}
|
||||
${NativeScript_Headers}
|
||||
${CallFF_INCLUDES}
|
||||
)
|
||||
source_group(tes3mp-server FILES ${SERVER} ${SERVER_HEADER})
|
||||
|
||||
|
@ -152,6 +144,7 @@ set(PROCESSORS
|
|||
source_group(tes3mp-server\\processors FILES ${PROCESSORS})
|
||||
|
||||
include_directories("./")
|
||||
include_directories(${CMAKE_SOURCE_DIR}/extern)
|
||||
|
||||
# Main executable
|
||||
|
||||
|
@ -160,7 +153,22 @@ add_executable(tes3mp-server
|
|||
${PROCESSORS_ACTOR} ${PROCESSORS_PLAYER} ${PROCESSORS_OBJECT} ${PROCESSORS_WORLDSTATE} ${PROCESSORS}
|
||||
${APPLE_BUNDLE_RESOURCES}
|
||||
)
|
||||
add_definitions(-std=gnu++14 -Wno-ignored-qualifiers)
|
||||
|
||||
target_compile_options(tes3mp-server PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/permissive->)
|
||||
|
||||
if (OPENMW_MP_BUILD)
|
||||
target_compile_options(tes3mp-server PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/MP>)
|
||||
endif()
|
||||
|
||||
set_target_properties(tes3mp-server PROPERTIES
|
||||
CXX_STANDARD 14
|
||||
CXX_STANDARD_REQUIRED YES
|
||||
CXX_EXTENSIONS YES
|
||||
)
|
||||
|
||||
if (UNIX)
|
||||
target_compile_options(tes3mp-server PRIVATE -Wno-ignored-qualifiers)
|
||||
endif()
|
||||
|
||||
target_link_libraries(tes3mp-server
|
||||
#${Boost_SYSTEM_LIBRARY}
|
||||
|
@ -171,7 +179,6 @@ target_link_libraries(tes3mp-server
|
|||
components
|
||||
${LuaJit_LIBRARIES}
|
||||
${Breakpad_Library}
|
||||
${CallFF_LIBRARY}
|
||||
)
|
||||
|
||||
if (UNIX)
|
||||
|
|
|
@ -53,11 +53,13 @@ void Cell::addPlayer(Player *player)
|
|||
players.push_back(player);
|
||||
}
|
||||
|
||||
void Cell::removePlayer(Player *player)
|
||||
void Cell::removePlayer(Player *player, bool cleanPlayer)
|
||||
{
|
||||
for (Iterator it = begin(); it != end(); it++)
|
||||
{
|
||||
if (*it == player)
|
||||
{
|
||||
if (cleanPlayer)
|
||||
{
|
||||
auto it2 = find(player->cells.begin(), player->cells.end(), this);
|
||||
if (it2 != player->cells.end())
|
||||
|
@ -66,6 +68,7 @@ void Cell::removePlayer(Player *player)
|
|||
|
||||
player->cells.erase(it2);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_APPEND(Log::LOG_INFO, "- Removing %s from Cell %s", player->npc.mName.c_str(), getDescription().c_str());
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ public:
|
|||
Iterator end() const;
|
||||
|
||||
void addPlayer(Player *player);
|
||||
void removePlayer(Player *player);
|
||||
void removePlayer(Player *player, bool cleanPlayer = true);
|
||||
|
||||
void readActorList(unsigned char packetID, const mwmp::BaseActorList *newActorList);
|
||||
bool containsActor(int refNum, int mpNum);
|
||||
|
|
|
@ -134,28 +134,35 @@ void CellController::removeCell(Cell *cell)
|
|||
}
|
||||
}
|
||||
|
||||
void CellController::removePlayer(Cell *cell, Player *player)
|
||||
void CellController::deletePlayer(Player *player)
|
||||
{
|
||||
cell->removePlayer(player);
|
||||
LOG_APPEND(Log::LOG_INFO, "- Iterating through Cells from Player %s", player->npc.mName.c_str());
|
||||
|
||||
if (cell->players.empty())
|
||||
std::vector<Cell*> toDelete;
|
||||
|
||||
auto it = player->getCells()->begin();
|
||||
const auto endIter = player->getCells()->end();
|
||||
|
||||
for (; it != endIter; ++it)
|
||||
{
|
||||
Cell *c = *it;
|
||||
c->removePlayer(player, false);
|
||||
if (c->players.empty())
|
||||
toDelete.push_back(c);
|
||||
}
|
||||
|
||||
for (auto &&cell : toDelete)
|
||||
{
|
||||
LOG_APPEND(Log::LOG_INFO, "- Cell %s has no players left", cell->getDescription().c_str());
|
||||
removeCell(cell);
|
||||
}
|
||||
}
|
||||
|
||||
void CellController::deletePlayer(Player *player)
|
||||
{
|
||||
LOG_APPEND(Log::LOG_INFO, "- Iterating through Cells from Player %s", player->npc.mName.c_str());
|
||||
|
||||
for (auto it = player->getCells()->begin(); player->getCells()->size() != 0; ++it)
|
||||
removePlayer(*it, player);
|
||||
}
|
||||
|
||||
void CellController::update(Player *player)
|
||||
{
|
||||
for (auto cell : player->cellStateChanges.cellStates)
|
||||
std::vector<Cell*> toDelete;
|
||||
|
||||
for (auto &&cell : player->cellStateChanges.cellStates)
|
||||
{
|
||||
if (cell.type == mwmp::CellState::LOAD)
|
||||
{
|
||||
|
@ -171,7 +178,16 @@ void CellController::update(Player *player)
|
|||
c = getCellByXY(cell.cell.getGridX(), cell.cell.getGridY());
|
||||
|
||||
if (c != nullptr)
|
||||
removePlayer(c, player);
|
||||
{
|
||||
c->removePlayer(player);
|
||||
if (c->players.empty())
|
||||
toDelete.push_back(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto &&cell : toDelete)
|
||||
{
|
||||
LOG_APPEND(Log::LOG_INFO, "- Cell %s has no players left", cell->getDescription().c_str());
|
||||
removeCell(cell);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ public:
|
|||
Cell * addCell(ESM::Cell cell);
|
||||
void removeCell(Cell *);
|
||||
|
||||
void removePlayer(Cell *cell, Player *player);
|
||||
void deletePlayer(Player *player);
|
||||
|
||||
Cell *getCell(ESM::Cell *esmCell);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <Script/API/TimerAPI.hpp>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <csignal>
|
||||
|
||||
#include "Networking.hpp"
|
||||
#include "MasterClient.hpp"
|
||||
|
@ -30,7 +31,9 @@ using namespace std;
|
|||
Networking *Networking::sThis = 0;
|
||||
|
||||
static int currentMpNum = 0;
|
||||
static bool pluginEnforcementState = true;
|
||||
static bool dataFileEnforcementState = true;
|
||||
static bool scriptErrorIgnoringState = false;
|
||||
bool killLoop = false;
|
||||
|
||||
Networking::Networking(RakNet::RakPeerInterface *peer) : mclient(nullptr)
|
||||
{
|
||||
|
@ -74,9 +77,9 @@ Networking::~Networking()
|
|||
delete worldstatePacketController;
|
||||
}
|
||||
|
||||
void Networking::setServerPassword(std::string passw) noexcept
|
||||
void Networking::setServerPassword(std::string password) noexcept
|
||||
{
|
||||
serverPassword = passw.empty() ? TES3MP_DEFAULT_PASSW : passw;
|
||||
serverPassword = password.empty() ? TES3MP_DEFAULT_PASSW : password;
|
||||
}
|
||||
|
||||
bool Networking::isPassworded() const
|
||||
|
@ -97,26 +100,33 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
|
|||
|
||||
if (!myPacket->isPacketValid())
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Invalid handshake packet from %d", player->getId());
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Invalid handshake packet from client at %s", packet->systemAddress.ToString());
|
||||
kickPlayer(player->guid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (player->isHandshaked())
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Wrong handshake with player %d, name: %s", player->getId(),
|
||||
player->npc.mName.c_str());
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Wrong handshake with client at %s", packet->systemAddress.ToString());
|
||||
kickPlayer(player->guid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (player->passw != serverPassword)
|
||||
if (player->serverPassword != serverPassword)
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Wrong server password for player %d, name: %s (pass: %s)",
|
||||
player->getId(), player->npc.mName.c_str(), player->passw.c_str());
|
||||
if (isPassworded())
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Wrong server password %s used by client at %s",
|
||||
player->serverPassword.c_str(), packet->systemAddress.ToString());
|
||||
kickPlayer(player->guid);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Client at %s tried to join using password, despite the server not being passworded",
|
||||
packet->systemAddress.ToString());
|
||||
}
|
||||
}
|
||||
player->setHandshake();
|
||||
return;
|
||||
}
|
||||
|
@ -124,9 +134,8 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
|
|||
if (!player->isHandshaked())
|
||||
{
|
||||
player->incrementHandshakeAttempts();
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Have not completed handshake with player %d", player->getId());
|
||||
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Attempts so far: %i", player->getHandshakeAttempts());
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Have not completed handshake with client at %s", packet->systemAddress.ToString());
|
||||
LOG_APPEND(Log::LOG_WARN, "- Attempts so far: %i", player->getHandshakeAttempts());
|
||||
|
||||
if (player->getHandshakeAttempts() > 20)
|
||||
kickPlayer(player->guid, false);
|
||||
|
@ -140,11 +149,10 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
|
|||
{
|
||||
player->setLoadState(Player::LOADED);
|
||||
|
||||
static constexpr unsigned int ident = Script::CallbackIdentity("OnPlayerConnect");
|
||||
Script::CallBackReturn<ident> result = true;
|
||||
Script::Call<ident>(result, Players::getPlayer(packet->guid)->getId());
|
||||
unsigned short pid = Players::getPlayer(packet->guid)->getId();
|
||||
Script::Call<Script::CallbackIdentity("OnPlayerConnect")>(pid);
|
||||
|
||||
if (!result)
|
||||
if (player->getLoadState() == Player::KICKED) // kicked inside in OnPlayerConnect
|
||||
{
|
||||
playerPacketController->GetPacket(ID_USER_DISCONNECTED)->setPlayer(Players::getPlayer(packet->guid));
|
||||
playerPacketController->GetPacket(ID_USER_DISCONNECTED)->Send(false);
|
||||
|
@ -222,34 +230,34 @@ bool Networking::preInit(RakNet::Packet *packet, RakNet::BitStream &bsIn)
|
|||
}
|
||||
|
||||
DEBUG_PRINTF("ID_GAME_PREINIT");
|
||||
PacketPreInit::PluginContainer plugins;
|
||||
PacketPreInit::PluginContainer dataFiles;
|
||||
|
||||
PacketPreInit packetPreInit(peer);
|
||||
packetPreInit.SetReadStream(&bsIn);
|
||||
packetPreInit.setChecksums(&plugins);
|
||||
packetPreInit.setChecksums(&dataFiles);
|
||||
packetPreInit.Read();
|
||||
|
||||
if (!packetPreInit.isPacketValid() || plugins.empty())
|
||||
if (!packetPreInit.isPacketValid() || dataFiles.empty())
|
||||
{
|
||||
LOG_APPEND(Log::LOG_ERROR, "Invalid packetPreInit");
|
||||
peer->CloseConnection(packet->systemAddress, false); // close connection without notification
|
||||
return false;
|
||||
}
|
||||
|
||||
auto plugin = plugins.begin();
|
||||
if (samples.size() == plugins.size())
|
||||
auto dataFile = dataFiles.begin();
|
||||
if (samples.size() == dataFiles.size())
|
||||
{
|
||||
for (int i = 0; plugin != plugins.end(); plugin++, i++)
|
||||
for (int i = 0; dataFile != dataFiles.end(); dataFile++, i++)
|
||||
{
|
||||
LOG_APPEND(Log::LOG_VERBOSE, "- %X\t%s", plugin->second[0], plugin->first.c_str());
|
||||
LOG_APPEND(Log::LOG_VERBOSE, "- %X\t%s", dataFile->second[0], dataFile->first.c_str());
|
||||
// Check if the filenames match, ignoring case
|
||||
if (Misc::StringUtils::ciEqual(samples[i].first, plugin->first))
|
||||
if (Misc::StringUtils::ciEqual(samples[i].first, dataFile->first))
|
||||
{
|
||||
auto &hashList = samples[i].second;
|
||||
// Proceed if no checksums have been listed for this plugin on the server
|
||||
// Proceed if no checksums have been listed for this dataFile on the server
|
||||
if (hashList.empty())
|
||||
continue;
|
||||
auto it = find(hashList.begin(), hashList.end(), plugin->second[0]);
|
||||
auto it = find(hashList.begin(), hashList.end(), dataFile->second[0]);
|
||||
// Break the loop if the client's checksum isn't among those accepted by
|
||||
// the server
|
||||
if (it == hashList.end())
|
||||
|
@ -262,10 +270,10 @@ bool Networking::preInit(RakNet::Packet *packet, RakNet::BitStream &bsIn)
|
|||
RakNet::BitStream bs;
|
||||
packetPreInit.SetSendStream(&bs);
|
||||
|
||||
// If the loop above was broken, then the client's plugins do not match the server's
|
||||
if (pluginEnforcementState && plugin != plugins.end())
|
||||
// If the loop above was broken, then the client's data files do not match the server's
|
||||
if (dataFileEnforcementState && dataFile != dataFiles.end())
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "%s was not allowed to connect due to incompatible plugins", packet->systemAddress.ToString());
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "%s was not allowed to connect due to incompatible data files", packet->systemAddress.ToString());
|
||||
packetPreInit.setChecksums(&samples);
|
||||
packetPreInit.Send(packet->systemAddress);
|
||||
peer->CloseConnection(packet->systemAddress, true);
|
||||
|
@ -421,14 +429,24 @@ int Networking::incrementMpNum()
|
|||
return currentMpNum;
|
||||
}
|
||||
|
||||
bool Networking::getPluginEnforcementState()
|
||||
bool Networking::getDataFileEnforcementState()
|
||||
{
|
||||
return pluginEnforcementState;
|
||||
return dataFileEnforcementState;
|
||||
}
|
||||
|
||||
void Networking::setPluginEnforcementState(bool state)
|
||||
void Networking::setDataFileEnforcementState(bool state)
|
||||
{
|
||||
pluginEnforcementState = state;
|
||||
dataFileEnforcementState = state;
|
||||
}
|
||||
|
||||
bool Networking::getScriptErrorIgnoringState()
|
||||
{
|
||||
return scriptErrorIgnoringState;
|
||||
}
|
||||
|
||||
void Networking::setScriptErrorIgnoringState(bool state)
|
||||
{
|
||||
scriptErrorIgnoringState = state;
|
||||
}
|
||||
|
||||
const Networking &Networking::get()
|
||||
|
@ -447,44 +465,36 @@ RakNet::SystemAddress Networking::getSystemAddress(RakNet::RakNetGUID guid)
|
|||
return peer->GetSystemAddressFromGuid(guid);
|
||||
}
|
||||
|
||||
PacketPreInit::PluginContainer Networking::getPluginListSample()
|
||||
{
|
||||
PacketPreInit::PluginContainer pls;
|
||||
unsigned id = 0;
|
||||
while (true)
|
||||
{
|
||||
unsigned field = 0;
|
||||
auto name = "";
|
||||
Script::Call<Script::CallbackIdentity("OnRequestPluginList")>(name, id, field++);
|
||||
if (strlen(name) == 0)
|
||||
break;
|
||||
PacketPreInit::HashList hashList;
|
||||
while (true)
|
||||
{
|
||||
auto hash = "";
|
||||
Script::Call<Script::CallbackIdentity("OnRequestPluginList")>(hash, id, field++);
|
||||
if (strlen(hash) == 0)
|
||||
break;
|
||||
hashList.push_back((unsigned)stoul(hash));
|
||||
}
|
||||
pls.push_back({name, hashList});
|
||||
id++;
|
||||
}
|
||||
return pls;
|
||||
}
|
||||
|
||||
void Networking::stopServer(int code)
|
||||
{
|
||||
running = false;
|
||||
exitCode = code;
|
||||
}
|
||||
|
||||
void signalHandler(int signum)
|
||||
{
|
||||
cout << "Interrupt signal (" << signum << ") received.\n";
|
||||
//15 is SIGTERM(Normal OS stop call), 2 is SIGINT(Ctrl+C)
|
||||
if(signum == 15 or signum == 2)
|
||||
{
|
||||
killLoop = true;
|
||||
}
|
||||
}
|
||||
|
||||
int Networking::mainLoop()
|
||||
{
|
||||
RakNet::Packet *packet;
|
||||
|
||||
while (running)
|
||||
struct sigaction sigIntHandler;
|
||||
|
||||
sigIntHandler.sa_handler = signalHandler;
|
||||
sigemptyset(&sigIntHandler.sa_mask);
|
||||
sigIntHandler.sa_flags = 0;
|
||||
|
||||
while (running and !killLoop)
|
||||
{
|
||||
sigaction(SIGTERM, &sigIntHandler, NULL);
|
||||
sigaction(SIGINT, &sigIntHandler, NULL);
|
||||
if (kbhit() && getch() == '\n')
|
||||
break;
|
||||
for (packet=peer->Receive(); packet; peer->DeallocatePacket(packet), packet=peer->Receive())
|
||||
|
@ -595,16 +605,11 @@ void Networking::InitQuery(std::string queryAddr, unsigned short queryPort)
|
|||
|
||||
void Networking::postInit()
|
||||
{
|
||||
Script::Call<Script::CallbackIdentity("OnRequestDataFileList")>();
|
||||
Script::Call<Script::CallbackIdentity("OnServerPostInit")>();
|
||||
samples = getPluginListSample();
|
||||
if (mclient)
|
||||
{
|
||||
for (auto plugin : samples)
|
||||
{
|
||||
if (!plugin.second.empty())
|
||||
mclient->PushPlugin({plugin.first, plugin.second[0]});
|
||||
else
|
||||
mclient->PushPlugin({plugin.first, 0});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PacketPreInit::PluginContainer &Networking::getSamples()
|
||||
{
|
||||
return samples;
|
||||
}
|
||||
|
|
|
@ -53,8 +53,11 @@ namespace mwmp
|
|||
void setCurrentMpNum(int value);
|
||||
int incrementMpNum();
|
||||
|
||||
bool getPluginEnforcementState();
|
||||
void setPluginEnforcementState(bool state);
|
||||
bool getDataFileEnforcementState();
|
||||
void setDataFileEnforcementState(bool state);
|
||||
|
||||
bool getScriptErrorIgnoringState();
|
||||
void setScriptErrorIgnoringState(bool state);
|
||||
|
||||
MasterClient *getMasterClient();
|
||||
void InitQuery(std::string queryAddr, unsigned short queryPort);
|
||||
|
@ -65,9 +68,10 @@ namespace mwmp
|
|||
static Networking *getPtr();
|
||||
|
||||
void postInit();
|
||||
|
||||
PacketPreInit::PluginContainer &getSamples();
|
||||
private:
|
||||
bool preInit(RakNet::Packet *packet, RakNet::BitStream &bsIn);
|
||||
PacketPreInit::PluginContainer getPluginListSample();
|
||||
std::string serverPassword;
|
||||
static Networking *sThis;
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include "Cell.hpp"
|
||||
#include "CellController.hpp"
|
||||
|
||||
struct Player;
|
||||
typedef std::map<RakNet::RakNetGUID, Player*> TPlayers;
|
||||
typedef std::map<unsigned short, Player*> TSlots;
|
||||
|
||||
|
@ -51,7 +50,8 @@ public:
|
|||
{
|
||||
NOTLOADED=0,
|
||||
LOADED,
|
||||
POSTLOADED
|
||||
POSTLOADED,
|
||||
KICKED
|
||||
};
|
||||
Player(RakNet::RakNetGUID guid);
|
||||
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
//
|
||||
// Created by koncord on 15.03.16.
|
||||
//
|
||||
|
||||
#include "TimerAPI.hpp"
|
||||
|
||||
#include <chrono>
|
||||
|
@ -14,7 +10,7 @@ Timer::Timer(ScriptFunc callback, long msec, const std::string& def, std::vector
|
|||
{
|
||||
targetMsec = msec;
|
||||
this->args = args;
|
||||
end = true;
|
||||
isEnded = true;
|
||||
}
|
||||
|
||||
#if defined(ENABLE_LUA)
|
||||
|
@ -22,13 +18,13 @@ Timer::Timer(lua_State *lua, ScriptFuncLua callback, long msec, const std::strin
|
|||
{
|
||||
targetMsec = msec;
|
||||
this->args = args;
|
||||
end = true;
|
||||
isEnded = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Timer::Tick()
|
||||
{
|
||||
if (end)
|
||||
if (isEnded)
|
||||
return;
|
||||
|
||||
const auto duration = chrono::system_clock::now().time_since_epoch();
|
||||
|
@ -36,19 +32,19 @@ void Timer::Tick()
|
|||
|
||||
if (time - startTime >= targetMsec)
|
||||
{
|
||||
end = true;
|
||||
isEnded = true;
|
||||
Call(args);
|
||||
}
|
||||
}
|
||||
|
||||
bool Timer::IsEnd()
|
||||
bool Timer::IsEnded()
|
||||
{
|
||||
return end;
|
||||
return isEnded;
|
||||
}
|
||||
|
||||
void Timer::Stop()
|
||||
{
|
||||
end = true;
|
||||
isEnded = true;
|
||||
}
|
||||
|
||||
void Timer::Restart(int msec)
|
||||
|
@ -59,7 +55,7 @@ void Timer::Restart(int msec)
|
|||
|
||||
void Timer::Start()
|
||||
{
|
||||
end = false;
|
||||
isEnded = false;
|
||||
|
||||
const auto duration = chrono::system_clock::now().time_since_epoch();
|
||||
const auto msec = chrono::duration_cast<chrono::milliseconds>(duration).count();
|
||||
|
@ -172,12 +168,12 @@ void TimerAPI::StopTimer(int timerid)
|
|||
}
|
||||
}
|
||||
|
||||
bool TimerAPI::IsEndTimer(int timerid)
|
||||
bool TimerAPI::IsTimerElapsed(int timerid)
|
||||
{
|
||||
bool ret = false;
|
||||
try
|
||||
{
|
||||
ret = timers.at(timerid)->IsEnd();
|
||||
ret = timers.at(timerid)->IsEnded();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
//
|
||||
// Created by koncord on 15.03.16.
|
||||
//
|
||||
|
||||
#ifndef OPENMW_TIMERAPI_HPP
|
||||
#define OPENMW_TIMERAPI_HPP
|
||||
|
||||
|
@ -27,7 +23,7 @@ namespace mwmp
|
|||
#endif
|
||||
void Tick();
|
||||
|
||||
bool IsEnd();
|
||||
bool IsEnded();
|
||||
void Stop();
|
||||
void Start();
|
||||
void Restart(int msec);
|
||||
|
@ -36,7 +32,7 @@ namespace mwmp
|
|||
std::string publ, arg_types;
|
||||
std::vector<boost::any> args;
|
||||
Script *scr;
|
||||
bool end;
|
||||
bool isEnded;
|
||||
};
|
||||
|
||||
class TimerAPI
|
||||
|
@ -50,7 +46,7 @@ namespace mwmp
|
|||
static void ResetTimer(int timerid, long msec);
|
||||
static void StartTimer(int timerid);
|
||||
static void StopTimer(int timerid);
|
||||
static bool IsEndTimer(int timerid);
|
||||
static bool IsTimerElapsed(int timerid);
|
||||
|
||||
static void Terminate();
|
||||
|
||||
|
|
|
@ -537,7 +537,7 @@ void ActorFunctions::InitializeActorList(unsigned short pid) noexcept
|
|||
|
||||
void ActorFunctions::CopyLastActorListToStore() noexcept
|
||||
{
|
||||
CopyLastActorListToStore();
|
||||
CopyReceivedActorListToStore();
|
||||
}
|
||||
|
||||
unsigned int ActorFunctions::GetActorRefNumIndex(unsigned int index) noexcept
|
||||
|
|
|
@ -1,38 +1,12 @@
|
|||
#include "Miscellaneous.hpp"
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <components/openmw-mp/Log.hpp>
|
||||
|
||||
#include <apps/openmw-mp/Script/ScriptFunctions.hpp>
|
||||
#include <apps/openmw-mp/Networking.hpp>
|
||||
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
static std::string tempFilename;
|
||||
|
||||
bool MiscellaneousFunctions::DoesFileExist(const char *filePath) noexcept
|
||||
{
|
||||
return boost::filesystem::exists(filePath);
|
||||
}
|
||||
|
||||
const char *MiscellaneousFunctions::GetCaseInsensitiveFilename(const char *folderPath, const char *filename) noexcept
|
||||
{
|
||||
if (!boost::filesystem::exists(folderPath)) return "invalid";
|
||||
|
||||
boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end
|
||||
|
||||
for (boost::filesystem::directory_iterator itr(folderPath); itr != end_itr; ++itr)
|
||||
{
|
||||
if (Misc::StringUtils::ciEqual(itr->path().filename().string(), filename))
|
||||
{
|
||||
tempFilename = itr->path().filename().string();
|
||||
return tempFilename.c_str();
|
||||
}
|
||||
}
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
unsigned int MiscellaneousFunctions::GetLastPlayerId() noexcept
|
||||
{
|
||||
return Players::getLastPlayerId();
|
||||
|
@ -47,23 +21,3 @@ void MiscellaneousFunctions::SetCurrentMpNum(int mpNum) noexcept
|
|||
{
|
||||
mwmp::Networking::getPtr()->setCurrentMpNum(mpNum);
|
||||
}
|
||||
|
||||
int MiscellaneousFunctions::GetPluginEnforcementState() noexcept
|
||||
{
|
||||
return mwmp::Networking::getPtr()->getPluginEnforcementState();
|
||||
}
|
||||
|
||||
void MiscellaneousFunctions::SetPluginEnforcementState(bool state) noexcept
|
||||
{
|
||||
mwmp::Networking::getPtr()->setPluginEnforcementState(state);
|
||||
}
|
||||
|
||||
void MiscellaneousFunctions::LogMessage(unsigned short level, const char *message) noexcept
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(level, "[Script]: %s", message);
|
||||
}
|
||||
|
||||
void MiscellaneousFunctions::LogAppend(unsigned short level, const char *message) noexcept
|
||||
{
|
||||
LOG_APPEND(level, "[Script]: %s", message);
|
||||
}
|
||||
|
|
|
@ -4,45 +4,15 @@
|
|||
#include "../Types.hpp"
|
||||
|
||||
#define MISCELLANEOUSAPI \
|
||||
{"DoesFileExist", MiscellaneousFunctions::DoesFileExist},\
|
||||
{"GetCaseInsensitiveFilename", MiscellaneousFunctions::GetCaseInsensitiveFilename},\
|
||||
\
|
||||
{"GetLastPlayerId", MiscellaneousFunctions::GetLastPlayerId},\
|
||||
\
|
||||
{"GetCurrentMpNum", MiscellaneousFunctions::GetCurrentMpNum},\
|
||||
{"SetCurrentMpNum", MiscellaneousFunctions::SetCurrentMpNum},\
|
||||
\
|
||||
{"GetPluginEnforcementState", MiscellaneousFunctions::GetPluginEnforcementState},\
|
||||
{"SetPluginEnforcementState", MiscellaneousFunctions::SetPluginEnforcementState},\
|
||||
\
|
||||
{"LogMessage", MiscellaneousFunctions::LogMessage},\
|
||||
{"LogAppend", MiscellaneousFunctions::LogAppend}
|
||||
{"SetCurrentMpNum", MiscellaneousFunctions::SetCurrentMpNum}
|
||||
|
||||
class MiscellaneousFunctions
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief Check whether a certain file exists.
|
||||
*
|
||||
* This will be a case sensitive check on case sensitive filesystems.
|
||||
*
|
||||
* Whenever you want to enforce case insensitivity, use GetCaseInsensitiveFilename() instead.
|
||||
*
|
||||
* \return Whether the file exists or not.
|
||||
*/
|
||||
static bool DoesFileExist(const char *filePath) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the first filename in a folder that has a case insensitive match with the filename
|
||||
* argument.
|
||||
*
|
||||
* This is used to retain case insensitivity when opening data files on Linux.
|
||||
*
|
||||
* \return The filename that matches.
|
||||
*/
|
||||
static const char *GetCaseInsensitiveFilename(const char *folderPath, const char *filename) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the last player ID currently connected to the server.
|
||||
*
|
||||
|
@ -78,49 +48,6 @@ public:
|
|||
* \return void
|
||||
*/
|
||||
static void SetCurrentMpNum(int mpNum) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the plugin enforcement state of the server.
|
||||
*
|
||||
* If true, clients are required to use the same plugins as set for the server.
|
||||
*
|
||||
* \return The enforcement state.
|
||||
*/
|
||||
static int GetPluginEnforcementState() noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the plugin enforcement state of the server.
|
||||
*
|
||||
* If true, clients are required to use the same plugins as set for the server.
|
||||
*
|
||||
* \param state The new enforcement state.
|
||||
* \return void
|
||||
*/
|
||||
static void SetPluginEnforcementState(bool state) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Write a log message with its own timestamp.
|
||||
*
|
||||
* It will have "[Script]:" prepended to it so as to mark it as a script-generated log message.
|
||||
*
|
||||
* \param level The logging level used (0 for LOG_VERBOSE, 1 for LOG_INFO, 2 for LOG_WARN,
|
||||
* 3 for LOG_ERROR, 4 for LOG_FATAL).
|
||||
* \param message The message logged.
|
||||
* \return void
|
||||
*/
|
||||
static void LogMessage(unsigned short level, const char *message) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Write a log message without its own timestamp.
|
||||
*
|
||||
* It will have "[Script]:" prepended to it so as to mark it as a script-generated log message.
|
||||
*
|
||||
* \param level The logging level used (0 for LOG_VERBOSE, 1 for LOG_INFO, 2 for LOG_WARN,
|
||||
* 3 for LOG_ERROR, 4 for LOG_FATAL).
|
||||
* \param message The message logged.
|
||||
* \return void
|
||||
*/
|
||||
static void LogAppend(unsigned short level, const char *message) noexcept;
|
||||
};
|
||||
|
||||
#endif //OPENMW_MISCELLANEOUSAPI_HPP
|
||||
|
|
|
@ -399,6 +399,15 @@ void ObjectFunctions::SetObjectRotation(double x, double y, double z) noexcept
|
|||
tempObject.position.rot[2] = z;
|
||||
}
|
||||
|
||||
void ObjectFunctions::SetObjectActivatingPid(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, );
|
||||
|
||||
tempObject.activatingActor.guid = player->guid;
|
||||
tempObject.activatingActor.isPlayer = true;
|
||||
}
|
||||
|
||||
void ObjectFunctions::SetObjectDoorState(int doorState) noexcept
|
||||
{
|
||||
tempObject.doorState = doorState;
|
||||
|
|
|
@ -85,6 +85,8 @@
|
|||
{"SetObjectPosition", ObjectFunctions::SetObjectPosition},\
|
||||
{"SetObjectRotation", ObjectFunctions::SetObjectRotation},\
|
||||
\
|
||||
{"SetObjectActivatingPid", ObjectFunctions::SetObjectActivatingPid},\
|
||||
\
|
||||
{"SetObjectDoorState", ObjectFunctions::SetObjectDoorState},\
|
||||
{"SetObjectDoorTeleportState", ObjectFunctions::SetObjectDoorTeleportState},\
|
||||
{"SetObjectDoorDestinationCell", ObjectFunctions::SetObjectDoorDestinationCell},\
|
||||
|
@ -776,6 +778,15 @@ public:
|
|||
*/
|
||||
static void SetObjectRotation(double x, double y, double z) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the player ID of the player activating the temporary object stored on the
|
||||
* server. Currently only used for ObjectActivate packets.
|
||||
*
|
||||
* \param pid The pid of the player.
|
||||
* \return void
|
||||
*/
|
||||
static void SetObjectActivatingPid(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the door state of the temporary object stored on the server.
|
||||
*
|
||||
|
|
|
@ -7,20 +7,6 @@
|
|||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
void PositionFunctions::GetPos(unsigned short pid, float *x, float *y, float *z) noexcept
|
||||
{
|
||||
*x = 0.00;
|
||||
*y = 0.00;
|
||||
*z = 0.00;
|
||||
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player,);
|
||||
|
||||
*x = player->position.pos[0];
|
||||
*y = player->position.pos[1];
|
||||
*z = player->position.pos[2];
|
||||
}
|
||||
|
||||
double PositionFunctions::GetPosX(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
|
@ -69,20 +55,6 @@ double PositionFunctions::GetPreviousCellPosZ(unsigned short pid) noexcept
|
|||
return player->previousCellPosition.pos[2];
|
||||
}
|
||||
|
||||
void PositionFunctions::GetRot(unsigned short pid, float *x, float *y, float *z) noexcept
|
||||
{
|
||||
*x = 0.00;
|
||||
*y = 0.00;
|
||||
*z = 0.00;
|
||||
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, );
|
||||
|
||||
*x = player->position.rot[0];
|
||||
*y = player->position.rot[1];
|
||||
*z = player->position.rot[2];
|
||||
}
|
||||
|
||||
double PositionFunctions::GetRotX(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include "../Types.hpp"
|
||||
|
||||
#define POSITIONAPI \
|
||||
{"GetPos", PositionFunctions::GetPos},\
|
||||
{"GetPosX", PositionFunctions::GetPosX},\
|
||||
{"GetPosY", PositionFunctions::GetPosY},\
|
||||
{"GetPosZ", PositionFunctions::GetPosZ},\
|
||||
|
@ -13,7 +12,6 @@
|
|||
{"GetPreviousCellPosY", PositionFunctions::GetPreviousCellPosY},\
|
||||
{"GetPreviousCellPosZ", PositionFunctions::GetPreviousCellPosZ},\
|
||||
\
|
||||
{"GetRot", PositionFunctions::GetRot},\
|
||||
{"GetRotX", PositionFunctions::GetRotX},\
|
||||
{"GetRotZ", PositionFunctions::GetRotZ},\
|
||||
\
|
||||
|
@ -29,18 +27,6 @@ class PositionFunctions
|
|||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief Assign the player's positional coordinate values to the variables passed as
|
||||
* parameters.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \param x The variable for the X position.
|
||||
* \param y The variable for the Y position.
|
||||
* \param z The variable for the Z position.
|
||||
* \return void
|
||||
*/
|
||||
static void GetPos(unsigned short pid, float *x, float *y, float *z) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the X position of a player.
|
||||
*
|
||||
|
@ -89,18 +75,6 @@ public:
|
|||
*/
|
||||
static double GetPreviousCellPosZ(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Assign the player's rotational coordinate values to the variables passed as
|
||||
* parameters.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \param x The variable for the X rotation.
|
||||
* \param y The variable for the Y rotation.
|
||||
* \param z The variable for the Z rotation.
|
||||
* \return void
|
||||
*/
|
||||
static void GetRot(unsigned short pid, float *x, float *y, float *z) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the X rotation of a player.
|
||||
*
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "Server.hpp"
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
#include <components/openmw-mp/Log.hpp>
|
||||
#include <components/openmw-mp/Version.hpp>
|
||||
|
@ -7,7 +8,20 @@
|
|||
#include <apps/openmw-mp/Script/ScriptFunctions.hpp>
|
||||
#include <apps/openmw-mp/Networking.hpp>
|
||||
#include <apps/openmw-mp/MasterClient.hpp>
|
||||
#include <Script/Script.hpp>
|
||||
|
||||
static std::string tempFilename;
|
||||
static std::chrono::high_resolution_clock::time_point startupTime = std::chrono::high_resolution_clock::now();
|
||||
|
||||
void ServerFunctions::LogMessage(unsigned short level, const char *message) noexcept
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(level, "[Script]: %s", message);
|
||||
}
|
||||
|
||||
void ServerFunctions::LogAppend(unsigned short level, const char *message) noexcept
|
||||
{
|
||||
LOG_APPEND(level, "[Script]: %s", message);
|
||||
}
|
||||
|
||||
void ServerFunctions::StopServer(int code) noexcept
|
||||
{
|
||||
|
@ -18,7 +32,10 @@ void ServerFunctions::Kick(unsigned short pid) noexcept
|
|||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player,);
|
||||
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Kicking player %s (%i)", player->npc.mName.c_str(), player->getId());
|
||||
mwmp::Networking::getPtr()->kickPlayer(player->guid);
|
||||
player->setLoadState(Player::KICKED);
|
||||
}
|
||||
|
||||
void ServerFunctions::BanAddress(const char *ipAddress) noexcept
|
||||
|
@ -31,6 +48,52 @@ void ServerFunctions::UnbanAddress(const char *ipAddress) noexcept
|
|||
mwmp::Networking::getPtr()->unbanAddress(ipAddress);
|
||||
}
|
||||
|
||||
bool ServerFunctions::DoesFilePathExist(const char *filePath) noexcept
|
||||
{
|
||||
return boost::filesystem::exists(filePath);
|
||||
}
|
||||
|
||||
const char *ServerFunctions::GetCaseInsensitiveFilename(const char *folderPath, const char *filename) noexcept
|
||||
{
|
||||
if (!boost::filesystem::exists(folderPath)) return "invalid";
|
||||
|
||||
boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end
|
||||
|
||||
for (boost::filesystem::directory_iterator itr(folderPath); itr != end_itr; ++itr)
|
||||
{
|
||||
if (Misc::StringUtils::ciEqual(itr->path().filename().string(), filename))
|
||||
{
|
||||
tempFilename = itr->path().filename().string();
|
||||
return tempFilename.c_str();
|
||||
}
|
||||
}
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
const char* ServerFunctions::GetDataPath() noexcept
|
||||
{
|
||||
return Script::GetModDir();
|
||||
}
|
||||
|
||||
unsigned int ServerFunctions::GetMillisecondsSinceServerStart() noexcept
|
||||
{
|
||||
std::chrono::high_resolution_clock::time_point currentTime = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::milliseconds milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - startupTime);
|
||||
return milliseconds.count();
|
||||
}
|
||||
|
||||
const char *ServerFunctions::GetOperatingSystemType() noexcept
|
||||
{
|
||||
static const std::string operatingSystemType = Utils::getOperatingSystemType();
|
||||
return operatingSystemType.c_str();
|
||||
}
|
||||
|
||||
const char *ServerFunctions::GetArchitectureType() noexcept
|
||||
{
|
||||
static const std::string architectureType = Utils::getArchitectureType();
|
||||
return architectureType.c_str();
|
||||
}
|
||||
|
||||
const char *ServerFunctions::GetServerVersion() noexcept
|
||||
{
|
||||
return TES3MP_VERSION;
|
||||
|
@ -72,6 +135,16 @@ bool ServerFunctions::HasPassword() noexcept
|
|||
return mwmp::Networking::get().isPassworded();
|
||||
}
|
||||
|
||||
bool ServerFunctions::GetDataFileEnforcementState() noexcept
|
||||
{
|
||||
return mwmp::Networking::getPtr()->getDataFileEnforcementState();
|
||||
}
|
||||
|
||||
bool ServerFunctions::GetScriptErrorIgnoringState() noexcept
|
||||
{
|
||||
return mwmp::Networking::getPtr()->getScriptErrorIgnoringState();
|
||||
}
|
||||
|
||||
void ServerFunctions::SetGameMode(const char *gameMode) noexcept
|
||||
{
|
||||
if (mwmp::Networking::getPtr()->getMasterClient())
|
||||
|
@ -89,6 +162,16 @@ void ServerFunctions::SetServerPassword(const char *password) noexcept
|
|||
mwmp::Networking::getPtr()->setServerPassword(password);
|
||||
}
|
||||
|
||||
void ServerFunctions::SetDataFileEnforcementState(bool state) noexcept
|
||||
{
|
||||
mwmp::Networking::getPtr()->setDataFileEnforcementState(state);
|
||||
}
|
||||
|
||||
void ServerFunctions::SetScriptErrorIgnoringState(bool state) noexcept
|
||||
{
|
||||
mwmp::Networking::getPtr()->setScriptErrorIgnoringState(state);
|
||||
}
|
||||
|
||||
void ServerFunctions::SetRuleString(const char *key, const char *value) noexcept
|
||||
{
|
||||
auto mc = mwmp::Networking::getPtr()->getMasterClient();
|
||||
|
@ -102,3 +185,64 @@ void ServerFunctions::SetRuleValue(const char *key, double value) noexcept
|
|||
if (mc)
|
||||
mc->SetRuleValue(key, value);
|
||||
}
|
||||
|
||||
void ServerFunctions::AddDataFileRequirement(const char *dataFilename, const char *checksumString) noexcept
|
||||
{
|
||||
auto &samples = mwmp::Networking::getPtr()->getSamples();
|
||||
|
||||
auto it = std::find_if(samples.begin(), samples.end(), [&dataFilename](mwmp::PacketPreInit::PluginPair &item) {
|
||||
return item.first == dataFilename;
|
||||
});
|
||||
|
||||
if (it != samples.end())
|
||||
{
|
||||
// If this is a filename we've added before, ensure our new checksumString for it isn't empty
|
||||
if (strlen(checksumString) != 0)
|
||||
it->second.push_back((unsigned)std::stoul(checksumString));
|
||||
}
|
||||
else
|
||||
{
|
||||
mwmp::PacketPreInit::HashList checksumList;
|
||||
|
||||
unsigned checksum = 0;
|
||||
|
||||
if (strlen(checksumString) != 0)
|
||||
{
|
||||
checksum = (unsigned) std::stoul(checksumString);
|
||||
checksumList.push_back(checksum);
|
||||
}
|
||||
samples.emplace_back(dataFilename, checksumList);
|
||||
|
||||
auto masterClient = mwmp::Networking::getPtr()->getMasterClient();
|
||||
|
||||
if (masterClient)
|
||||
masterClient->PushPlugin({dataFilename, checksum});
|
||||
}
|
||||
}
|
||||
|
||||
// All methods below are deprecated versions of methods from above
|
||||
|
||||
bool ServerFunctions::DoesFileExist(const char *filePath) noexcept
|
||||
{
|
||||
return DoesFilePathExist(filePath);
|
||||
}
|
||||
|
||||
const char* ServerFunctions::GetModDir() noexcept
|
||||
{
|
||||
return GetDataPath();
|
||||
}
|
||||
|
||||
bool ServerFunctions::GetPluginEnforcementState() noexcept
|
||||
{
|
||||
return mwmp::Networking::getPtr()->getDataFileEnforcementState();
|
||||
}
|
||||
|
||||
void ServerFunctions::SetPluginEnforcementState(bool state) noexcept
|
||||
{
|
||||
SetDataFileEnforcementState(state);
|
||||
}
|
||||
|
||||
void ServerFunctions::AddPluginHash(const char *pluginName, const char *checksumString) noexcept
|
||||
{
|
||||
AddDataFileRequirement(pluginName, checksumString);
|
||||
}
|
||||
|
|
|
@ -4,12 +4,21 @@
|
|||
#include "../Types.hpp"
|
||||
|
||||
#define SERVERAPI \
|
||||
{"LogMessage", ServerFunctions::LogMessage},\
|
||||
{"LogAppend", ServerFunctions::LogAppend},\
|
||||
\
|
||||
{"StopServer", ServerFunctions::StopServer},\
|
||||
\
|
||||
{"Kick", ServerFunctions::Kick},\
|
||||
{"BanAddress", ServerFunctions::BanAddress},\
|
||||
{"UnbanAddress", ServerFunctions::UnbanAddress},\
|
||||
\
|
||||
{"DoesFilePathExist", ServerFunctions::DoesFilePathExist},\
|
||||
{"GetCaseInsensitiveFilename", ServerFunctions::GetCaseInsensitiveFilename},\
|
||||
{"GetDataPath", ServerFunctions::GetDataPath},\
|
||||
{"GetMillisecondsSinceServerStart", ServerFunctions::GetMillisecondsSinceServerStart},\
|
||||
{"GetOperatingSystemType", ServerFunctions::GetOperatingSystemType},\
|
||||
{"GetArchitectureType", ServerFunctions::GetArchitectureType},\
|
||||
{"GetServerVersion", ServerFunctions::GetServerVersion},\
|
||||
{"GetProtocolVersion", ServerFunctions::GetProtocolVersion},\
|
||||
{"GetAvgPing", ServerFunctions::GetAvgPing},\
|
||||
|
@ -17,17 +26,53 @@
|
|||
{"GetMaxPlayers", ServerFunctions::GetMaxPlayers},\
|
||||
{"GetPort", ServerFunctions::GetPort},\
|
||||
{"HasPassword", ServerFunctions::HasPassword},\
|
||||
{"GetDataFileEnforcementState", ServerFunctions::GetDataFileEnforcementState},\
|
||||
{"GetScriptErrorIgnoringState", ServerFunctions::GetScriptErrorIgnoringState},\
|
||||
\
|
||||
{"SetGameMode", ServerFunctions::SetGameMode},\
|
||||
{"SetHostname", ServerFunctions::SetHostname},\
|
||||
{"SetServerPassword", ServerFunctions::SetServerPassword},\
|
||||
{"SetDataFileEnforcementState", ServerFunctions::SetDataFileEnforcementState},\
|
||||
{"SetScriptErrorIgnoringState", ServerFunctions::SetScriptErrorIgnoringState},\
|
||||
{"SetRuleString", ServerFunctions::SetRuleString},\
|
||||
{"SetRuleValue", ServerFunctions::SetRuleValue}
|
||||
{"SetRuleValue", ServerFunctions::SetRuleValue},\
|
||||
\
|
||||
{"AddDataFileRequirement", ServerFunctions::AddDataFileRequirement},\
|
||||
\
|
||||
{"DoesFileExist", ServerFunctions::DoesFileExist},\
|
||||
{"GetModDir", ServerFunctions::GetModDir},\
|
||||
{"GetPluginEnforcementState", ServerFunctions::GetPluginEnforcementState},\
|
||||
{"SetPluginEnforcementState", ServerFunctions::SetPluginEnforcementState},\
|
||||
{"AddPluginHash", ServerFunctions::AddPluginHash}
|
||||
|
||||
class ServerFunctions
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief Write a log message with its own timestamp.
|
||||
*
|
||||
* It will have "[Script]:" prepended to it so as to mark it as a script-generated log message.
|
||||
*
|
||||
* \param level The logging level used (0 for LOG_VERBOSE, 1 for LOG_INFO, 2 for LOG_WARN,
|
||||
* 3 for LOG_ERROR, 4 for LOG_FATAL).
|
||||
* \param message The message logged.
|
||||
* \return void
|
||||
*/
|
||||
static void LogMessage(unsigned short level, const char *message) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Write a log message without its own timestamp.
|
||||
*
|
||||
* It will have "[Script]:" prepended to it so as to mark it as a script-generated log message.
|
||||
*
|
||||
* \param level The logging level used (0 for LOG_VERBOSE, 1 for LOG_INFO, 2 for LOG_WARN,
|
||||
* 3 for LOG_ERROR, 4 for LOG_FATAL).
|
||||
* \param message The message logged.
|
||||
* \return void
|
||||
*/
|
||||
static void LogAppend(unsigned short level, const char *message) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Shut down the server.
|
||||
*
|
||||
|
@ -60,6 +105,59 @@ public:
|
|||
*/
|
||||
static void UnbanAddress(const char *ipAddress) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Check whether a certain file path exists.
|
||||
*
|
||||
* This will be a case sensitive check on case sensitive filesystems.
|
||||
*
|
||||
* Whenever you want to enforce case insensitivity, use GetCaseInsensitiveFilename() instead.
|
||||
*
|
||||
* \return Whether the file exists or not.
|
||||
*/
|
||||
static bool DoesFilePathExist(const char *filePath) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the first filename in a folder that has a case insensitive match with the filename
|
||||
* argument.
|
||||
*
|
||||
* This is used to retain case insensitivity when opening data files on Linux.
|
||||
*
|
||||
* \return The filename that matches.
|
||||
*/
|
||||
static const char *GetCaseInsensitiveFilename(const char *folderPath, const char *filename) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the path of the server's data folder.
|
||||
*
|
||||
* \return The data path.
|
||||
*/
|
||||
static const char *GetDataPath() noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the milliseconds elapsed since the server was started.
|
||||
*
|
||||
* \return The time since the server's startup in milliseconds.
|
||||
*/
|
||||
static unsigned int GetMillisecondsSinceServerStart() noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the type of the operating system used by the server.
|
||||
*
|
||||
* Note: Currently, the type can be "Windows", "Linux", "OS X" or "Unknown OS".
|
||||
*
|
||||
* \return The type of the operating system.
|
||||
*/
|
||||
static const char *GetOperatingSystemType() noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the architecture type used by the server.
|
||||
*
|
||||
* Note: Currently, the type can be "64-bit", "32-bit", "ARMv#" or "Unknown architecture".
|
||||
*
|
||||
* \return The architecture type.
|
||||
*/
|
||||
static const char *GetArchitectureType() noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the TES3MP version of the server.
|
||||
*
|
||||
|
@ -93,7 +191,7 @@ public:
|
|||
/**
|
||||
* \brief Get the port used by the server.
|
||||
*
|
||||
* \return Port
|
||||
* \return The port.
|
||||
*/
|
||||
static unsigned short GetPort() noexcept;
|
||||
|
||||
|
@ -111,6 +209,24 @@ public:
|
|||
*/
|
||||
static bool HasPassword() noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the data file enforcement state of the server.
|
||||
*
|
||||
* If true, clients are required to use the same data files as set for the server.
|
||||
*
|
||||
* \return The enforcement state.
|
||||
*/
|
||||
static bool GetDataFileEnforcementState() noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the script error ignoring state of the server.
|
||||
*
|
||||
* If true, script errors will not crash the server.
|
||||
*
|
||||
* \return The script error ignoring state.
|
||||
*/
|
||||
static bool GetScriptErrorIgnoringState() noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the game mode of the server, as displayed in the server browser.
|
||||
*
|
||||
|
@ -133,7 +249,29 @@ public:
|
|||
* \param password The password.
|
||||
* \return void
|
||||
*/
|
||||
static void SetServerPassword(const char *passw) noexcept;
|
||||
static void SetServerPassword(const char *password) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the data file enforcement state of the server.
|
||||
*
|
||||
* If true, clients are required to use the same data files as set for the server.
|
||||
*
|
||||
* \param state The new enforcement state.
|
||||
* \return void
|
||||
*/
|
||||
static void SetDataFileEnforcementState(bool state) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set whether script errors should be ignored or not.
|
||||
*
|
||||
* If true, script errors will not crash the server, but could have any number
|
||||
* of unforeseen consequences, which is why this is a highly experimental
|
||||
* setting.
|
||||
*
|
||||
* \param state The new script error ignoring state.
|
||||
* \return void
|
||||
*/
|
||||
static void SetScriptErrorIgnoringState(bool state) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set a rule string for the server details displayed in the server browser.
|
||||
|
@ -152,6 +290,28 @@ public:
|
|||
* \return void
|
||||
*/
|
||||
static void SetRuleValue(const char *key, double value) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Add a data file and a corresponding CRC32 checksum to the data file loadout
|
||||
* that connecting clients need to match.
|
||||
*
|
||||
* It can be used multiple times to set multiple checksums for the same data file.
|
||||
*
|
||||
* Note: If an empty string is provided for the checksum, a checksum will not be
|
||||
* required for that data file.
|
||||
*
|
||||
* @param dataFilename The filename of the data file.
|
||||
* @param checksumString A string with the CRC32 checksum required.
|
||||
*/
|
||||
static void AddDataFileRequirement(const char *dataFilename, const char *checksumString) noexcept;
|
||||
|
||||
// All methods below are deprecated versions of methods from above
|
||||
|
||||
static bool DoesFileExist(const char *filePath) noexcept;
|
||||
static const char *GetModDir() noexcept;
|
||||
static bool GetPluginEnforcementState() noexcept;
|
||||
static void SetPluginEnforcementState(bool state) noexcept;
|
||||
static void AddPluginHash(const char *pluginName, const char *checksumString) noexcept;
|
||||
};
|
||||
|
||||
#endif //OPENMW_SERVERAPI_HPP
|
||||
|
|
|
@ -201,6 +201,17 @@ int StatsFunctions::GetAttributeModifier(unsigned short pid, unsigned short attr
|
|||
return player->creatureStats.mAttributes[attributeId].mMod;
|
||||
}
|
||||
|
||||
double StatsFunctions::GetAttributeDamage(unsigned short pid, unsigned short attributeId) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
|
||||
if (attributeId >= Attribute::Length)
|
||||
return 0;
|
||||
|
||||
return player->creatureStats.mAttributes[attributeId].mDamage;
|
||||
}
|
||||
|
||||
int StatsFunctions::GetSkillBase(unsigned short pid, unsigned short skillId) noexcept
|
||||
{
|
||||
Player *player;
|
||||
|
@ -223,6 +234,17 @@ int StatsFunctions::GetSkillModifier(unsigned short pid, unsigned short skillId)
|
|||
return player->npcStats.mSkills[skillId].mMod;
|
||||
}
|
||||
|
||||
double StatsFunctions::GetSkillDamage(unsigned short pid, unsigned short skillId) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
|
||||
if (skillId >= Skill::Length)
|
||||
return 0;
|
||||
|
||||
return player->npcStats.mSkills[skillId].mDamage;
|
||||
}
|
||||
|
||||
double StatsFunctions::GetSkillProgress(unsigned short pid, unsigned short skillId) noexcept
|
||||
{
|
||||
Player *player;
|
||||
|
@ -437,6 +459,20 @@ void StatsFunctions::ClearAttributeModifier(unsigned short pid, unsigned short a
|
|||
player->attributeIndexChanges.push_back(attributeId);
|
||||
}
|
||||
|
||||
void StatsFunctions::SetAttributeDamage(unsigned short pid, unsigned short attributeId, double value) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, );
|
||||
|
||||
if (attributeId >= Attribute::Length)
|
||||
return;
|
||||
|
||||
player->creatureStats.mAttributes[attributeId].mDamage = value;
|
||||
|
||||
if (!Utils::vectorContains(player->attributeIndexChanges, attributeId))
|
||||
player->attributeIndexChanges.push_back(attributeId);
|
||||
}
|
||||
|
||||
void StatsFunctions::SetSkillBase(unsigned short pid, unsigned short skillId, int value) noexcept
|
||||
{
|
||||
Player *player;
|
||||
|
@ -465,6 +501,20 @@ void StatsFunctions::ClearSkillModifier(unsigned short pid, unsigned short skill
|
|||
player->skillIndexChanges.push_back(skillId);
|
||||
}
|
||||
|
||||
void StatsFunctions::SetSkillDamage(unsigned short pid, unsigned short skillId, double value) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, );
|
||||
|
||||
if (skillId >= Skill::Length)
|
||||
return;
|
||||
|
||||
player->npcStats.mSkills[skillId].mDamage = value;
|
||||
|
||||
if (!Utils::vectorContains(player->skillIndexChanges, skillId))
|
||||
player->skillIndexChanges.push_back(skillId);
|
||||
}
|
||||
|
||||
void StatsFunctions::SetSkillProgress(unsigned short pid, unsigned short skillId, double value) noexcept
|
||||
{
|
||||
Player *player;
|
||||
|
|
|
@ -30,9 +30,11 @@
|
|||
\
|
||||
{"GetAttributeBase", StatsFunctions::GetAttributeBase},\
|
||||
{"GetAttributeModifier", StatsFunctions::GetAttributeModifier},\
|
||||
{"GetAttributeDamage", StatsFunctions::GetAttributeDamage},\
|
||||
\
|
||||
{"GetSkillBase", StatsFunctions::GetSkillBase},\
|
||||
{"GetSkillModifier", StatsFunctions::GetSkillModifier},\
|
||||
{"GetSkillDamage", StatsFunctions::GetSkillDamage},\
|
||||
{"GetSkillProgress", StatsFunctions::GetSkillProgress},\
|
||||
{"GetSkillIncrease", StatsFunctions::GetSkillIncrease},\
|
||||
\
|
||||
|
@ -58,9 +60,11 @@
|
|||
\
|
||||
{"SetAttributeBase", StatsFunctions::SetAttributeBase},\
|
||||
{"ClearAttributeModifier", StatsFunctions::ClearAttributeModifier},\
|
||||
{"SetAttributeDamage", StatsFunctions::SetAttributeDamage},\
|
||||
\
|
||||
{"SetSkillBase", StatsFunctions::SetSkillBase},\
|
||||
{"ClearSkillModifier", StatsFunctions::ClearSkillModifier},\
|
||||
{"SetSkillDamage", StatsFunctions::SetSkillDamage},\
|
||||
{"SetSkillProgress", StatsFunctions::SetSkillProgress},\
|
||||
{"SetSkillIncrease", StatsFunctions::SetSkillIncrease},\
|
||||
\
|
||||
|
@ -267,6 +271,16 @@ public:
|
|||
*/
|
||||
static int GetAttributeModifier(unsigned short pid, unsigned short attributeId) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the amount of damage (as caused through the Damage Attribute effect)
|
||||
* to a player's attribute.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \param attributeId The attribute ID.
|
||||
* \return The amount of damage to the attribute.
|
||||
*/
|
||||
static double GetAttributeDamage(unsigned short pid, unsigned short attributeId) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the base value of a player's skill.
|
||||
*
|
||||
|
@ -285,6 +299,16 @@ public:
|
|||
*/
|
||||
static int GetSkillModifier(unsigned short pid, unsigned short skillId) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the amount of damage (as caused through the Damage Skill effect)
|
||||
* to a player's skill.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \param skillId The skill ID.
|
||||
* \return The amount of damage to the skill.
|
||||
*/
|
||||
static double GetSkillDamage(unsigned short pid, unsigned short skillId) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the progress the player has made towards increasing a certain skill by 1.
|
||||
*
|
||||
|
@ -477,6 +501,17 @@ public:
|
|||
*/
|
||||
static void ClearAttributeModifier(unsigned short pid, unsigned short attributeId) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the amount of damage (as caused through the Damage Attribute effect) to
|
||||
* a player's attribute.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \param attributeId The attribute ID.
|
||||
* \param value The amount of damage to the player's attribute.
|
||||
* \return void
|
||||
*/
|
||||
static void SetAttributeDamage(unsigned short pid, unsigned short attributeId, double value) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the base value of a player's skill.
|
||||
*
|
||||
|
@ -501,6 +536,17 @@ public:
|
|||
*/
|
||||
static void ClearSkillModifier(unsigned short pid, unsigned short skillId) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the amount of damage (as caused through the Damage Skill effect) to
|
||||
* a player's skill.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \param skillId The skill ID.
|
||||
* \param value The amount of damage to the player's skill.
|
||||
* \return void
|
||||
*/
|
||||
static void SetSkillDamage(unsigned short pid, unsigned short skillId, double value) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the progress the player has made towards increasing a certain skill by 1.
|
||||
*
|
||||
|
|
|
@ -21,7 +21,7 @@ int ScriptFunctions::CreateTimerEx(ScriptFunc callback, int msec, const char *ty
|
|||
try
|
||||
{
|
||||
vector<boost::any> params;
|
||||
GetArguments(params, args, types);
|
||||
Utils::getArguments(params, args, types);
|
||||
|
||||
return mwmp::TimerAPI::CreateTimer(callback, msec, types, params);
|
||||
}
|
||||
|
@ -54,5 +54,5 @@ void ScriptFunctions::FreeTimer(int timerId) noexcept
|
|||
|
||||
bool ScriptFunctions::IsTimerElapsed(int timerId) noexcept
|
||||
{
|
||||
return TimerAPI::IsEndTimer(timerId);
|
||||
return TimerAPI::IsTimerElapsed(timerId);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,24 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
std::set<std::string> LangLua::packagePath;
|
||||
std::set<std::string> LangLua::packageCPath;
|
||||
|
||||
void setLuaPath(lua_State* L, const char* path, bool cpath = false)
|
||||
{
|
||||
string field = cpath ? "cpath" : "path";
|
||||
lua_getglobal(L, "package");
|
||||
|
||||
lua_getfield(L, -1, field.c_str());
|
||||
std::string cur_path = lua_tostring(L, -1);
|
||||
cur_path.append(";");
|
||||
cur_path.append(path);
|
||||
lua_pop(L, 1);
|
||||
lua_pushstring(L, cur_path.c_str());
|
||||
lua_setfield(L, -2, field.c_str());
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
lib_t LangLua::GetInterface()
|
||||
{
|
||||
return reinterpret_cast<lib_t>(lua);
|
||||
|
@ -23,6 +41,17 @@ LangLua::LangLua()
|
|||
{
|
||||
lua = luaL_newstate();
|
||||
luaL_openlibs(lua); // load all lua std libs
|
||||
|
||||
std::string p, cp;
|
||||
for (auto& path : packagePath)
|
||||
p += path + ';';
|
||||
|
||||
for (auto& path : packageCPath)
|
||||
cp += path + ';';
|
||||
|
||||
setLuaPath(lua, p.c_str());
|
||||
setLuaPath(lua, cp.c_str(), true);
|
||||
|
||||
}
|
||||
|
||||
LangLua::~LangLua()
|
||||
|
@ -78,8 +107,18 @@ template<> struct F_<1> { static constexpr LuaFuctionData F{"CreateTimerEx", Lan
|
|||
template<> struct F_<2> { static constexpr LuaFuctionData F{"MakePublic", LangLua::MakePublic}; };
|
||||
template<> struct F_<3> { static constexpr LuaFuctionData F{"CallPublic", LangLua::CallPublic}; };
|
||||
|
||||
#ifdef __arm__
|
||||
template<std::size_t... Is>
|
||||
struct indices {};
|
||||
template<std::size_t N, std::size_t... Is>
|
||||
struct build_indices : build_indices<N-1, N-1, Is...> {};
|
||||
template<std::size_t... Is>
|
||||
struct build_indices<0, Is...> : indices<Is...> {};
|
||||
template<std::size_t N>
|
||||
using IndicesFor = build_indices<N>;
|
||||
|
||||
template<size_t... Indices>
|
||||
inline LuaFuctionData *LangLua::functions(indices<Indices...>)
|
||||
LuaFuctionData *functions(indices<Indices...>)
|
||||
{
|
||||
|
||||
static LuaFuctionData functions_[sizeof...(Indices)]{
|
||||
|
@ -93,6 +132,41 @@ inline LuaFuctionData *LangLua::functions(indices<Indices...>)
|
|||
|
||||
return functions_;
|
||||
}
|
||||
#else
|
||||
template<unsigned int I>
|
||||
struct C
|
||||
{
|
||||
constexpr static void Fn(LuaFuctionData *functions_)
|
||||
{
|
||||
functions_[I] = F_<I>::F;
|
||||
C<I - 1>::Fn(functions_);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct C<0>
|
||||
{
|
||||
constexpr static void Fn(LuaFuctionData *functions_)
|
||||
{
|
||||
functions_[0] = F_<0>::F;
|
||||
}
|
||||
};
|
||||
|
||||
template<size_t LastI>
|
||||
LuaFuctionData *functions()
|
||||
{
|
||||
|
||||
static LuaFuctionData functions_[LastI];
|
||||
C<LastI - 1>::Fn(functions_);
|
||||
|
||||
static_assert(
|
||||
sizeof(functions_) / sizeof(functions_[0]) ==
|
||||
sizeof(ScriptFunctions::functions) / sizeof(ScriptFunctions::functions[0]),
|
||||
"Not all functions have been mapped to Lua");
|
||||
|
||||
return functions_;
|
||||
}
|
||||
#endif
|
||||
|
||||
void LangLua::LoadProgram(const char *filename)
|
||||
{
|
||||
|
@ -104,8 +178,11 @@ void LangLua::LoadProgram(const char *filename)
|
|||
|
||||
constexpr auto functions_n = sizeof(ScriptFunctions::functions) / sizeof(ScriptFunctions::functions[0]);
|
||||
|
||||
#ifdef __arm__
|
||||
LuaFuctionData *functions_ = functions(IndicesFor<functions_n>{});
|
||||
|
||||
#else
|
||||
LuaFuctionData *functions_ = functions<sizeof(ScriptFunctions::functions) / sizeof(ScriptFunctions::functions[0])>();
|
||||
#endif
|
||||
luabridge::Namespace tes3mp = luabridge::getGlobalNamespace(lua).beginNamespace("tes3mp");
|
||||
|
||||
for (unsigned i = 0; i < functions_n; i++)
|
||||
|
@ -133,20 +210,65 @@ boost::any LangLua::Call(const char *name, const char *argl, int buf, ...)
|
|||
{
|
||||
va_list vargs;
|
||||
va_start(vargs, buf);
|
||||
std::vector<boost::any> args;
|
||||
|
||||
ScriptFunctions::GetArguments(args, vargs, argl);
|
||||
int n_args = (int)(strlen(argl));
|
||||
|
||||
return Call(name, argl, args);
|
||||
lua_getglobal(lua, name);
|
||||
|
||||
for (int index = 0; index < n_args; index++)
|
||||
{
|
||||
switch (argl[index])
|
||||
{
|
||||
case 'i':
|
||||
luabridge::Stack<unsigned int>::push(lua,va_arg(vargs, unsigned int));
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
luabridge::Stack<signed int>::push(lua,va_arg(vargs, signed int));
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
luabridge::Stack<unsigned long long>::push(lua, va_arg(vargs, unsigned long long));
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
luabridge::Stack<signed long long>::push(lua, va_arg(vargs, signed long long));
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
luabridge::Stack<double>::push(lua, va_arg(vargs, double));
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
luabridge::Stack<void*>::push(lua, va_arg(vargs, void*));
|
||||
break;
|
||||
|
||||
case 's':
|
||||
luabridge::Stack<const char*>::push(lua, va_arg(vargs, const char*));
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
luabridge::Stack<bool>::push(lua, (bool) va_arg(vargs, int));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw runtime_error("C++ call: Unknown argument identifier " + argl[index]);
|
||||
}
|
||||
}
|
||||
|
||||
va_end(vargs);
|
||||
|
||||
luabridge::LuaException::pcall(lua, n_args, 1);
|
||||
return boost::any(luabridge::LuaRef::fromStack(lua, -1));
|
||||
}
|
||||
|
||||
boost::any LangLua::Call(const char *name, const char *argl, const std::vector<boost::any> &args)
|
||||
{
|
||||
int n_args = (int)(strlen(argl)) ;
|
||||
int n_args = (int)(strlen(argl));
|
||||
|
||||
lua_getglobal (lua, name);
|
||||
lua_getglobal(lua, name);
|
||||
|
||||
for (intptr_t index = 0; index < n_args; index++)
|
||||
for (int index = 0; index < n_args; index++)
|
||||
{
|
||||
switch (argl[index])
|
||||
{
|
||||
|
@ -186,6 +308,16 @@ boost::any LangLua::Call(const char *name, const char *argl, const std::vector<b
|
|||
}
|
||||
}
|
||||
|
||||
luabridge::LuaException::pcall (lua, n_args, 1);
|
||||
luabridge::LuaException::pcall(lua, n_args, 1);
|
||||
return boost::any(luabridge::LuaRef::fromStack(lua, -1));
|
||||
}
|
||||
|
||||
void LangLua::AddPackagePath(const std::string& path)
|
||||
{
|
||||
packagePath.emplace(path);
|
||||
}
|
||||
|
||||
void LangLua::AddPackageCPath(const std::string& path)
|
||||
{
|
||||
packageCPath.emplace(path);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <extern/LuaBridge/LuaBridge.h>
|
||||
#include <LuaBridge.h>
|
||||
#include <set>
|
||||
|
||||
#include <boost/any.hpp>
|
||||
#include "../ScriptFunction.hpp"
|
||||
|
@ -22,25 +23,17 @@ struct LuaFuctionData
|
|||
|
||||
class LangLua: public Language
|
||||
{
|
||||
private:
|
||||
template<std::size_t... Is>
|
||||
struct indices {};
|
||||
template<std::size_t N, std::size_t... Is>
|
||||
struct build_indices : build_indices<N-1, N-1, Is...> {};
|
||||
template<std::size_t... Is>
|
||||
struct build_indices<0, Is...> : indices<Is...> {};
|
||||
template<std::size_t N>
|
||||
using IndicesFor = build_indices<N>;
|
||||
|
||||
public:
|
||||
virtual lib_t GetInterface() override;
|
||||
template<std::size_t... Indices>
|
||||
static LuaFuctionData* functions(indices<Indices...>);
|
||||
lua_State *lua;
|
||||
public:
|
||||
LangLua();
|
||||
LangLua(lua_State *lua);
|
||||
~LangLua();
|
||||
|
||||
static void AddPackagePath(const std::string &path);
|
||||
static void AddPackageCPath(const std::string &path);
|
||||
|
||||
static int MakePublic(lua_State *lua) noexcept;
|
||||
static int CallPublic(lua_State *lua);
|
||||
|
||||
|
@ -52,6 +45,9 @@ public:
|
|||
virtual bool IsCallbackPresent(const char *name) override;
|
||||
virtual boost::any Call(const char *name, const char *argl, int buf, ...) override;
|
||||
virtual boost::any Call(const char *name, const char *argl, const std::vector<boost::any> &args) override;
|
||||
private:
|
||||
static std::set<std::string> packageCPath;
|
||||
static std::set<std::string> packagePath;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
using namespace std;
|
||||
|
||||
Script::ScriptList Script::scripts;
|
||||
std::string Script::moddir;
|
||||
|
||||
Script::Script(const char *path)
|
||||
{
|
||||
|
@ -94,3 +95,14 @@ void Script::LoadScript(const char *script, const char *base)
|
|||
snprintf(path, sizeof(path), Utils::convertPath("%s/%s/%s").c_str(), base, "scripts", script);
|
||||
Script::scripts.emplace_back(new Script(path));
|
||||
}
|
||||
|
||||
void Script::SetModDir(const std::string &moddir)
|
||||
{
|
||||
if (Script::moddir.empty()) // do not allow to change in runtime
|
||||
Script::moddir = moddir;
|
||||
}
|
||||
|
||||
const char* Script::GetModDir()
|
||||
{
|
||||
return moddir.c_str();
|
||||
}
|
||||
|
|
|
@ -4,15 +4,18 @@
|
|||
|
||||
#ifndef PLUGINSYSTEM3_SCRIPT_HPP
|
||||
#define PLUGINSYSTEM3_SCRIPT_HPP
|
||||
|
||||
#include <boost/any.hpp>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
|
||||
#include "Types.hpp"
|
||||
#include "SystemInterface.hpp"
|
||||
#include "ScriptFunction.hpp"
|
||||
#include "ScriptFunctions.hpp"
|
||||
#include "Language.hpp"
|
||||
|
||||
#include <boost/any.hpp>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include "Networking.hpp"
|
||||
|
||||
class Script : private ScriptFunctions
|
||||
{
|
||||
|
@ -51,60 +54,27 @@ private:
|
|||
Script(const Script&) = delete;
|
||||
Script& operator=(const Script&) = delete;
|
||||
|
||||
protected:
|
||||
static std::string moddir;
|
||||
public:
|
||||
~Script();
|
||||
|
||||
static void LoadScript(const char *script, const char* base);
|
||||
static void LoadScripts(char* scripts, const char* base);
|
||||
static void UnloadScripts();
|
||||
static void SetModDir(const std::string &moddir);
|
||||
static const char* GetModDir();
|
||||
|
||||
static constexpr ScriptCallbackData const& CallBackData(const unsigned int I, const unsigned int N = 0) {
|
||||
return callbacks[N].index == I ? callbacks[N] : CallBackData(I, N + 1);
|
||||
}
|
||||
|
||||
template<unsigned int I>
|
||||
using CallBackReturn = typename CharType<CallBackData(I).callback.ret>::type;
|
||||
|
||||
template<size_t N>
|
||||
static constexpr unsigned int CallbackIdentity(const char(&str)[N])
|
||||
{
|
||||
return Utils::hash(str);
|
||||
}
|
||||
|
||||
|
||||
template<unsigned int I, bool B = false, typename... Args>
|
||||
static unsigned int Call(CallBackReturn<I>& result, Args&&... args) {
|
||||
constexpr ScriptCallbackData const& data = CallBackData(I);
|
||||
static_assert(data.callback.matches(TypeString<typename std::remove_reference<Args>::type...>::value),
|
||||
"Wrong number or types of arguments");
|
||||
|
||||
unsigned int count = 0;
|
||||
|
||||
for (auto& script : scripts)
|
||||
{
|
||||
if (!script->callbacks_.count(I))
|
||||
script->callbacks_.emplace(I, script->GetScript<FunctionEllipsis<void>>(data.name));
|
||||
|
||||
auto callback = script->callbacks_[I];
|
||||
|
||||
if (!callback)
|
||||
continue;
|
||||
|
||||
if (script->script_type == SCRIPT_CPP)
|
||||
result = reinterpret_cast<FunctionEllipsis<CallBackReturn<I>>>(callback)(std::forward<Args>(args)...);
|
||||
#if defined (ENABLE_LUA)
|
||||
else if (script->script_type == SCRIPT_LUA)
|
||||
{
|
||||
boost::any any = script->lang->Call(data.name, data.callback.types, B, std::forward<Args>(args)...);
|
||||
result = static_cast<CallBackReturn<I>>(boost::any_cast<luabridge::LuaRef>(any).cast<CallBackReturn<I>>());
|
||||
}
|
||||
#endif
|
||||
++count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
template<unsigned int I, bool B = false, typename... Args>
|
||||
static unsigned int Call(Args&&... args) {
|
||||
constexpr ScriptCallbackData const& data = CallBackData(I);
|
||||
|
@ -124,10 +94,23 @@ public:
|
|||
continue;
|
||||
|
||||
if (script->script_type == SCRIPT_CPP)
|
||||
reinterpret_cast<FunctionEllipsis<CallBackReturn<I>>>(callback)(std::forward<Args>(args)...);
|
||||
(callback)(std::forward<Args>(args)...);
|
||||
#if defined (ENABLE_LUA)
|
||||
else if (script->script_type == SCRIPT_LUA)
|
||||
{
|
||||
try
|
||||
{
|
||||
script->lang->Call(data.name, data.callback.types, B, std::forward<Args>(args)...);
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, e.what());
|
||||
Script::Call<Script::CallbackIdentity("OnServerScriptCrash")>(e.what());
|
||||
|
||||
if (!mwmp::Networking::getPtr()->getScriptErrorIgnoringState())
|
||||
throw;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
++count;
|
||||
}
|
||||
|
|
|
@ -6,10 +6,6 @@
|
|||
#include <stdexcept>
|
||||
#include "ScriptFunction.hpp"
|
||||
|
||||
#if !defined(_WIN32) && !defined(__ARM_ARCH) // temporarily disabled
|
||||
#include <call.hpp>
|
||||
#endif
|
||||
|
||||
#if defined (ENABLE_LUA)
|
||||
#include "LangLua/LangLua.hpp"
|
||||
#endif
|
||||
|
@ -70,48 +66,10 @@ boost::any ScriptFunction::Call(const vector<boost::any> &args)
|
|||
default:
|
||||
throw runtime_error("Lua call: Unknown return type" + ret_type);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
#if !defined(_WIN32) && !defined(__ARM_ARCH) // temporarily disabled
|
||||
string::iterator it;
|
||||
vector<boost::any>::const_iterator it2;
|
||||
vector<intptr_t> data;
|
||||
CallArgs callArgs;
|
||||
|
||||
for (it = def.begin(), it2 = args.begin(); it != def.end(); ++it, ++it2)
|
||||
{
|
||||
switch (*it)
|
||||
{
|
||||
case 'i':
|
||||
callArgs.push_integer(boost::any_cast<unsigned int>(*it2));
|
||||
break;
|
||||
case 'q':
|
||||
callArgs.push_integer(boost::any_cast<signed int>(*it2));
|
||||
break;
|
||||
case 'f':
|
||||
callArgs.push_double(boost::any_cast<double>(*it2));
|
||||
break;
|
||||
case 'd':
|
||||
callArgs.push_double(boost::any_cast<double*>(*it2));
|
||||
break;
|
||||
case 's':
|
||||
callArgs.push_stringPtr(boost::any_cast<const char *>(*it2));
|
||||
break;
|
||||
case 'v':
|
||||
result = boost::any();
|
||||
break;
|
||||
default:
|
||||
throw runtime_error("C++ call: Unknown argument identifier " + *it);
|
||||
lua_settop(fLua.lua, 0);
|
||||
}
|
||||
}
|
||||
Func f = reinterpret_cast<Func>(fCpp);
|
||||
result = ::Call(f, callArgs);
|
||||
#else
|
||||
throw runtime_error("C++ call: Windows and ARM not supported yet.");
|
||||
#endif
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -13,62 +13,6 @@ constexpr ScriptCallbackData ScriptFunctions::callbacks[];
|
|||
|
||||
using namespace std;
|
||||
|
||||
void ScriptFunctions::GetArguments(std::vector<boost::any> ¶ms, va_list args, const std::string &def)
|
||||
{
|
||||
params.reserve(def.length());
|
||||
|
||||
try
|
||||
{
|
||||
for (char c : def)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'i':
|
||||
params.emplace_back(va_arg(args, unsigned int));
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
params.emplace_back(va_arg(args, signed int));
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
params.emplace_back(va_arg(args, unsigned long long));
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
params.emplace_back(va_arg(args, signed long long));
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
params.emplace_back(va_arg(args, double));
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
params.emplace_back(va_arg(args, void*));
|
||||
break;
|
||||
|
||||
case 's':
|
||||
params.emplace_back(va_arg(args, const char*));
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
params.emplace_back(va_arg(args, int));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw runtime_error("C++ call: Unknown argument identifier " + c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
catch (...)
|
||||
{
|
||||
va_end(args);
|
||||
throw;
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void ScriptFunctions::MakePublic(ScriptFunc _public, const char *name, char ret_type, const char *def) noexcept
|
||||
{
|
||||
Public::MakePublic(_public, name, ret_type, def);
|
||||
|
@ -81,7 +25,7 @@ boost::any ScriptFunctions::CallPublic(const char *name, va_list args) noexcept
|
|||
try
|
||||
{
|
||||
string def = Public::GetDefinition(name);
|
||||
GetArguments(params, args, def);
|
||||
Utils::getArguments(params, args, def);
|
||||
|
||||
return Public::Call(name, params);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,10 @@
|
|||
|
||||
#include <components/openmw-mp/Log.hpp>
|
||||
|
||||
#ifndef __PRETTY_FUNCTION__
|
||||
#define __PRETTY_FUNCTION__ __FUNCTION__
|
||||
#endif
|
||||
|
||||
#define GET_PLAYER(pid, pl, retvalue) \
|
||||
pl = Players::getPlayer(pid); \
|
||||
if (player == 0) {\
|
||||
|
@ -43,7 +47,6 @@ class ScriptFunctions
|
|||
{
|
||||
public:
|
||||
|
||||
static void GetArguments(std::vector<boost::any> ¶ms, va_list args, const std::string &def);
|
||||
static void MakePublic(ScriptFunc _public, const char *name, char ret_type, const char *def) noexcept;
|
||||
static boost::any CallPublic(const char *name, va_list args) noexcept;
|
||||
|
||||
|
@ -116,9 +119,9 @@ public:
|
|||
|
||||
static constexpr ScriptFunctionData functions[]{
|
||||
{"CreateTimer", ScriptFunctions::CreateTimer},
|
||||
{"CreateTimerEx", reinterpret_cast<Function<void>>(ScriptFunctions::CreateTimerEx)},
|
||||
{"CreateTimerEx", ScriptFunctions::CreateTimerEx},
|
||||
{"MakePublic", ScriptFunctions::MakePublic},
|
||||
{"CallPublic", reinterpret_cast<Function<void>>(ScriptFunctions::CallPublic)},
|
||||
{"CallPublic", ScriptFunctions::CallPublic},
|
||||
|
||||
{"StartTimer", ScriptFunctions::StartTimer},
|
||||
{"StopTimer", ScriptFunctions::StopTimer},
|
||||
|
@ -150,63 +153,63 @@ public:
|
|||
};
|
||||
|
||||
static constexpr ScriptCallbackData callbacks[]{
|
||||
{"Main", Function<int, int, int>()},
|
||||
{"OnServerInit", Function<void>()},
|
||||
{"OnServerPostInit", Function<void>()},
|
||||
{"OnServerExit", Function<void, bool>()},
|
||||
{"OnPlayerConnect", Function<bool, unsigned short>()},
|
||||
{"OnPlayerDisconnect", Function<void, unsigned short>()},
|
||||
{"OnPlayerDeath", Function<void, unsigned short>()},
|
||||
{"OnPlayerResurrect", Function<void, unsigned short>()},
|
||||
{"OnPlayerCellChange", Function<void, unsigned short>()},
|
||||
{"OnPlayerAttribute", Function<void, unsigned short>()},
|
||||
{"OnPlayerSkill", Function<void, unsigned short>()},
|
||||
{"OnPlayerLevel", Function<void, unsigned short>()},
|
||||
{"OnPlayerBounty", Function<void, unsigned short>()},
|
||||
{"OnPlayerReputation", Function<void, unsigned short>()},
|
||||
{"OnPlayerEquipment", Function<void, unsigned short>()},
|
||||
{"OnPlayerInventory", Function<void, unsigned short>()},
|
||||
{"OnPlayerJournal", Function<void, unsigned short>()},
|
||||
{"OnPlayerFaction", Function<void, unsigned short>()},
|
||||
{"OnPlayerShapeshift", Function<void, unsigned short>()},
|
||||
{"OnPlayerSpellbook", Function<void, unsigned short>()},
|
||||
{"OnPlayerQuickKeys", Function<void, unsigned short>()},
|
||||
{"OnPlayerTopic", Function<void, unsigned short>()},
|
||||
{"OnPlayerDisposition", Function<void, unsigned short>()},
|
||||
{"OnPlayerBook", Function<void, unsigned short>()},
|
||||
{"OnPlayerItemUse", Function<void, unsigned short>()},
|
||||
{"OnPlayerMiscellaneous", Function<void, unsigned short>()},
|
||||
{"OnPlayerInput", Function<void, unsigned short>()},
|
||||
{"OnPlayerRest", Function<void, unsigned short>()},
|
||||
{"OnRecordDynamic", Function<void, unsigned short>()},
|
||||
{"OnCellLoad", Function<void, unsigned short, const char*>()},
|
||||
{"OnCellUnload", Function<void, unsigned short, const char*>()},
|
||||
{"OnCellDeletion", Function<void, const char*>()},
|
||||
{"OnContainer", Function<void, unsigned short, const char*>()},
|
||||
{"OnDoorState", Function<void, unsigned short, const char*>()},
|
||||
{"OnObjectActivate", Function<void, unsigned short, const char*>()},
|
||||
{"OnObjectPlace", Function<void, unsigned short, const char*>()},
|
||||
{"OnObjectState", Function<void, unsigned short, const char*>()},
|
||||
{"OnObjectSpawn", Function<void, unsigned short, const char*>()},
|
||||
{"OnObjectDelete", Function<void, unsigned short, const char*>()},
|
||||
{"OnObjectLock", Function<void, unsigned short, const char*>()},
|
||||
{"OnObjectScale", Function<void, unsigned short, const char*>()},
|
||||
{"OnObjectTrap", Function<void, unsigned short, const char*>()},
|
||||
{"OnVideoPlay", Function<void, unsigned short, const char*>()},
|
||||
{"OnActorList", Function<void, unsigned short, const char*>()},
|
||||
{"OnActorEquipment", Function<void, unsigned short, const char*>()},
|
||||
{"OnActorAI", Function<void, unsigned short, const char*>()},
|
||||
{"OnActorDeath", Function<void, unsigned short, const char*>()},
|
||||
{"OnActorCellChange", Function<void, unsigned short, const char*>()},
|
||||
{"OnActorTest", Function<void, unsigned short, const char*>()},
|
||||
{"OnPlayerSendMessage", Function<bool, unsigned short, const char*>()},
|
||||
{"OnPlayerEndCharGen", Function<void, unsigned short>()},
|
||||
{"OnGUIAction", Function<void, unsigned short, int, const char*>()},
|
||||
{"OnWorldKillCount", Function<void, unsigned short>()},
|
||||
{"OnWorldMap", Function<void, unsigned short>()},
|
||||
{"OnWorldWeather", Function<void, unsigned short>() },
|
||||
{"OnMpNumIncrement", Function<void, int>()},
|
||||
{"OnRequestPluginList", Function<const char *, unsigned int, unsigned int>()}
|
||||
{"OnServerInit", Callback<>()},
|
||||
{"OnServerPostInit", Callback<>()},
|
||||
{"OnServerExit", Callback<bool>()},
|
||||
{"OnServerScriptCrash", Callback<const char*>()},
|
||||
{"OnPlayerConnect", Callback<unsigned short>()},
|
||||
{"OnPlayerDisconnect", Callback<unsigned short>()},
|
||||
{"OnPlayerDeath", Callback<unsigned short>()},
|
||||
{"OnPlayerResurrect", Callback<unsigned short>()},
|
||||
{"OnPlayerCellChange", Callback<unsigned short>()},
|
||||
{"OnPlayerAttribute", Callback<unsigned short>()},
|
||||
{"OnPlayerSkill", Callback<unsigned short>()},
|
||||
{"OnPlayerLevel", Callback<unsigned short>()},
|
||||
{"OnPlayerBounty", Callback<unsigned short>()},
|
||||
{"OnPlayerReputation", Callback<unsigned short>()},
|
||||
{"OnPlayerEquipment", Callback<unsigned short>()},
|
||||
{"OnPlayerInventory", Callback<unsigned short>()},
|
||||
{"OnPlayerJournal", Callback<unsigned short>()},
|
||||
{"OnPlayerFaction", Callback<unsigned short>()},
|
||||
{"OnPlayerShapeshift", Callback<unsigned short>()},
|
||||
{"OnPlayerSpellbook", Callback<unsigned short>()},
|
||||
{"OnPlayerQuickKeys", Callback<unsigned short>()},
|
||||
{"OnPlayerTopic", Callback<unsigned short>()},
|
||||
{"OnPlayerDisposition", Callback<unsigned short>()},
|
||||
{"OnPlayerBook", Callback<unsigned short>()},
|
||||
{"OnPlayerItemUse", Callback<unsigned short>()},
|
||||
{"OnPlayerMiscellaneous", Callback<unsigned short>()},
|
||||
{"OnPlayerInput", Callback<unsigned short>()},
|
||||
{"OnPlayerRest", Callback<unsigned short>()},
|
||||
{"OnRecordDynamic", Callback<unsigned short>()},
|
||||
{"OnCellLoad", Callback<unsigned short, const char*>()},
|
||||
{"OnCellUnload", Callback<unsigned short, const char*>()},
|
||||
{"OnCellDeletion", Callback<const char*>()},
|
||||
{"OnContainer", Callback<unsigned short, const char*>()},
|
||||
{"OnDoorState", Callback<unsigned short, const char*>()},
|
||||
{"OnObjectActivate", Callback<unsigned short, const char*>()},
|
||||
{"OnObjectPlace", Callback<unsigned short, const char*>()},
|
||||
{"OnObjectState", Callback<unsigned short, const char*>()},
|
||||
{"OnObjectSpawn", Callback<unsigned short, const char*>()},
|
||||
{"OnObjectDelete", Callback<unsigned short, const char*>()},
|
||||
{"OnObjectLock", Callback<unsigned short, const char*>()},
|
||||
{"OnObjectScale", Callback<unsigned short, const char*>()},
|
||||
{"OnObjectTrap", Callback<unsigned short, const char*>()},
|
||||
{"OnVideoPlay", Callback<unsigned short, const char*>()},
|
||||
{"OnActorList", Callback<unsigned short, const char*>()},
|
||||
{"OnActorEquipment", Callback<unsigned short, const char*>()},
|
||||
{"OnActorAI", Callback<unsigned short, const char*>()},
|
||||
{"OnActorDeath", Callback<unsigned short, const char*>()},
|
||||
{"OnActorCellChange", Callback<unsigned short, const char*>()},
|
||||
{"OnActorTest", Callback<unsigned short, const char*>()},
|
||||
{"OnPlayerSendMessage", Callback<unsigned short, const char*>()},
|
||||
{"OnPlayerEndCharGen", Callback<unsigned short>()},
|
||||
{"OnGUIAction", Callback<unsigned short, int, const char*>()},
|
||||
{"OnWorldKillCount", Callback<unsigned short>()},
|
||||
{"OnWorldMap", Callback<unsigned short>()},
|
||||
{"OnWorldWeather", Callback<unsigned short>() },
|
||||
{"OnMpNumIncrement", Callback<int>()},
|
||||
{"OnRequestDataFileList", Callback<>()}
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -82,14 +82,34 @@ struct ScriptIdentity
|
|||
constexpr ScriptIdentity(Function<R, Types...>) : types(TypeString<Types...>::value), ret(TypeChar<R, sizeof_void<R>::value>::value), numargs(sizeof(TypeString<Types...>::value) - 1) {}
|
||||
};
|
||||
|
||||
template<typename... Types>
|
||||
using Callback = void (*)(Types...);
|
||||
|
||||
struct CallbackIdentity
|
||||
{
|
||||
const char* types;
|
||||
const unsigned int numargs;
|
||||
|
||||
constexpr bool matches(const char* types, const unsigned int N = 0) const
|
||||
{
|
||||
return N < numargs ? this->types[N] == types[N] && matches(types, N + 1) : this->types[N] == types[N];
|
||||
}
|
||||
|
||||
template<typename... Types>
|
||||
constexpr CallbackIdentity(Callback<Types...>) : types(TypeString<Types...>::value), numargs(sizeof(TypeString<Types...>::value) - 1) {}
|
||||
};
|
||||
|
||||
|
||||
struct ScriptFunctionPointer : public ScriptIdentity
|
||||
{
|
||||
Function<void> addr;
|
||||
|
||||
void *addr;
|
||||
#if (!defined(__clang__) && defined(__GNUC__))
|
||||
template<typename R, typename... Types>
|
||||
constexpr ScriptFunctionPointer(Function<R, Types...> addr) : ScriptIdentity(addr), addr(reinterpret_cast<Function<void>>(addr)) {}
|
||||
constexpr ScriptFunctionPointer(Function<R, Types...> addr) : ScriptIdentity(addr), addr((void*)(addr)) {}
|
||||
#else
|
||||
template<typename R, typename... Types>
|
||||
constexpr ScriptFunctionPointer(Function<R, Types...> addr) : ScriptIdentity(addr), addr(addr) {}
|
||||
#endif
|
||||
};
|
||||
|
||||
struct ScriptFunctionData
|
||||
|
@ -104,10 +124,10 @@ struct ScriptCallbackData
|
|||
{
|
||||
const char* name;
|
||||
const unsigned int index;
|
||||
const ScriptIdentity callback;
|
||||
const CallbackIdentity callback;
|
||||
|
||||
template<size_t N>
|
||||
constexpr ScriptCallbackData(const char(&name)[N], ScriptIdentity _callback) : name(name), index(Utils::hash(name)), callback(_callback) {}
|
||||
constexpr ScriptCallbackData(const char(&name)[N], CallbackIdentity _callback) : name(name), index(Utils::hash(name)), callback(_callback) {}
|
||||
};
|
||||
|
||||
#endif //TMPTYPES_HPP
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
//
|
||||
// Created by koncord on 04.03.17.
|
||||
//
|
||||
|
||||
#include "Utils.hpp"
|
||||
|
||||
#include <cstdarg>
|
||||
|
||||
using namespace std;
|
||||
|
||||
const vector<string> Utils::split(const string &str, int delimiter)
|
||||
|
@ -52,3 +50,59 @@ ESM::Cell Utils::getCellFromDescription(std::string cellDescription)
|
|||
|
||||
return cell;
|
||||
}
|
||||
|
||||
void Utils::getArguments(std::vector<boost::any> ¶ms, va_list args, const std::string &def)
|
||||
{
|
||||
params.reserve(def.length());
|
||||
|
||||
try
|
||||
{
|
||||
for (char c : def)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'i':
|
||||
params.emplace_back(va_arg(args, unsigned int));
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
params.emplace_back(va_arg(args, signed int));
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
params.emplace_back(va_arg(args, unsigned long long));
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
params.emplace_back(va_arg(args, signed long long));
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
params.emplace_back(va_arg(args, double));
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
params.emplace_back(va_arg(args, void*));
|
||||
break;
|
||||
|
||||
case 's':
|
||||
params.emplace_back(va_arg(args, const char*));
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
params.emplace_back(va_arg(args, int));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw runtime_error("C++ call: Unknown argument identifier " + c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
catch (...)
|
||||
{
|
||||
va_end(args);
|
||||
throw;
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
//
|
||||
// Created by koncord on 04.03.17.
|
||||
//
|
||||
|
||||
#ifndef OPENMW_UTILS_HPP
|
||||
#define OPENMW_UTILS_HPP
|
||||
|
||||
|
@ -9,6 +5,8 @@
|
|||
#include <regex>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/any.hpp>
|
||||
|
||||
#include <components/esm/loadcell.hpp>
|
||||
|
||||
#include <components/openmw-mp/Utils.hpp>
|
||||
|
@ -27,6 +25,8 @@ namespace Utils
|
|||
|
||||
ESM::Cell getCellFromDescription(std::string cellDescription);
|
||||
|
||||
void getArguments(std::vector<boost::any> ¶ms, va_list args, const std::string &def);
|
||||
|
||||
template<size_t N>
|
||||
constexpr unsigned int hash(const char(&str)[N], size_t I = N)
|
||||
{
|
||||
|
|
|
@ -190,48 +190,29 @@ int main(int argc, char *argv[])
|
|||
LOG_INIT(logLevel);
|
||||
|
||||
int players = mgr.getInt("maximumPlayers", "General");
|
||||
string addr = mgr.getString("localAddress", "General");
|
||||
string address = mgr.getString("localAddress", "General");
|
||||
int port = mgr.getInt("port", "General");
|
||||
|
||||
string passw = mgr.getString("password", "General");
|
||||
string password = mgr.getString("password", "General");
|
||||
|
||||
string plugin_home = mgr.getString("home", "Plugins");
|
||||
string moddir = Utils::convertPath(plugin_home + "/data");
|
||||
string pluginHome = mgr.getString("home", "Plugins");
|
||||
string dataDirectory = Utils::convertPath(pluginHome + "/data");
|
||||
|
||||
vector<string> plugins (Utils::split(mgr.getString("plugins", "Plugins"), ','));
|
||||
vector<string> plugins(Utils::split(mgr.getString("plugins", "Plugins"), ','));
|
||||
|
||||
Utils::printVersion("TES3MP dedicated server", TES3MP_VERSION, version.mCommitHash, TES3MP_PROTO_VERSION);
|
||||
|
||||
// Check for unmodified tes3mp-credits file; this makes it so people can't repackage official releases with
|
||||
// their own made-up credits, though it obviously has no bearing on unofficial releases that change
|
||||
// the checksum below
|
||||
boost::filesystem::path folderPath(boost::filesystem::initial_path<boost::filesystem::path>());
|
||||
folderPath = boost::filesystem::system_complete(boost::filesystem::path(argv[0])).remove_filename();
|
||||
std::string creditsPath = folderPath.string() + "/tes3mp-credits";
|
||||
Script::SetModDir(dataDirectory);
|
||||
|
||||
unsigned int expectedChecksumInt = Utils::hexStrToInt(TES3MP_CREDITS_CHECKSUM);
|
||||
bool hasValidCredits = Utils::doesFileHaveChecksum(creditsPath + ".md", expectedChecksumInt);
|
||||
|
||||
if (!hasValidCredits)
|
||||
hasValidCredits = Utils::doesFileHaveChecksum(creditsPath + ".txt", expectedChecksumInt);
|
||||
|
||||
if (!hasValidCredits)
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_FATAL, "The server is shutting down");
|
||||
LOG_APPEND(Log::LOG_FATAL, "- %s", TES3MP_CREDITS_ERROR);
|
||||
return 1;
|
||||
}
|
||||
|
||||
setenv("MOD_DIR", moddir.c_str(), 1); // hack for lua
|
||||
|
||||
setenv("LUA_PATH", Utils::convertPath(plugin_home + "/scripts/?.lua" + ";"
|
||||
+ plugin_home + "/scripts/?.t" + ";"
|
||||
+ plugin_home + "/lib/lua/?.lua" + ";"
|
||||
+ plugin_home + "/lib/lua/?.t").c_str(), 1);
|
||||
#ifdef ENABLE_LUA
|
||||
LangLua::AddPackagePath(Utils::convertPath(pluginHome + "/scripts/?.lua" + ";"
|
||||
+ pluginHome + "/lib/lua/?.lua" + ";"));
|
||||
#ifdef _WIN32
|
||||
setenv("LUA_CPATH", Utils::convertPath(plugin_home + "/lib/?.dll").c_str(), 1);
|
||||
LangLua::AddPackageCPath(Utils::convertPath(pluginHome + "/lib/?.dll"));
|
||||
#else
|
||||
setenv("LUA_CPATH", Utils::convertPath(plugin_home + "/lib/?.so").c_str(), 1);
|
||||
LangLua::AddPackageCPath(Utils::convertPath(pluginHome + "/lib/?.so"));
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
int code;
|
||||
|
@ -245,18 +226,18 @@ int main(int argc, char *argv[])
|
|||
|
||||
peer->SetIncomingPassword(sstr.str().c_str(), (int) sstr.str().size());
|
||||
|
||||
if (RakNet::NonNumericHostString(addr.c_str()))
|
||||
if (RakNet::NonNumericHostString(address.c_str()))
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "You cannot use non-numeric addresses for the server.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
RakNet::SocketDescriptor sd((unsigned short) port, addr.c_str());
|
||||
RakNet::SocketDescriptor sd((unsigned short) port, address.c_str());
|
||||
|
||||
try
|
||||
{
|
||||
for (auto plugin : plugins)
|
||||
Script::LoadScript(plugin.c_str(), plugin_home.c_str());
|
||||
Script::LoadScript(plugin.c_str(), pluginHome.c_str());
|
||||
|
||||
switch (peer->Startup((unsigned) players, &sd, 1))
|
||||
{
|
||||
|
@ -271,7 +252,7 @@ int main(int argc, char *argv[])
|
|||
case RakNet::SOCKET_FAILED_TO_BIND:
|
||||
case RakNet::SOCKET_PORT_ALREADY_IN_USE:
|
||||
case RakNet::PORT_CANNOT_BE_ZERO:
|
||||
throw runtime_error("Failed to bind port");
|
||||
throw runtime_error("Failed to bind port. Make sure a server isn't already running on that port.");
|
||||
case RakNet::SOCKET_FAILED_TEST_SEND:
|
||||
case RakNet::SOCKET_FAMILY_NOT_SUPPORTED:
|
||||
case RakNet::FAILED_TO_CREATE_NETWORK_THREAD:
|
||||
|
@ -283,7 +264,7 @@ int main(int argc, char *argv[])
|
|||
peer->SetMaximumIncomingConnections((unsigned short) (players));
|
||||
|
||||
Networking networking(peer);
|
||||
networking.setServerPassword(passw);
|
||||
networking.setServerPassword(password);
|
||||
|
||||
if (mgr.getBool("enabled", "MasterServer"))
|
||||
{
|
||||
|
@ -326,6 +307,7 @@ int main(int argc, char *argv[])
|
|||
catch (std::exception &e)
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, e.what());
|
||||
Script::Call<Script::CallbackIdentity("OnServerScriptCrash")>(e.what());
|
||||
throw; //fall through
|
||||
}
|
||||
|
||||
|
|
|
@ -21,16 +21,7 @@ namespace mwmp
|
|||
{
|
||||
DEBUG_PRINTF(strPacketID.c_str());
|
||||
|
||||
Script::CallBackReturn<Script::CallbackIdentity("OnPlayerSendMessage")> result = true;
|
||||
Script::Call<Script::CallbackIdentity("OnPlayerSendMessage")>(result, player.getId(), player.chatMessage.c_str());
|
||||
|
||||
if (result)
|
||||
{
|
||||
player.chatMessage = player.npc.mName + " (" + std::to_string(player.getId()) + "): "
|
||||
+ player.chatMessage + "\n";
|
||||
packet.Send(false);
|
||||
packet.Send(true);
|
||||
}
|
||||
Script::Call<Script::CallbackIdentity("OnPlayerSendMessage")>(player.getId(), player.chatMessage.c_str());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
//
|
||||
// Created by koncord on 31.03.17.
|
||||
//
|
||||
|
||||
#ifndef OPENMW_PROCESSORPLAYERPOSITION_HPP
|
||||
#define OPENMW_PROCESSORPLAYERPOSITION_HPP
|
||||
|
||||
|
@ -18,13 +14,9 @@ namespace mwmp
|
|||
}
|
||||
|
||||
void Do(PlayerPacket &packet, Player &player) override
|
||||
{
|
||||
//DEBUG_PRINTF(strPacketID);
|
||||
if (!player.creatureStats.mDead)
|
||||
{
|
||||
player.sendToLoaded(&packet);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -17,8 +17,6 @@ namespace mwmp
|
|||
{
|
||||
DEBUG_PRINTF(strPacketID.c_str());
|
||||
|
||||
packet.Send(true);
|
||||
|
||||
Script::Call<Script::CallbackIdentity("OnWorldKillCount")>(player.getId());
|
||||
}
|
||||
};
|
||||
|
|
|
@ -134,7 +134,7 @@ add_openmw_dir (mwmp/processors/object BaseObjectProcessor
|
|||
ProcessorScriptGlobalFloat
|
||||
)
|
||||
|
||||
add_openmw_dir (mwmp/processors/worldstate ProcessorCellCreate ProcessorCellReplace ProcessorRecordDynamic
|
||||
add_openmw_dir (mwmp/processors/worldstate ProcessorCellCreate ProcessorCellReset ProcessorRecordDynamic
|
||||
ProcessorWorldCollisionOverride ProcessorWorldMap ProcessorWorldRegionAuthority ProcessorWorldTime
|
||||
ProcessorWorldWeather
|
||||
)
|
||||
|
|
|
@ -221,13 +221,13 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
|
|||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
Check for unmodified tes3mp-credits file; this makes it so people can't repackage official releases with
|
||||
their own made-up credits, though it obviously has no bearing on unofficial releases that change
|
||||
the checksum below
|
||||
Check for unmodified tes3mp-credits file on Windows; this makes it so people can't repackage official
|
||||
releases with their own made-up credits, though it obviously has no bearing on unofficial releases that
|
||||
change the checksum below
|
||||
*/
|
||||
boost::filesystem::path folderPath(boost::filesystem::initial_path<boost::filesystem::path>());
|
||||
folderPath = boost::filesystem::system_complete(boost::filesystem::path(argv[0])).remove_filename();
|
||||
std::string creditsPath = folderPath.string() + "/tes3mp-credits";
|
||||
#ifdef _WIN32
|
||||
|
||||
std::string creditsPath = (cfgMgr.getLocalPath() / "tes3mp-credits").string();
|
||||
|
||||
unsigned int expectedChecksumInt = Utils::hexStrToInt(TES3MP_CREDITS_CHECKSUM);
|
||||
bool hasValidCredits = Utils::doesFileHaveChecksum(creditsPath + ".md", expectedChecksumInt);
|
||||
|
@ -242,6 +242,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
|
|||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "tes3mp", TES3MP_CREDITS_ERROR, 0);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
|
|
@ -553,9 +553,10 @@ namespace MWClass
|
|||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
If the attacker was the LocalPlayer or LocalActor, record their target and send a packet with it
|
||||
If the attacker was the LocalPlayer or LocalActor, record their target and send a
|
||||
packet with it
|
||||
|
||||
If the victim was a LocalActor who died, record their attacker as the deathReason
|
||||
If the victim was a LocalActor who died, record their attacker as the killer
|
||||
*/
|
||||
mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(attacker);
|
||||
|
||||
|
|
|
@ -966,12 +966,13 @@ namespace MWClass
|
|||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
If the attacker was the LocalPlayer or LocalActor, record their target and send a packet with it
|
||||
If the attacker was the LocalPlayer or LocalActor, record their target and send a
|
||||
packet with it
|
||||
|
||||
If the victim was the LocalPlayer, check whether packets should be sent about
|
||||
their new dynamic stats and position
|
||||
|
||||
If the victim was a LocalActor who died, record their attacker as the deathReason
|
||||
If the victim was a LocalActor who died, record their attacker as the killer
|
||||
*/
|
||||
mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(attacker);
|
||||
|
||||
|
|
|
@ -197,12 +197,13 @@ namespace MWGui
|
|||
/*
|
||||
Start of tes3mp change (major)
|
||||
|
||||
Avoid running any of the original code for dropping items, to prevent possibilities
|
||||
for item duping or interaction with restricted containers
|
||||
For valid drops, avoid running the original code for the item transfer, to prevent unilateral
|
||||
item duping or interaction on this client
|
||||
|
||||
Instead, finish the drag in a way that removes the items in it
|
||||
Instead, finish the drag in a way that removes the items in it, and let the server's reply handle
|
||||
the rest
|
||||
*/
|
||||
//if (success)
|
||||
if (success)
|
||||
// mDragAndDrop->drop(mModel, mItemView);
|
||||
mDragAndDrop->finish(true);
|
||||
/*
|
||||
|
|
|
@ -306,7 +306,26 @@ namespace MWGui
|
|||
{
|
||||
// pick up object
|
||||
if (!object.isEmpty())
|
||||
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(object);
|
||||
/*
|
||||
Start of tes3mp change (major)
|
||||
|
||||
Disable unilateral picking up of objects on this client
|
||||
|
||||
Instead, send an ID_OBJECT_ACTIVATE packet every time an item is made to pick up
|
||||
an item here, and expect the server's reply to our packet to cause the actual
|
||||
picking up of items
|
||||
*/
|
||||
//MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(object);
|
||||
{
|
||||
mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();
|
||||
objectList->reset();
|
||||
objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;
|
||||
objectList->addObjectActivate(object, MWMechanics::getPlayer());
|
||||
objectList->sendObjectActivate();
|
||||
}
|
||||
/*
|
||||
End of tes3mp change (major)
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -718,21 +718,6 @@ namespace MWGui
|
|||
// can't use ActionTake here because we need an MWWorld::Ptr to the newly inserted object
|
||||
MWWorld::Ptr newObject = *player.getClass().getContainerStore (player).add (object, object.getRefData().getCount(), player);
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
Send an ID_OBJECT_DELETE packet every time an item from the world is picked up
|
||||
by the player through the inventory HUD
|
||||
*/
|
||||
mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();
|
||||
objectList->reset();
|
||||
objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;
|
||||
objectList->addObjectDelete(object);
|
||||
objectList->sendObjectDelete();
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
// remove from world
|
||||
MWBase::Environment::get().getWorld()->deleteObject (object);
|
||||
|
||||
|
|
|
@ -164,10 +164,10 @@ namespace MWGui
|
|||
Start of tes3mp addition
|
||||
|
||||
Send a PLAYER_QUICKKEYS packet whenever a key is unassigned, but only if the player
|
||||
has finished character generation, so as to avoid doing anything doing startup when all
|
||||
is logged in on the server, so as to avoid doing anything doing at startup when all
|
||||
quick keys get unassigned by default
|
||||
*/
|
||||
if (mwmp::Main::get().getLocalPlayer()->hasFinishedCharGen() && !mwmp::Main::get().getLocalPlayer()->isReceivingQuickKeys)
|
||||
if (mwmp::Main::get().getLocalPlayer()->isLoggedIn() && !mwmp::Main::get().getLocalPlayer()->isReceivingQuickKeys)
|
||||
{
|
||||
mwmp::Main::get().getLocalPlayer()->sendQuickKey(key->index, Type_Unassigned);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,19 @@
|
|||
|
||||
#include <components/misc/rng.hpp>
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
Include additional headers for multiplayer purposes
|
||||
*/
|
||||
#include "../mwmp/Main.hpp"
|
||||
#include "../mwmp/Networking.hpp"
|
||||
#include "../mwmp/LocalPlayer.hpp"
|
||||
#include "../mwmp/MechanicsHelper.hpp"
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
@ -163,9 +176,26 @@ void Recharge::onItemClicked(MyGUI::Widget *sender, const MWWorld::Ptr& item)
|
|||
|
||||
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(
|
||||
item.getClass().getEnchantment(item));
|
||||
|
||||
/*
|
||||
Start of tes3mp change (minor)
|
||||
|
||||
Send PlayerInventory packets that replace the original item with the new one
|
||||
*/
|
||||
mwmp::LocalPlayer *localPlayer = mwmp::Main::get().getLocalPlayer();
|
||||
mwmp::Item removedItem = MechanicsHelper::getItem(item, 1);
|
||||
|
||||
item.getCellRef().setEnchantmentCharge(
|
||||
std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast<float>(enchantment->mData.mCharge)));
|
||||
|
||||
mwmp::Item addedItem = MechanicsHelper::getItem(item, 1);
|
||||
|
||||
localPlayer->sendItemChange(addedItem, mwmp::InventoryChanges::ADD);
|
||||
localPlayer->sendItemChange(removedItem, mwmp::InventoryChanges::REMOVE);
|
||||
/*
|
||||
End of tes3mp change (minor)
|
||||
*/
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->playSound("Enchant Success");
|
||||
|
||||
player.getClass().getContainerStore(player).restack(item);
|
||||
|
|
|
@ -1575,7 +1575,7 @@ namespace MWGui
|
|||
switch (quickKeyType)
|
||||
{
|
||||
case QuickKeysMenu::Type_Unassigned:
|
||||
mQuickKeysMenu->unassignIndex(slot);
|
||||
mQuickKeysMenu->unassignIndex(slot - 1);
|
||||
break;
|
||||
case QuickKeysMenu::Type_Item:
|
||||
mQuickKeysMenu->onAssignItem(item);
|
||||
|
|
|
@ -1070,9 +1070,9 @@ namespace MWInput
|
|||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
Ignore attempts to rest if the player has not finished character generation yet
|
||||
Ignore attempts to rest if the player has not logged in on the server yet
|
||||
*/
|
||||
if (!mwmp::Main::get().getLocalPlayer()->hasFinishedCharGen())
|
||||
if (!mwmp::Main::get().getLocalPlayer()->isLoggedIn())
|
||||
return;
|
||||
/*
|
||||
End of tes3mp addition
|
||||
|
@ -1137,6 +1137,17 @@ namespace MWInput
|
|||
if (MyGUI::InputManager::getInstance ().isModalAny())
|
||||
return;
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
Ignore attempts to open inventory if the player has not logged in on the server yet
|
||||
*/
|
||||
if (!mwmp::Main::get().getLocalPlayer()->isLoggedIn())
|
||||
return;
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
// Toggle between game mode and inventory mode
|
||||
if(!MWBase::Environment::get().getWindowManager()->isGuiMode())
|
||||
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Inventory);
|
||||
|
|
|
@ -324,16 +324,20 @@ namespace MWMechanics
|
|||
|
||||
Make it easy to get an effect's duration
|
||||
*/
|
||||
float ActiveSpells::getEffectDuration(short effectId)
|
||||
float ActiveSpells::getEffectDuration(short effectId, std::string sourceId)
|
||||
{
|
||||
for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it)
|
||||
{
|
||||
for (std::vector<ActiveEffect>::iterator effectIt = it->second.mEffects.begin();
|
||||
effectIt != it->second.mEffects.end();)
|
||||
if (sourceId.compare(it->first) == 0)
|
||||
{
|
||||
for (std::vector<ActiveEffect>::iterator effectIt = it->second.mEffects.begin();
|
||||
effectIt != it->second.mEffects.end(); ++effectIt)
|
||||
{
|
||||
if (effectIt->mEffectId == effectId)
|
||||
return effectIt->mDuration;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0.f;
|
||||
}
|
||||
/*
|
||||
|
|
|
@ -109,7 +109,7 @@ namespace MWMechanics
|
|||
|
||||
Make it easy to get an effect's duration
|
||||
*/
|
||||
float getEffectDuration(short effectId);
|
||||
float getEffectDuration(short effectId, std::string sourceId);
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
|
|
@ -240,8 +240,22 @@ namespace MWMechanics
|
|||
|
||||
// Set the soul on just one of the gems, not the whole stack
|
||||
gem->getContainerStore()->unstack(*gem, caster);
|
||||
|
||||
/*
|
||||
Start of tes3mp change (minor)
|
||||
|
||||
Send PlayerInventory packets that replace the original gem with the new one
|
||||
*/
|
||||
mwmp::LocalPlayer *localPlayer = mwmp::Main::get().getLocalPlayer();
|
||||
localPlayer->sendItemChange(*gem, 1, mwmp::InventoryChanges::REMOVE);
|
||||
|
||||
gem->getCellRef().setSoul(mCreature.getCellRef().getRefId());
|
||||
|
||||
localPlayer->sendItemChange(*gem, 1, mwmp::InventoryChanges::ADD);
|
||||
/*
|
||||
End of tes3mp change (minor)
|
||||
*/
|
||||
|
||||
// Restack the gem with other gems with the same soul
|
||||
gem->getContainerStore()->restack(*gem);
|
||||
|
||||
|
@ -828,6 +842,25 @@ namespace MWMechanics
|
|||
|
||||
if (isDamageEffect)
|
||||
{
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
If the victim was the LocalPlayer or a LocalActor, record the caster as their killer
|
||||
*/
|
||||
mwmp::Target killer = MechanicsHelper::getTarget(caster);
|
||||
|
||||
if (ptr == MWMechanics::getPlayer())
|
||||
{
|
||||
mwmp::Main::get().getLocalPlayer()->killer = killer;
|
||||
}
|
||||
else if (mwmp::Main::get().getCellController()->isLocalActor(ptr))
|
||||
{
|
||||
mwmp::Main::get().getCellController()->getLocalActor(ptr)->killer = killer;
|
||||
}
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
if (caster == player || playerFollowers.find(caster) != playerFollowers.end())
|
||||
{
|
||||
if (caster.getClass().getNpcStats(caster).isWerewolf())
|
||||
|
|
|
@ -578,7 +578,7 @@ namespace MWMechanics
|
|||
if (localAttack && localAttack->pressed != true)
|
||||
{
|
||||
MechanicsHelper::resetAttack(localAttack);
|
||||
localAttack->type = distantCombat ? mwmp::Attack::MELEE : mwmp::Attack::RANGED;
|
||||
localAttack->type = distantCombat ? mwmp::Attack::RANGED : mwmp::Attack::MELEE;
|
||||
localAttack->pressed = true;
|
||||
localAttack->shouldSend = true;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,19 @@
|
|||
|
||||
#include <components/misc/rng.hpp>
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
Include additional headers for multiplayer purposes
|
||||
*/
|
||||
#include "../mwmp/Main.hpp"
|
||||
#include "../mwmp/Networking.hpp"
|
||||
#include "../mwmp/LocalPlayer.hpp"
|
||||
#include "../mwmp/MechanicsHelper.hpp"
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
|
@ -57,8 +70,25 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair)
|
|||
// repair by 'y' points
|
||||
int charge = itemToRepair.getClass().getItemHealth(itemToRepair);
|
||||
charge = std::min(charge + y, itemToRepair.getClass().getItemMaxHealth(itemToRepair));
|
||||
|
||||
/*
|
||||
Start of tes3mp change (minor)
|
||||
|
||||
Send PlayerInventory packets that replace the original item with the new one
|
||||
*/
|
||||
mwmp::LocalPlayer *localPlayer = mwmp::Main::get().getLocalPlayer();
|
||||
mwmp::Item removedItem = MechanicsHelper::getItem(itemToRepair, 1);
|
||||
|
||||
itemToRepair.getCellRef().setCharge(charge);
|
||||
|
||||
mwmp::Item addedItem = MechanicsHelper::getItem(itemToRepair, 1);
|
||||
|
||||
localPlayer->sendItemChange(addedItem, mwmp::InventoryChanges::ADD);
|
||||
localPlayer->sendItemChange(removedItem, mwmp::InventoryChanges::REMOVE);
|
||||
/*
|
||||
End of tes3mp change (minor)
|
||||
*/
|
||||
|
||||
// attempt to re-stack item, in case it was fully repaired
|
||||
MWWorld::ContainerStoreIterator stacked = player.getClass().getContainerStore(player).restack(itemToRepair);
|
||||
|
||||
|
|
|
@ -537,7 +537,7 @@ namespace MWMechanics
|
|||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
If the victim was a LocalPlayer or LocalActor who died, record their attacker as the deathReason
|
||||
If the victim was a LocalPlayer or LocalActor who died, record the caster as the killer
|
||||
*/
|
||||
if (!wasDead && isDead)
|
||||
{
|
||||
|
|
|
@ -111,8 +111,8 @@ namespace MWMechanics
|
|||
objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;
|
||||
|
||||
MWMechanics::CreatureStats *actorCreatureStats = &mActor.getClass().getCreatureStats(mActor);
|
||||
|
||||
objectList->addObjectSpawn(placed, mActor, actorCreatureStats->getActiveSpells().getEffectDuration(it->first));
|
||||
float duration = actorCreatureStats->getActiveSpells().getEffectDuration(it->first, it->second);
|
||||
objectList->addObjectSpawn(placed, mActor, duration);
|
||||
objectList->sendObjectSpawn();
|
||||
}
|
||||
|
||||
|
|
|
@ -82,8 +82,10 @@ void Cell::updateLocal(bool forceUpdate)
|
|||
}
|
||||
else
|
||||
{
|
||||
// Forcibly update this local actor if its data has never been sent before;
|
||||
// otherwise, use the current forceUpdate value
|
||||
if (actor->getPtr().getRefData().isEnabled())
|
||||
actor->update(forceUpdate);
|
||||
actor->update(actor->hasSentData ? forceUpdate : true);
|
||||
|
||||
++it;
|
||||
}
|
||||
|
@ -299,19 +301,23 @@ void Cell::readAttack(ActorList& actorList)
|
|||
|
||||
if (dedicatedActors.count(mapIndex) > 0)
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Reading ActorAttack about %s", mapIndex.c_str());
|
||||
|
||||
DedicatedActor *actor = dedicatedActors[mapIndex];
|
||||
actor->attack = baseActor.attack;
|
||||
|
||||
// Set the correct drawState here if we've somehow we've missed a previous
|
||||
// AnimFlags packet
|
||||
if (actor->drawState != 1 && (actor->attack.type == mwmp::Attack::MELEE || actor->attack.type == mwmp::Attack::RANGED))
|
||||
if (actor->drawState != MWMechanics::DrawState_::DrawState_Weapon &&
|
||||
(actor->attack.type == mwmp::Attack::MELEE || actor->attack.type == mwmp::Attack::RANGED))
|
||||
{
|
||||
actor->drawState = 1;
|
||||
actor->drawState = MWMechanics::DrawState_::DrawState_Weapon;
|
||||
actor->setAnimFlags();
|
||||
}
|
||||
else if (actor->drawState != 2 && (actor->attack.type == mwmp::Attack::MAGIC || actor->attack.type == mwmp::Attack::ITEM_MAGIC))
|
||||
else if (actor->drawState != MWMechanics::DrawState_::DrawState_Spell &&
|
||||
(actor->attack.type == mwmp::Attack::MAGIC || actor->attack.type == mwmp::Attack::ITEM_MAGIC))
|
||||
{
|
||||
actor->drawState = 2;
|
||||
actor->drawState = MWMechanics::DrawState_::DrawState_Spell;
|
||||
actor->setAnimFlags();
|
||||
}
|
||||
|
||||
|
@ -397,6 +403,9 @@ void Cell::readCellChange(ActorList& actorList)
|
|||
|
||||
void Cell::initializeLocalActor(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
std::string mapIndex = Main::get().getCellController()->generateMapIndex(ptr);
|
||||
LOG_APPEND(Log::LOG_VERBOSE, "- Initializing LocalActor %s in %s", mapIndex.c_str(), getDescription().c_str());
|
||||
|
||||
LocalActor *actor = new LocalActor();
|
||||
actor->cell = *store->getCell();
|
||||
actor->setPtr(ptr);
|
||||
|
@ -406,16 +415,17 @@ void Cell::initializeLocalActor(const MWWorld::Ptr& ptr)
|
|||
if (ptr.getClass().getCreatureStats(ptr).isDead())
|
||||
actor->wasDead = true;
|
||||
|
||||
std::string mapIndex = Main::get().getCellController()->generateMapIndex(ptr);
|
||||
localActors[mapIndex] = actor;
|
||||
|
||||
Main::get().getCellController()->setLocalActorRecord(mapIndex, getDescription());
|
||||
|
||||
LOG_APPEND(Log::LOG_VERBOSE, "- Initialized LocalActor %s in %s", mapIndex.c_str(), getDescription().c_str());
|
||||
LOG_APPEND(Log::LOG_VERBOSE, "- Successfully initialized LocalActor %s in %s", mapIndex.c_str(), getDescription().c_str());
|
||||
}
|
||||
|
||||
void Cell::initializeLocalActors()
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Initializing LocalActors in %s", getDescription().c_str());
|
||||
|
||||
for (const auto &mergedRef : store->getMergedRefs())
|
||||
{
|
||||
if (mergedRef->mClass->isActor())
|
||||
|
@ -432,20 +442,24 @@ void Cell::initializeLocalActors()
|
|||
initializeLocalActor(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_APPEND(Log::LOG_VERBOSE, "- Successfully initialized LocalActors in %s", getDescription().c_str());
|
||||
}
|
||||
|
||||
void Cell::initializeDedicatedActor(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
std::string mapIndex = Main::get().getCellController()->generateMapIndex(ptr);
|
||||
LOG_APPEND(Log::LOG_VERBOSE, "- Initializing DedicatedActor %s in %s", mapIndex.c_str(), getDescription().c_str());
|
||||
|
||||
DedicatedActor *actor = new DedicatedActor();
|
||||
actor->cell = *store->getCell();
|
||||
actor->setPtr(ptr);
|
||||
|
||||
std::string mapIndex = Main::get().getCellController()->generateMapIndex(ptr);
|
||||
dedicatedActors[mapIndex] = actor;
|
||||
|
||||
Main::get().getCellController()->setDedicatedActorRecord(mapIndex, getDescription());
|
||||
|
||||
LOG_APPEND(Log::LOG_VERBOSE, "- Initialized DedicatedActor %s in %s", mapIndex.c_str(), getDescription().c_str());
|
||||
LOG_APPEND(Log::LOG_VERBOSE, "- Successfully initialized DedicatedActor %s in %s", mapIndex.c_str(), getDescription().c_str());
|
||||
}
|
||||
|
||||
void Cell::initializeDedicatedActors(ActorList& actorList)
|
||||
|
|
|
@ -77,6 +77,8 @@ void CellController::initializeCell(const ESM::Cell& cell)
|
|||
// If this key doesn't exist, create it
|
||||
if (cellsInitialized.count(mapIndex) == 0)
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Initializing mwmp::Cell %s", cell.getDescription().c_str());
|
||||
|
||||
MWWorld::CellStore *cellStore = getCellStore(cell);
|
||||
|
||||
if (!cellStore) return;
|
||||
|
@ -84,7 +86,7 @@ void CellController::initializeCell(const ESM::Cell& cell)
|
|||
mwmp::Cell *mpCell = new mwmp::Cell(cellStore);
|
||||
cellsInitialized[mapIndex] = mpCell;
|
||||
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "- Initialized mwmp::Cell %s", mpCell->getDescription().c_str());
|
||||
LOG_APPEND(Log::LOG_VERBOSE, "- Successfully initialized mwmp::Cell %s", cell.getDescription().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ using namespace std;
|
|||
|
||||
DedicatedActor::DedicatedActor()
|
||||
{
|
||||
drawState = 0;
|
||||
drawState = MWMechanics::DrawState_::DrawState_Nothing;
|
||||
movementFlags = 0;
|
||||
animation.groupname = "";
|
||||
sound = "";
|
||||
|
@ -133,12 +133,7 @@ void DedicatedActor::setAnimFlags()
|
|||
|
||||
MWMechanics::CreatureStats *ptrCreatureStats = &ptr.getClass().getCreatureStats(ptr);
|
||||
|
||||
if (drawState == 0)
|
||||
ptrCreatureStats->setDrawState(DrawState_Nothing);
|
||||
else if (drawState == 1)
|
||||
ptrCreatureStats->setDrawState(DrawState_Weapon);
|
||||
else if (drawState == 2)
|
||||
ptrCreatureStats->setDrawState(DrawState_Spell);
|
||||
ptrCreatureStats->setDrawState(static_cast<MWMechanics::DrawState_>(drawState));
|
||||
|
||||
ptrCreatureStats->setMovementFlag(CreatureStats::Flag_Run, (movementFlags & CreatureStats::Flag_Run) != 0);
|
||||
ptrCreatureStats->setMovementFlag(CreatureStats::Flag_Sneak, (movementFlags & CreatureStats::Flag_Sneak) != 0);
|
||||
|
|
|
@ -71,6 +71,13 @@ DedicatedPlayer::~DedicatedPlayer()
|
|||
|
||||
void DedicatedPlayer::update(float dt)
|
||||
{
|
||||
// Only move and set anim flags if the framerate isn't too low
|
||||
if (dt < 0.1)
|
||||
{
|
||||
move(dt);
|
||||
setAnimFlags();
|
||||
}
|
||||
|
||||
MWMechanics::CreatureStats *ptrCreatureStats = &ptr.getClass().getCreatureStats(ptr);
|
||||
|
||||
MWMechanics::DynamicStat<float> value;
|
||||
|
@ -100,13 +107,6 @@ void DedicatedPlayer::update(float dt)
|
|||
ptrCreatureStats->setAiSetting(MWMechanics::CreatureStats::AI_Fight, 0);
|
||||
ptrCreatureStats->setAiSetting(MWMechanics::CreatureStats::AI_Flee, 0);
|
||||
ptrCreatureStats->setAiSetting(MWMechanics::CreatureStats::AI_Hello, 0);
|
||||
|
||||
// Only move and set anim flags if the framerate isn't too low
|
||||
if (dt < 0.1)
|
||||
{
|
||||
move(dt);
|
||||
setAnimFlags();
|
||||
}
|
||||
}
|
||||
|
||||
void DedicatedPlayer::move(float dt)
|
||||
|
@ -258,14 +258,10 @@ void DedicatedPlayer::setAnimFlags()
|
|||
cast.cast("Levitate");
|
||||
}
|
||||
|
||||
if (drawState == 0)
|
||||
ptr.getClass().getCreatureStats(ptr).setDrawState(DrawState_Nothing);
|
||||
else if (drawState == 1)
|
||||
ptr.getClass().getCreatureStats(ptr).setDrawState(DrawState_Weapon);
|
||||
else if (drawState == 2)
|
||||
ptr.getClass().getCreatureStats(ptr).setDrawState(DrawState_Spell);
|
||||
|
||||
MWMechanics::CreatureStats *ptrCreatureStats = &ptr.getClass().getCreatureStats(ptr);
|
||||
|
||||
ptrCreatureStats->setDrawState(static_cast<MWMechanics::DrawState_>(drawState));
|
||||
|
||||
ptrCreatureStats->setMovementFlag(CreatureStats::Flag_Run, (movementFlags & CreatureStats::Flag_Run) != 0);
|
||||
ptrCreatureStats->setMovementFlag(CreatureStats::Flag_Sneak, (movementFlags & CreatureStats::Flag_Sneak) != 0);
|
||||
ptrCreatureStats->setMovementFlag(CreatureStats::Flag_ForceJump, (movementFlags & CreatureStats::Flag_ForceJump) != 0);
|
||||
|
@ -388,7 +384,9 @@ void DedicatedPlayer::setCell()
|
|||
removeMarker();
|
||||
// Otherwise, update their marker so the player shows up in the right cell on the world map
|
||||
else
|
||||
updateMarker();
|
||||
{
|
||||
enableMarker();
|
||||
}
|
||||
|
||||
// If this player is now in a cell that we are the local authority over, we should send them all
|
||||
// NPC data in that cell
|
||||
|
@ -412,7 +410,9 @@ void DedicatedPlayer::setCell()
|
|||
void DedicatedPlayer::updateMarker()
|
||||
{
|
||||
if (!markerEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GUIController *gui = Main::get().getGUIController();
|
||||
|
||||
|
@ -423,7 +423,15 @@ void DedicatedPlayer::updateMarker()
|
|||
gui->mPlayerMarkers.addMarker(marker);
|
||||
}
|
||||
else
|
||||
{
|
||||
gui->mPlayerMarkers.addMarker(marker, true);
|
||||
}
|
||||
}
|
||||
|
||||
void DedicatedPlayer::enableMarker()
|
||||
{
|
||||
markerEnabled = true;
|
||||
updateMarker();
|
||||
}
|
||||
|
||||
void DedicatedPlayer::removeMarker()
|
||||
|
@ -435,18 +443,9 @@ void DedicatedPlayer::removeMarker()
|
|||
GUIController *gui = Main::get().getGUIController();
|
||||
|
||||
if (gui->mPlayerMarkers.contains(marker))
|
||||
Main::get().getGUIController()->mPlayerMarkers.deleteMarker(marker);
|
||||
}
|
||||
|
||||
void DedicatedPlayer::setMarkerState(bool state)
|
||||
{
|
||||
if (state)
|
||||
{
|
||||
markerEnabled = true;
|
||||
updateMarker();
|
||||
Main::get().getGUIController()->mPlayerMarkers.deleteMarker(marker);
|
||||
}
|
||||
else
|
||||
removeMarker();
|
||||
}
|
||||
|
||||
void DedicatedPlayer::playAnimation()
|
||||
|
@ -476,7 +475,7 @@ void DedicatedPlayer::createReference(const std::string& recId)
|
|||
|
||||
ESM::CustomMarker mEditingMarker = Main::get().getGUIController()->createMarker(guid);
|
||||
marker = mEditingMarker;
|
||||
setMarkerState(true);
|
||||
enableMarker();
|
||||
}
|
||||
|
||||
void DedicatedPlayer::deleteReference()
|
||||
|
|
|
@ -47,7 +47,7 @@ namespace mwmp
|
|||
|
||||
void updateMarker();
|
||||
void removeMarker();
|
||||
void setMarkerState(bool state);
|
||||
void enableMarker();
|
||||
|
||||
void playAnimation();
|
||||
void playSpeech();
|
||||
|
|
|
@ -162,9 +162,9 @@ namespace mwmp
|
|||
windowState++;
|
||||
if (windowState == 3) windowState = 0;
|
||||
|
||||
std::string chatMode = windowState == CHAT_DISABLED ? "Chat disabled" :
|
||||
windowState == CHAT_ENABLED ? "Chat enabled" :
|
||||
"Chat in hidden mode";
|
||||
std::string chatMode = windowState == CHAT_DISABLED ? "Chat hidden" :
|
||||
windowState == CHAT_ENABLED ? "Chat visible" :
|
||||
"Chat appearing when needed";
|
||||
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Switch chat mode to %s", chatMode.c_str());
|
||||
MWBase::Environment::get().getWindowManager()->messageBox(chatMode);
|
||||
|
|
|
@ -22,6 +22,7 @@ using namespace std;
|
|||
|
||||
LocalActor::LocalActor()
|
||||
{
|
||||
hasSentData = false;
|
||||
posWasChanged = false;
|
||||
equipmentChanged = false;
|
||||
|
||||
|
@ -61,14 +62,16 @@ void LocalActor::update(bool forceUpdate)
|
|||
updateSpeech();
|
||||
updateAttack();
|
||||
}
|
||||
|
||||
hasSentData = true;
|
||||
}
|
||||
|
||||
void LocalActor::updateCell()
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Sending ID_ACTOR_CELL_CHANGE about %s %i-%i to server",
|
||||
refId.c_str(), refNum, mpNum);
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Sending ID_ACTOR_CELL_CHANGE about %s %i-%i in cell %s to server",
|
||||
refId.c_str(), refNum, mpNum, cell.getDescription().c_str());
|
||||
|
||||
LOG_APPEND(Log::LOG_VERBOSE, "- Moved from %s to %s", cell.getDescription().c_str(), ptr.getCell()->getCell()->getDescription().c_str());
|
||||
LOG_APPEND(Log::LOG_VERBOSE, "- Moved to cell %s", ptr.getCell()->getCell()->getDescription().c_str());
|
||||
|
||||
cell = *ptr.getCell()->getCell();
|
||||
position = ptr.getRefData().getPosition();
|
||||
|
@ -191,8 +194,8 @@ void LocalActor::updateStatsDynamic(bool forceUpdate)
|
|||
if (MechanicsHelper::isEmptyTarget(killer))
|
||||
killer = MechanicsHelper::getTarget(ptr);
|
||||
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_ACTOR_DEATH about %s %i-%i to server",
|
||||
refId.c_str(), refNum, mpNum);
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_ACTOR_DEATH about %s %i-%i in cell %s to server",
|
||||
refId.c_str(), refNum, mpNum, cell.getDescription().c_str());
|
||||
|
||||
mwmp::Main::get().getNetworking()->getActorList()->addDeathActor(*this);
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ namespace mwmp
|
|||
MWWorld::Ptr getPtr();
|
||||
void setPtr(const MWWorld::Ptr& newPtr);
|
||||
|
||||
bool hasSentData;
|
||||
bool wasDead;
|
||||
|
||||
private:
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "LocalPlayer.hpp"
|
||||
#include "Main.hpp"
|
||||
#include "Networking.hpp"
|
||||
#include "PlayerList.hpp"
|
||||
#include "CellController.hpp"
|
||||
#include "GUIController.hpp"
|
||||
#include "MechanicsHelper.hpp"
|
||||
|
@ -46,6 +47,7 @@ using namespace std;
|
|||
LocalPlayer::LocalPlayer()
|
||||
{
|
||||
deathTime = time(0);
|
||||
receivedCharacter = false;
|
||||
|
||||
charGenState.currentStage = 0;
|
||||
charGenState.endStage = 1;
|
||||
|
@ -193,9 +195,12 @@ bool LocalPlayer::processCharGen()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool LocalPlayer::hasFinishedCharGen()
|
||||
bool LocalPlayer::isLoggedIn()
|
||||
{
|
||||
return charGenState.isFinished;
|
||||
if (charGenState.isFinished && (charGenState.endStage > 1 || receivedCharacter))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void LocalPlayer::updateStatsDynamic(bool forceUpdate)
|
||||
|
@ -221,16 +226,16 @@ void LocalPlayer::updateStatsDynamic(bool forceUpdate)
|
|||
|| abs(oldVal.getCurrent() - newVal.getCurrent()) >= limit);
|
||||
};
|
||||
|
||||
if (needUpdate(oldHealth, health, 2))
|
||||
if (forceUpdate || needUpdate(oldHealth, health, 2))
|
||||
statsDynamicIndexChanges.push_back(0);
|
||||
|
||||
if (needUpdate(oldMagicka, magicka, 4))
|
||||
if (forceUpdate || needUpdate(oldMagicka, magicka, 4))
|
||||
statsDynamicIndexChanges.push_back(1);
|
||||
|
||||
if (needUpdate(oldFatigue, fatigue, 4))
|
||||
if (forceUpdate || needUpdate(oldFatigue, fatigue, 4))
|
||||
statsDynamicIndexChanges.push_back(2);
|
||||
|
||||
if (statsDynamicIndexChanges.size() > 0 || forceUpdate)
|
||||
if (forceUpdate || statsDynamicIndexChanges.size() > 0)
|
||||
{
|
||||
oldHealth = health;
|
||||
oldMagicka = magicka;
|
||||
|
@ -280,6 +285,7 @@ void LocalPlayer::updateAttributes(bool forceUpdate)
|
|||
{
|
||||
if (ptrNpcStats.getAttribute(i).getBase() != creatureStats.mAttributes[i].mBase ||
|
||||
ptrNpcStats.getAttribute(i).getModifier() != creatureStats.mAttributes[i].mMod ||
|
||||
ptrNpcStats.getAttribute(i).getDamage() != creatureStats.mAttributes[i].mDamage ||
|
||||
ptrNpcStats.getSkillIncrease(i) != npcStats.mSkillIncrease[i] ||
|
||||
forceUpdate)
|
||||
{
|
||||
|
@ -314,6 +320,7 @@ void LocalPlayer::updateSkills(bool forceUpdate)
|
|||
// Update a skill if its base value has changed at all or its progress has changed enough
|
||||
if (ptrNpcStats.getSkill(i).getBase() != npcStats.mSkills[i].mBase ||
|
||||
ptrNpcStats.getSkill(i).getModifier() != npcStats.mSkills[i].mMod ||
|
||||
ptrNpcStats.getSkill(i).getDamage() != npcStats.mSkills[i].mDamage ||
|
||||
abs(ptrNpcStats.getSkill(i).getProgress() - npcStats.mSkills[i].mProgress) > 0.75 ||
|
||||
forceUpdate)
|
||||
{
|
||||
|
@ -457,6 +464,13 @@ void LocalPlayer::updateCell(bool forceUpdate)
|
|||
getNetworking()->getPlayerPacket(ID_PLAYER_CELL_CHANGE)->Send();
|
||||
|
||||
isChangingRegion = false;
|
||||
|
||||
// If this is an interior cell, are there any other players in it? If so,
|
||||
// enable their markers
|
||||
if (!ptrCell->isExterior())
|
||||
{
|
||||
mwmp::PlayerList::enableMarkers(*ptrCell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -623,12 +637,12 @@ void LocalPlayer::updateAnimFlags(bool forceUpdate)
|
|||
static bool wasJumping = false;
|
||||
static bool wasFlying = false;
|
||||
|
||||
MWMechanics::DrawState_ currentDrawState = ptrPlayer.getClass().getNpcStats(ptrPlayer).getDrawState();
|
||||
static MWMechanics::DrawState_ lastDrawState = ptrPlayer.getClass().getNpcStats(ptrPlayer).getDrawState();
|
||||
drawState = ptrPlayer.getClass().getNpcStats(ptrPlayer).getDrawState();
|
||||
static char lastDrawState = ptrPlayer.getClass().getNpcStats(ptrPlayer).getDrawState();
|
||||
|
||||
if (wasRunning != isRunning ||
|
||||
wasSneaking != isSneaking || wasForceJumping != isForceJumping ||
|
||||
wasForceMoveJumping != isForceMoveJumping || lastDrawState != currentDrawState ||
|
||||
wasForceMoveJumping != isForceMoveJumping || lastDrawState != drawState ||
|
||||
wasJumping || isJumping || wasFlying != isFlying ||
|
||||
forceUpdate)
|
||||
{
|
||||
|
@ -636,7 +650,7 @@ void LocalPlayer::updateAnimFlags(bool forceUpdate)
|
|||
wasRunning = isRunning;
|
||||
wasForceJumping = isForceJumping;
|
||||
wasForceMoveJumping = isForceMoveJumping;
|
||||
lastDrawState = currentDrawState;
|
||||
lastDrawState = drawState;
|
||||
|
||||
wasFlying = isFlying;
|
||||
wasJumping = isJumping;
|
||||
|
@ -653,13 +667,6 @@ void LocalPlayer::updateAnimFlags(bool forceUpdate)
|
|||
|
||||
#undef __SETFLAG
|
||||
|
||||
if (currentDrawState == MWMechanics::DrawState_Nothing)
|
||||
drawState = 0;
|
||||
else if (currentDrawState == MWMechanics::DrawState_Weapon)
|
||||
drawState = 1;
|
||||
else if (currentDrawState == MWMechanics::DrawState_Spell)
|
||||
drawState = 2;
|
||||
|
||||
if (isJumping)
|
||||
updatePosition(true); // fix position after jump;
|
||||
|
||||
|
@ -671,6 +678,7 @@ void LocalPlayer::updateAnimFlags(bool forceUpdate)
|
|||
void LocalPlayer::addItems()
|
||||
{
|
||||
MWWorld::Ptr ptrPlayer = getPlayerPtr();
|
||||
const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore();
|
||||
MWWorld::ContainerStore &ptrStore = ptrPlayer.getClass().getContainerStore(ptrPlayer);
|
||||
|
||||
for (const auto &item : inventoryChanges.items)
|
||||
|
@ -681,7 +689,9 @@ void LocalPlayer::addItems()
|
|||
|
||||
try
|
||||
{
|
||||
MWWorld::Ptr itemPtr = *ptrStore.add(item.refId, item.count, ptrPlayer);
|
||||
MWWorld::ManualRef itemRef(esmStore, item.refId, item.count);
|
||||
MWWorld::Ptr itemPtr = itemRef.getPtr();
|
||||
|
||||
if (item.charge != -1)
|
||||
itemPtr.getCellRef().setCharge(item.charge);
|
||||
|
||||
|
@ -690,6 +700,8 @@ void LocalPlayer::addItems()
|
|||
|
||||
if (!item.soul.empty())
|
||||
itemPtr.getCellRef().setSoul(item.soul);
|
||||
|
||||
ptrStore.add(itemPtr, item.count, ptrPlayer);
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
|
@ -697,7 +709,7 @@ void LocalPlayer::addItems()
|
|||
}
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView();
|
||||
updateInventoryWindow();
|
||||
}
|
||||
|
||||
void LocalPlayer::addSpells()
|
||||
|
@ -832,11 +844,11 @@ void LocalPlayer::resurrect()
|
|||
|
||||
// Ensure we unequip any items with constant effects that can put us into an infinite
|
||||
// death loop
|
||||
MechanicsHelper::unequipItemsByEffect(ptrPlayer, ESM::Enchantment::ConstantEffect, ESM::MagicEffect::DrainHealth);
|
||||
MechanicsHelper::unequipItemsByEffect(ptrPlayer, ESM::Enchantment::ConstantEffect, ESM::MagicEffect::FireDamage);
|
||||
MechanicsHelper::unequipItemsByEffect(ptrPlayer, ESM::Enchantment::ConstantEffect, ESM::MagicEffect::FrostDamage);
|
||||
MechanicsHelper::unequipItemsByEffect(ptrPlayer, ESM::Enchantment::ConstantEffect, ESM::MagicEffect::ShockDamage);
|
||||
MechanicsHelper::unequipItemsByEffect(ptrPlayer, ESM::Enchantment::ConstantEffect, ESM::MagicEffect::SunDamage);
|
||||
static const int damageEffects[5] = { ESM::MagicEffect::DrainHealth, ESM::MagicEffect::FireDamage,
|
||||
ESM::MagicEffect::FrostDamage, ESM::MagicEffect::ShockDamage, ESM::MagicEffect::SunDamage };
|
||||
|
||||
for (const auto &damageEffect : damageEffects)
|
||||
MechanicsHelper::unequipItemsByEffect(ptrPlayer, ESM::Enchantment::ConstantEffect, damageEffect);
|
||||
|
||||
Main::get().getNetworking()->getPlayerPacket(ID_PLAYER_RESURRECT)->setPlayer(this);
|
||||
Main::get().getNetworking()->getPlayerPacket(ID_PLAYER_RESURRECT)->Send();
|
||||
|
@ -853,8 +865,15 @@ void LocalPlayer::closeInventoryWindows()
|
|||
MWBase::Environment::get().getWindowManager()->finishDragDrop();
|
||||
}
|
||||
|
||||
void LocalPlayer::updateInventoryWindow()
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView();
|
||||
}
|
||||
|
||||
void LocalPlayer::setCharacter()
|
||||
{
|
||||
receivedCharacter = true;
|
||||
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
|
||||
// Ignore invalid races
|
||||
|
@ -909,7 +928,7 @@ void LocalPlayer::setAttributes()
|
|||
{
|
||||
MWWorld::Ptr ptrPlayer = getPlayerPtr();
|
||||
|
||||
MWMechanics::CreatureStats *ptrCreatureStats = &ptrPlayer.getClass().getCreatureStats(ptrPlayer);
|
||||
MWMechanics::NpcStats *ptrNpcStats = &ptrPlayer.getClass().getNpcStats(ptrPlayer);
|
||||
MWMechanics::AttributeValue attributeValue;
|
||||
|
||||
for (int attributeIndex = 0; attributeIndex < 8; ++attributeIndex)
|
||||
|
@ -917,14 +936,14 @@ void LocalPlayer::setAttributes()
|
|||
// If the server wants to clear our attribute's non-zero modifier, we need to remove
|
||||
// the spell effect causing it, to avoid an infinite loop where the effect keeps resetting
|
||||
// the modifier
|
||||
if (creatureStats.mAttributes[attributeIndex].mMod == 0 && ptrCreatureStats->getAttribute(attributeIndex).getModifier() > 0)
|
||||
if (creatureStats.mAttributes[attributeIndex].mMod == 0 && ptrNpcStats->getAttribute(attributeIndex).getModifier() > 0)
|
||||
{
|
||||
ptrCreatureStats->getActiveSpells().purgeEffectByArg(ESM::MagicEffect::FortifyAttribute, attributeIndex);
|
||||
ptrNpcStats->getActiveSpells().purgeEffectByArg(ESM::MagicEffect::FortifyAttribute, attributeIndex);
|
||||
MWBase::Environment::get().getMechanicsManager()->updateMagicEffects(ptrPlayer);
|
||||
|
||||
// Is the modifier for this attribute still higher than 0? If so, unequip items that
|
||||
// fortify the attribute
|
||||
if (ptrCreatureStats->getAttribute(attributeIndex).getModifier() > 0)
|
||||
if (ptrNpcStats->getAttribute(attributeIndex).getModifier() > 0)
|
||||
{
|
||||
MechanicsHelper::unequipItemsByEffect(ptrPlayer, ESM::Enchantment::ConstantEffect, ESM::MagicEffect::FortifyAttribute, attributeIndex, -1);
|
||||
mwmp::Main::get().getGUIController()->refreshGuiMode(MWGui::GM_Inventory);
|
||||
|
@ -932,7 +951,9 @@ void LocalPlayer::setAttributes()
|
|||
}
|
||||
|
||||
attributeValue.readState(creatureStats.mAttributes[attributeIndex]);
|
||||
ptrCreatureStats->setAttribute(attributeIndex, attributeValue);
|
||||
ptrNpcStats->setAttribute(attributeIndex, attributeValue);
|
||||
|
||||
ptrNpcStats->setSkillIncrease(attributeIndex, npcStats.mSkillIncrease[attributeIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -965,11 +986,6 @@ void LocalPlayer::setSkills()
|
|||
skillValue.readState(npcStats.mSkills[skillIndex]);
|
||||
ptrNpcStats->setSkill(skillIndex, skillValue);
|
||||
}
|
||||
|
||||
for (int attributeIndex = 0; attributeIndex < 8; ++attributeIndex)
|
||||
ptrNpcStats->setSkillIncrease(attributeIndex, npcStats.mSkillIncrease[attributeIndex]);
|
||||
|
||||
ptrNpcStats->setLevelProgress(npcStats.mLevelProgress);
|
||||
}
|
||||
|
||||
void LocalPlayer::setLevel()
|
||||
|
@ -977,8 +993,9 @@ void LocalPlayer::setLevel()
|
|||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
MWWorld::Ptr ptrPlayer = world->getPlayerPtr();
|
||||
|
||||
MWMechanics::CreatureStats *ptrCreatureStats = &ptrPlayer.getClass().getCreatureStats(ptrPlayer);
|
||||
ptrCreatureStats->setLevel(creatureStats.mLevel);
|
||||
MWMechanics::NpcStats *ptrNpcStats = &ptrPlayer.getClass().getNpcStats(ptrPlayer);
|
||||
ptrNpcStats->setLevel(creatureStats.mLevel);
|
||||
ptrNpcStats->setLevelProgress(npcStats.mLevelProgress);
|
||||
}
|
||||
|
||||
void LocalPlayer::setBounty()
|
||||
|
@ -1294,8 +1311,23 @@ void LocalPlayer::setFactions()
|
|||
|
||||
void LocalPlayer::setKills()
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Received ID_WORLD_KILL_COUNT with the following kill counts:");
|
||||
std::string debugMessage = "";
|
||||
|
||||
for (const auto &kill : killChanges.kills)
|
||||
{
|
||||
if (Log::GetLevel() <= Log::LOG_INFO)
|
||||
{
|
||||
if (!debugMessage.empty())
|
||||
debugMessage += ", ";
|
||||
|
||||
debugMessage += kill.refId + ": " + std::to_string(kill.number);
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getMechanicsManager()->setDeaths(kill.refId, kill.number);
|
||||
}
|
||||
|
||||
LOG_APPEND(Log::LOG_INFO, "- %s", debugMessage.c_str());
|
||||
}
|
||||
|
||||
void LocalPlayer::setBooks()
|
||||
|
@ -1359,7 +1391,7 @@ void LocalPlayer::sendClass()
|
|||
|
||||
void LocalPlayer::sendInventory()
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Sending entire inventory to server");
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending entire inventory to server");
|
||||
|
||||
MWWorld::Ptr ptrPlayer = getPlayerPtr();
|
||||
MWWorld::InventoryStore &ptrInventory = ptrPlayer.getClass().getInventoryStore(ptrPlayer);
|
||||
|
@ -1392,36 +1424,28 @@ void LocalPlayer::sendInventory()
|
|||
getNetworking()->getPlayerPacket(ID_PLAYER_INVENTORY)->Send();
|
||||
}
|
||||
|
||||
|
||||
void LocalPlayer::sendItemChange(const MWWorld::Ptr& itemPtr, int count, unsigned int action)
|
||||
void LocalPlayer::sendItemChange(const mwmp::Item& item, unsigned int action)
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Sending item change for %s with action %i, count %i",
|
||||
itemPtr.getCellRef().getRefId().c_str(), action, count);
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending item change for %s with action %i, count %i",
|
||||
item.refId.c_str(), action, item.count);
|
||||
|
||||
inventoryChanges.items.clear();
|
||||
|
||||
mwmp::Item item;
|
||||
|
||||
if (itemPtr.getClass().isGold(itemPtr))
|
||||
item.refId = MWWorld::ContainerStore::sGoldId;
|
||||
else
|
||||
item.refId = itemPtr.getCellRef().getRefId();
|
||||
|
||||
item.count = count;
|
||||
item.charge = itemPtr.getCellRef().getCharge();
|
||||
item.enchantmentCharge = itemPtr.getCellRef().getEnchantmentCharge();
|
||||
item.soul = itemPtr.getCellRef().getSoul();
|
||||
|
||||
inventoryChanges.items.push_back(item);
|
||||
|
||||
inventoryChanges.action = action;
|
||||
|
||||
getNetworking()->getPlayerPacket(ID_PLAYER_INVENTORY)->setPlayer(this);
|
||||
getNetworking()->getPlayerPacket(ID_PLAYER_INVENTORY)->Send();
|
||||
}
|
||||
|
||||
void LocalPlayer::sendItemChange(const MWWorld::Ptr& itemPtr, int count, unsigned int action)
|
||||
{
|
||||
mwmp::Item item = MechanicsHelper::getItem(itemPtr, count);
|
||||
sendItemChange(item, action);
|
||||
}
|
||||
|
||||
void LocalPlayer::sendItemChange(const std::string& refId, int count, unsigned int action)
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Sending item change for %s with action %i, count %i",
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending item change for %s with action %i, count %i",
|
||||
refId.c_str(), action, count);
|
||||
|
||||
inventoryChanges.items.clear();
|
||||
|
|
|
@ -16,6 +16,7 @@ namespace mwmp
|
|||
virtual ~LocalPlayer();
|
||||
|
||||
time_t deathTime;
|
||||
bool receivedCharacter;
|
||||
|
||||
bool isReceivingInventory;
|
||||
bool isReceivingQuickKeys;
|
||||
|
@ -25,7 +26,7 @@ namespace mwmp
|
|||
void update();
|
||||
|
||||
bool processCharGen();
|
||||
bool hasFinishedCharGen();
|
||||
bool isLoggedIn();
|
||||
|
||||
void updateStatsDynamic(bool forceUpdate = false);
|
||||
void updateAttributes(bool forceUpdate = false);
|
||||
|
@ -51,6 +52,7 @@ namespace mwmp
|
|||
void resurrect();
|
||||
|
||||
void closeInventoryWindows();
|
||||
void updateInventoryWindow();
|
||||
|
||||
void setCharacter();
|
||||
void setDynamicStats();
|
||||
|
@ -76,6 +78,7 @@ namespace mwmp
|
|||
|
||||
void sendClass();
|
||||
void sendInventory();
|
||||
void sendItemChange(const mwmp::Item& item, unsigned int action);
|
||||
void sendItemChange(const MWWorld::Ptr& itemPtr, int count, unsigned int action);
|
||||
void sendItemChange(const std::string& refId, int count, unsigned int action);
|
||||
void sendSpellbook();
|
||||
|
|
|
@ -51,8 +51,8 @@ using namespace mwmp;
|
|||
using namespace std;
|
||||
|
||||
Main *Main::pMain = 0;
|
||||
std::string Main::addr = "";
|
||||
std::string Main::passw = TES3MP_DEFAULT_PASSW;
|
||||
std::string Main::address = "";
|
||||
std::string Main::serverPassword = TES3MP_DEFAULT_PASSW;
|
||||
std::string Main::resourceDir = "";
|
||||
|
||||
std::string Main::getResDir()
|
||||
|
@ -60,7 +60,7 @@ std::string Main::getResDir()
|
|||
return resourceDir;
|
||||
}
|
||||
|
||||
std::string loadSettings (Settings::Manager & settings)
|
||||
std::string loadSettings(Settings::Manager& settings)
|
||||
{
|
||||
Files::ConfigurationManager mCfgMgr;
|
||||
// Create the settings manager and load default settings file
|
||||
|
@ -90,7 +90,6 @@ Main::Main()
|
|||
mLocalPlayer = new LocalPlayer();
|
||||
mGUIController = new GUIController();
|
||||
mCellController = new CellController();
|
||||
//mLocalPlayer->CharGen(0, 4);
|
||||
|
||||
server = "mp.tes3mp.com";
|
||||
port = 25565;
|
||||
|
@ -118,8 +117,8 @@ void Main::optionsDesc(boost::program_options::options_description *desc)
|
|||
|
||||
void Main::configure(const boost::program_options::variables_map &variables)
|
||||
{
|
||||
Main::addr = variables["connect"].as<string>();
|
||||
Main::passw = variables["password"].as<string>();
|
||||
Main::address = variables["connect"].as<string>();
|
||||
Main::serverPassword = variables["password"].as<string>();
|
||||
resourceDir = variables["resources"].as<Files::EscapeHashString>().toStdString();
|
||||
}
|
||||
|
||||
|
@ -127,22 +126,22 @@ static Settings::CategorySettingValueMap saveUserSettings;
|
|||
static Settings::CategorySettingValueMap saveDefaultSettings;
|
||||
static Settings::CategorySettingVector saveChangedSettings;
|
||||
|
||||
void InitMgr(Settings::Manager &mgr)
|
||||
void initializeManager(Settings::Manager &manager)
|
||||
{
|
||||
saveUserSettings = mgr.mUserSettings;
|
||||
saveDefaultSettings = mgr.mDefaultSettings;
|
||||
saveChangedSettings = mgr.mChangedSettings;
|
||||
mgr.mUserSettings.clear();
|
||||
mgr.mDefaultSettings.clear();
|
||||
mgr.mChangedSettings.clear();
|
||||
loadSettings(mgr);
|
||||
saveUserSettings = manager.mUserSettings;
|
||||
saveDefaultSettings = manager.mDefaultSettings;
|
||||
saveChangedSettings = manager.mChangedSettings;
|
||||
manager.mUserSettings.clear();
|
||||
manager.mDefaultSettings.clear();
|
||||
manager.mChangedSettings.clear();
|
||||
loadSettings(manager);
|
||||
}
|
||||
|
||||
void RestoreMgr(Settings::Manager &mgr)
|
||||
void restoreManager(Settings::Manager &manager)
|
||||
{
|
||||
mgr.mUserSettings = saveUserSettings;
|
||||
mgr.mDefaultSettings = saveDefaultSettings;
|
||||
mgr.mChangedSettings = saveChangedSettings;
|
||||
manager.mUserSettings = saveUserSettings;
|
||||
manager.mDefaultSettings = saveDefaultSettings;
|
||||
manager.mChangedSettings = saveChangedSettings;
|
||||
}
|
||||
|
||||
bool Main::init(std::vector<std::string> &content, Files::Collections &collections)
|
||||
|
@ -150,40 +149,40 @@ bool Main::init(std::vector<std::string> &content, Files::Collections &collectio
|
|||
assert(!pMain);
|
||||
pMain = new Main();
|
||||
|
||||
Settings::Manager mgr;
|
||||
InitMgr(mgr);
|
||||
Settings::Manager manager;
|
||||
initializeManager(manager);
|
||||
|
||||
int logLevel = mgr.getInt("logLevel", "General");
|
||||
int logLevel = manager.getInt("logLevel", "General");
|
||||
Log::SetLevel(logLevel);
|
||||
if (addr.empty())
|
||||
if (address.empty())
|
||||
{
|
||||
pMain->server = mgr.getString("destinationAddress", "General");
|
||||
pMain->port = (unsigned short) mgr.getInt("port", "General");
|
||||
pMain->server = manager.getString("destinationAddress", "General");
|
||||
pMain->port = (unsigned short) manager.getInt("port", "General");
|
||||
|
||||
passw = mgr.getString("password", "General");
|
||||
if (passw.empty())
|
||||
passw = TES3MP_DEFAULT_PASSW;
|
||||
serverPassword = manager.getString("password", "General");
|
||||
if (serverPassword.empty())
|
||||
serverPassword = TES3MP_DEFAULT_PASSW;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t delim_pos = addr.find(':');
|
||||
pMain->server = addr.substr(0, delim_pos);
|
||||
pMain->port = atoi(addr.substr(delim_pos + 1).c_str());
|
||||
size_t delimPos = address.find(':');
|
||||
pMain->server = address.substr(0, delimPos);
|
||||
pMain->port = atoi(address.substr(delimPos + 1).c_str());
|
||||
}
|
||||
get().mLocalPlayer->passw = passw;
|
||||
get().mLocalPlayer->serverPassword = serverPassword;
|
||||
|
||||
pMain->mNetworking->connect(pMain->server, pMain->port, content, collections);
|
||||
RestoreMgr(mgr);
|
||||
restoreManager(manager);
|
||||
return pMain->mNetworking->isConnected();
|
||||
}
|
||||
|
||||
void Main::postInit()
|
||||
{
|
||||
Settings::Manager mgr;
|
||||
InitMgr(mgr);
|
||||
Settings::Manager manager;
|
||||
initializeManager(manager);
|
||||
|
||||
pMain->mGUIController->setupChat(mgr);
|
||||
RestoreMgr(mgr);
|
||||
pMain->mGUIController->setupChat(manager);
|
||||
restoreManager(manager);
|
||||
|
||||
const MWBase::Environment &environment = MWBase::Environment::get();
|
||||
environment.getStateManager()->newGame(true);
|
||||
|
|
|
@ -39,8 +39,8 @@ namespace mwmp
|
|||
|
||||
private:
|
||||
static std::string resourceDir;
|
||||
static std::string addr;
|
||||
static std::string passw;
|
||||
static std::string address;
|
||||
static std::string serverPassword;
|
||||
Main (const Main&);
|
||||
///< not implemented
|
||||
Main& operator= (const Main&);
|
||||
|
|
|
@ -83,7 +83,7 @@ bool MechanicsHelper::isUsingRangedWeapon(const MWWorld::Ptr& ptr)
|
|||
MWWorld::ContainerStoreIterator weaponSlot = inventoryStore.getSlot(
|
||||
MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
|
||||
if (weaponSlot != inventoryStore.end())
|
||||
if (weaponSlot != inventoryStore.end() && weaponSlot->getTypeName() == typeid(ESM::Weapon).name())
|
||||
{
|
||||
const ESM::Weapon* weaponRecord = weaponSlot->get<ESM::Weapon>()->mBase;
|
||||
|
||||
|
@ -125,6 +125,23 @@ MWWorld::Ptr MechanicsHelper::getPlayerPtr(const Target& target)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
mwmp::Item MechanicsHelper::getItem(const MWWorld::Ptr& itemPtr, int count)
|
||||
{
|
||||
mwmp::Item item;
|
||||
|
||||
if (itemPtr.getClass().isGold(itemPtr))
|
||||
item.refId = MWWorld::ContainerStore::sGoldId;
|
||||
else
|
||||
item.refId = itemPtr.getCellRef().getRefId();
|
||||
|
||||
item.count = count;
|
||||
item.charge = itemPtr.getCellRef().getCharge();
|
||||
item.enchantmentCharge = itemPtr.getCellRef().getEnchantmentCharge();
|
||||
item.soul = itemPtr.getCellRef().getSoul();
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
mwmp::Target MechanicsHelper::getTarget(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
mwmp::Target target;
|
||||
|
@ -393,19 +410,15 @@ void MechanicsHelper::processAttack(Attack attack, const MWWorld::Ptr& attacker)
|
|||
break;
|
||||
}
|
||||
|
||||
if (it != inventoryStore.end())
|
||||
{
|
||||
// Add the item if it's missing
|
||||
if (it == inventoryStore.end())
|
||||
it = attacker.getClass().getContainerStore(attacker).add(attack.itemId, 1, attacker);
|
||||
|
||||
inventoryStore.setSelectedEnchantItem(it);
|
||||
LOG_APPEND(Log::LOG_VERBOSE, "- itemId: %s", attack.itemId.c_str());
|
||||
MWBase::Environment::get().getWorld()->castSpell(attacker);
|
||||
inventoryStore.setSelectedEnchantItem(inventoryStore.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Could not find item %s used by %s to cast item spell!",
|
||||
attack.itemId.c_str(), attacker.getCellRef().getRefId().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MechanicsHelper::doesEffectListContainEffect(const ESM::EffectList& effectList, short effectId, short attributeId, short skillId)
|
||||
|
@ -452,17 +465,25 @@ void MechanicsHelper::unequipItemsByEffect(const MWWorld::Ptr& ptr, short enchan
|
|||
|
||||
MWWorld::Ptr MechanicsHelper::getItemPtrFromStore(const mwmp::Item& item, MWWorld::ContainerStore& store)
|
||||
{
|
||||
MWWorld::Ptr closestPtr;
|
||||
|
||||
for (MWWorld::ContainerStoreIterator storeIterator = store.begin(); storeIterator != store.end(); ++storeIterator)
|
||||
{
|
||||
// Enchantment charges are often in the process of refilling themselves, so don't check for them here
|
||||
if (Misc::StringUtils::ciEqual(item.refId, storeIterator->getCellRef().getRefId()) &&
|
||||
item.count == storeIterator->getRefData().getCount() &&
|
||||
item.charge == storeIterator->getCellRef().getCharge() &&
|
||||
item.enchantmentCharge == storeIterator->getCellRef().getEnchantmentCharge() &&
|
||||
Misc::StringUtils::ciEqual(item.soul, storeIterator->getCellRef().getSoul()))
|
||||
{
|
||||
return *storeIterator;
|
||||
// If we have no closestPtr, set it to the Ptr corresponding to this storeIterator; otherwise, make
|
||||
// sure the storeIterator's enchantmentCharge is closer to our goal than that of the previous closestPtr
|
||||
if (!closestPtr || abs(storeIterator->getCellRef().getEnchantmentCharge() - item.enchantmentCharge) <
|
||||
abs(closestPtr.getCellRef().getEnchantmentCharge() - item.enchantmentCharge))
|
||||
{
|
||||
closestPtr = *storeIterator;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return closestPtr;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace MechanicsHelper
|
|||
|
||||
MWWorld::Ptr getPlayerPtr(const mwmp::Target& target);
|
||||
|
||||
mwmp::Item getItem(const MWWorld::Ptr& itemPtr, int count);
|
||||
mwmp::Target getTarget(const MWWorld::Ptr& ptr);
|
||||
void clearTarget(mwmp::Target& target);
|
||||
bool isEmptyTarget(const mwmp::Target& target);
|
||||
|
|
|
@ -45,7 +45,7 @@ using namespace mwmp;
|
|||
string listDiscrepancies(PacketPreInit::PluginContainer checksums, PacketPreInit::PluginContainer checksumsResponse)
|
||||
{
|
||||
std::ostringstream sstr;
|
||||
sstr << "Your plugins or their load order don't match the server's. A full comparison is included in your client console and latest log file. In short, the following discrepancies have been found:\n\n";
|
||||
sstr << "Your plugins or their load order don't match the server's. A full comparison is included in your debug window and latest log file. In short, the following discrepancies have been found:\n\n";
|
||||
|
||||
int discrepancyCount = 0;
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include "../mwgui/container.hpp"
|
||||
#include "../mwgui/inventorywindow.hpp"
|
||||
#include "../mwgui/windowmanagerimp.hpp"
|
||||
|
||||
#include "../mwmechanics/aifollow.hpp"
|
||||
#include "../mwmechanics/spellcasting.hpp"
|
||||
|
@ -320,11 +322,21 @@ void ObjectList::activateObjects(MWWorld::CellStore* cellStore)
|
|||
}
|
||||
|
||||
if (activatingActorPtr)
|
||||
{
|
||||
// Is an item that can be picked up being activated by the local player with their inventory open?
|
||||
if (activatingActorPtr == MWBase::Environment::get().getWorld()->getPlayerPtr() &&
|
||||
(MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Container ||
|
||||
MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Inventory))
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(ptrFound);
|
||||
}
|
||||
else
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->activate(ptrFound, activatingActorPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectList::placeObjects(MWWorld::CellStore* cellStore)
|
||||
|
@ -333,7 +345,7 @@ void ObjectList::placeObjects(MWWorld::CellStore* cellStore)
|
|||
|
||||
for (const auto &baseObject : baseObjects)
|
||||
{
|
||||
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s %i-%i, count: %i, charge: %i, enchantmentCharge: %i, soul: %s",
|
||||
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s %i-%i, count: %i, charge: %i, enchantmentCharge: %.2f, soul: %s",
|
||||
baseObject.refId.c_str(), baseObject.refNum, baseObject.mpNum, baseObject.count, baseObject.charge,
|
||||
baseObject.enchantmentCharge, baseObject.soul.c_str());
|
||||
|
||||
|
|
|
@ -92,6 +92,17 @@ bool PlayerList::isDedicatedPlayer(const MWWorld::Ptr &ptr)
|
|||
return (getPlayer(ptr) != 0);
|
||||
}
|
||||
|
||||
void PlayerList::enableMarkers(const ESM::Cell& cell)
|
||||
{
|
||||
for (auto &playerEntry : players)
|
||||
{
|
||||
if (Main::get().getCellController()->isSameCell(cell, playerEntry.second->cell))
|
||||
{
|
||||
playerEntry.second->enableMarker();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Go through all DedicatedPlayers checking if their mHitAttemptActorId matches this one
|
||||
and set it to -1 if it does
|
||||
|
|
|
@ -37,6 +37,8 @@ namespace mwmp
|
|||
|
||||
static bool isDedicatedPlayer(const MWWorld::Ptr &ptr);
|
||||
|
||||
static void enableMarkers(const ESM::Cell& cell);
|
||||
|
||||
static void clearHitAttemptActorId(int actorId);
|
||||
|
||||
private:
|
||||
|
|
|
@ -110,7 +110,7 @@ void RecordHelper::overrideCreatureRecord(const mwmp::CreatureRecord& record)
|
|||
|
||||
if (recordData.mId.empty())
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with no id provided");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -164,6 +164,11 @@ void RecordHelper::overrideCreatureRecord(const mwmp::CreatureRecord& record)
|
|||
|
||||
world->getModifiableStore().overrideRecord(finalData);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with invalid baseId %s", record.baseId.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (isExistingId)
|
||||
world->updatePtrsWithRefId(recordData.mId);
|
||||
|
@ -175,7 +180,7 @@ void RecordHelper::overrideNpcRecord(const mwmp::NpcRecord& record)
|
|||
|
||||
if (recordData.mId.empty())
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with no id provided");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -186,12 +191,12 @@ void RecordHelper::overrideNpcRecord(const mwmp::NpcRecord& record)
|
|||
{
|
||||
if (!doesRaceRecordExist(recordData.mRace))
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring new NPC record with invalid race provided");
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring new NPC record with invalid race provided");
|
||||
return;
|
||||
}
|
||||
else if (!doesClassRecordExist(recordData.mClass))
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring new NPC record with invalid class provided");
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring new NPC record with invalid class provided");
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
@ -272,6 +277,11 @@ void RecordHelper::overrideNpcRecord(const mwmp::NpcRecord& record)
|
|||
|
||||
world->getModifiableStore().overrideRecord(finalData);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with invalid baseId %s", record.baseId.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (isExistingId)
|
||||
world->updatePtrsWithRefId(recordData.mId);
|
||||
|
@ -283,18 +293,17 @@ void RecordHelper::overrideEnchantmentRecord(const mwmp::EnchantmentRecord& reco
|
|||
|
||||
if (recordData.mId.empty())
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with no id provided");
|
||||
return;
|
||||
}
|
||||
|
||||
bool isExistingId = doesEnchantmentRecordExist(recordData.mId);
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
|
||||
if (record.baseId.empty())
|
||||
{
|
||||
if (recordData.mEffects.mList.empty())
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring new enchantment record with no effects");
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring new enchantment record with no effects");
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
@ -323,6 +332,11 @@ void RecordHelper::overrideEnchantmentRecord(const mwmp::EnchantmentRecord& reco
|
|||
|
||||
world->getModifiableStore().overrideRecord(finalData);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with invalid baseId %s", record.baseId.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void RecordHelper::overridePotionRecord(const mwmp::PotionRecord& record)
|
||||
|
@ -331,7 +345,7 @@ void RecordHelper::overridePotionRecord(const mwmp::PotionRecord& record)
|
|||
|
||||
if (recordData.mId.empty())
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with no id provided");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -374,6 +388,11 @@ void RecordHelper::overridePotionRecord(const mwmp::PotionRecord& record)
|
|||
|
||||
world->getModifiableStore().overrideRecord(finalData);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with invalid baseId %s", record.baseId.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (isExistingId)
|
||||
world->updatePtrsWithRefId(recordData.mId);
|
||||
|
@ -385,7 +404,7 @@ void RecordHelper::overrideSpellRecord(const mwmp::SpellRecord& record)
|
|||
|
||||
if (recordData.mId.empty())
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with no id provided");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -419,9 +438,11 @@ void RecordHelper::overrideSpellRecord(const mwmp::SpellRecord& record)
|
|||
|
||||
world->getModifiableStore().overrideRecord(finalData);
|
||||
}
|
||||
|
||||
if (isExistingId)
|
||||
world->updatePtrsWithRefId(recordData.mId);
|
||||
else
|
||||
{
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with invalid baseId %s", record.baseId.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void RecordHelper::overrideArmorRecord(const mwmp::ArmorRecord& record)
|
||||
|
@ -430,7 +451,7 @@ void RecordHelper::overrideArmorRecord(const mwmp::ArmorRecord& record)
|
|||
|
||||
if (recordData.mId.empty())
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with no id provided");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -441,7 +462,7 @@ void RecordHelper::overrideArmorRecord(const mwmp::ArmorRecord& record)
|
|||
{
|
||||
if (!recordData.mEnchant.empty() && !doesEnchantmentRecordExist(recordData.mEnchant))
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring new armor record with invalid enchantment provided");
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring new armor record with invalid enchantmentId %s", recordData.mEnchant.c_str());
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
@ -479,8 +500,13 @@ void RecordHelper::overrideArmorRecord(const mwmp::ArmorRecord& record)
|
|||
if (record.baseOverrides.hasArmorRating)
|
||||
finalData.mData.mArmor = recordData.mData.mArmor;
|
||||
|
||||
if (record.baseOverrides.hasEnchantmentId && doesEnchantmentRecordExist(recordData.mEnchant))
|
||||
if (record.baseOverrides.hasEnchantmentId)
|
||||
{
|
||||
if (doesEnchantmentRecordExist(recordData.mEnchant))
|
||||
finalData.mEnchant = recordData.mEnchant;
|
||||
else
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring invalid enchantmentId %s", recordData.mEnchant.c_str());
|
||||
}
|
||||
|
||||
if (record.baseOverrides.hasEnchantmentCharge)
|
||||
finalData.mData.mEnchant = recordData.mData.mEnchant;
|
||||
|
@ -493,6 +519,11 @@ void RecordHelper::overrideArmorRecord(const mwmp::ArmorRecord& record)
|
|||
|
||||
world->getModifiableStore().overrideRecord(finalData);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with invalid baseId %s", record.baseId.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (isExistingId)
|
||||
world->updatePtrsWithRefId(recordData.mId);
|
||||
|
@ -504,7 +535,7 @@ void RecordHelper::overrideBookRecord(const mwmp::BookRecord& record)
|
|||
|
||||
if (recordData.mId.empty())
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with no id provided");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -515,7 +546,7 @@ void RecordHelper::overrideBookRecord(const mwmp::BookRecord& record)
|
|||
{
|
||||
if (!recordData.mEnchant.empty() && !doesEnchantmentRecordExist(recordData.mEnchant))
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring new book record with invalid enchantment provided");
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring new book record with invalid enchantmentId %s", recordData.mEnchant.c_str());
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
@ -551,8 +582,13 @@ void RecordHelper::overrideBookRecord(const mwmp::BookRecord& record)
|
|||
if (record.baseOverrides.hasSkillId)
|
||||
finalData.mData.mSkillId = recordData.mData.mSkillId;
|
||||
|
||||
if (record.baseOverrides.hasEnchantmentId && doesEnchantmentRecordExist(recordData.mEnchant))
|
||||
if (record.baseOverrides.hasEnchantmentId)
|
||||
{
|
||||
if (doesEnchantmentRecordExist(recordData.mEnchant))
|
||||
finalData.mEnchant = recordData.mEnchant;
|
||||
else
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring invalid enchantmentId %s", recordData.mEnchant.c_str());
|
||||
}
|
||||
|
||||
if (record.baseOverrides.hasEnchantmentCharge)
|
||||
finalData.mData.mEnchant = recordData.mData.mEnchant;
|
||||
|
@ -562,6 +598,11 @@ void RecordHelper::overrideBookRecord(const mwmp::BookRecord& record)
|
|||
|
||||
world->getModifiableStore().overrideRecord(finalData);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with invalid baseId %s", record.baseId.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (isExistingId)
|
||||
world->updatePtrsWithRefId(recordData.mId);
|
||||
|
@ -573,7 +614,7 @@ void RecordHelper::overrideClothingRecord(const mwmp::ClothingRecord& record)
|
|||
|
||||
if (recordData.mId.empty())
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with no id provided");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -584,7 +625,7 @@ void RecordHelper::overrideClothingRecord(const mwmp::ClothingRecord& record)
|
|||
{
|
||||
if (!recordData.mEnchant.empty() && !doesEnchantmentRecordExist(recordData.mEnchant))
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring new clothing record with invalid enchantment provided");
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring new clothing record with invalid enchantmentId %s", recordData.mEnchant.c_str());
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
@ -614,8 +655,13 @@ void RecordHelper::overrideClothingRecord(const mwmp::ClothingRecord& record)
|
|||
if (record.baseOverrides.hasValue)
|
||||
finalData.mData.mValue = recordData.mData.mValue;
|
||||
|
||||
if (record.baseOverrides.hasEnchantmentId && doesEnchantmentRecordExist(recordData.mEnchant))
|
||||
if (record.baseOverrides.hasEnchantmentId)
|
||||
{
|
||||
if (doesEnchantmentRecordExist(recordData.mEnchant))
|
||||
finalData.mEnchant = recordData.mEnchant;
|
||||
else
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring invalid enchantmentId %s", recordData.mEnchant.c_str());
|
||||
}
|
||||
|
||||
if (record.baseOverrides.hasEnchantmentCharge)
|
||||
finalData.mData.mEnchant = recordData.mData.mEnchant;
|
||||
|
@ -628,6 +674,11 @@ void RecordHelper::overrideClothingRecord(const mwmp::ClothingRecord& record)
|
|||
|
||||
world->getModifiableStore().overrideRecord(finalData);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with invalid baseId %s", record.baseId.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (isExistingId)
|
||||
world->updatePtrsWithRefId(recordData.mId);
|
||||
|
@ -639,7 +690,7 @@ void RecordHelper::overrideMiscellaneousRecord(const mwmp::MiscellaneousRecord&
|
|||
|
||||
if (recordData.mId.empty())
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with no id provided");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -679,6 +730,11 @@ void RecordHelper::overrideMiscellaneousRecord(const mwmp::MiscellaneousRecord&
|
|||
|
||||
world->getModifiableStore().overrideRecord(finalData);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with invalid baseId %s", record.baseId.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (isExistingId)
|
||||
world->updatePtrsWithRefId(recordData.mId);
|
||||
|
@ -690,7 +746,7 @@ void RecordHelper::overrideWeaponRecord(const mwmp::WeaponRecord& record)
|
|||
|
||||
if (recordData.mId.empty())
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with no id provided");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -701,7 +757,7 @@ void RecordHelper::overrideWeaponRecord(const mwmp::WeaponRecord& record)
|
|||
{
|
||||
if (!recordData.mEnchant.empty() && !doesEnchantmentRecordExist(recordData.mEnchant))
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring new weapon record with invalid enchantment provided");
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring new weapon record with invalid enchantmentId %s", recordData.mEnchant.c_str());
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
@ -761,8 +817,13 @@ void RecordHelper::overrideWeaponRecord(const mwmp::WeaponRecord& record)
|
|||
if (record.baseOverrides.hasFlags)
|
||||
finalData.mData.mFlags = recordData.mData.mFlags;
|
||||
|
||||
if (record.baseOverrides.hasEnchantmentId && doesEnchantmentRecordExist(recordData.mEnchant))
|
||||
if (record.baseOverrides.hasEnchantmentId)
|
||||
{
|
||||
if (doesEnchantmentRecordExist(recordData.mEnchant))
|
||||
finalData.mEnchant = recordData.mEnchant;
|
||||
else
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring invalid enchantmentId %s", recordData.mEnchant.c_str());
|
||||
}
|
||||
|
||||
if (record.baseOverrides.hasEnchantmentCharge)
|
||||
finalData.mData.mEnchant = recordData.mData.mEnchant;
|
||||
|
@ -772,6 +833,11 @@ void RecordHelper::overrideWeaponRecord(const mwmp::WeaponRecord& record)
|
|||
|
||||
world->getModifiableStore().overrideRecord(finalData);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with invalid baseId %s", record.baseId.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (isExistingId)
|
||||
world->updatePtrsWithRefId(recordData.mId);
|
||||
|
|
|
@ -35,7 +35,7 @@ Networking *Worldstate::getNetworking()
|
|||
|
||||
void Worldstate::addRecords()
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Received ID_RECORD_DYNAMIC with %i records of type %i",
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Received ID_RECORD_DYNAMIC with %i records of type %i",
|
||||
recordsCount, recordsType);
|
||||
|
||||
if (recordsType == mwmp::RECORD_TYPE::SPELL)
|
||||
|
@ -44,7 +44,7 @@ void Worldstate::addRecords()
|
|||
{
|
||||
bool hasBaseId = !record.baseId.empty();
|
||||
|
||||
LOG_APPEND(Log::LOG_ERROR, "- spell record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
||||
LOG_APPEND(Log::LOG_INFO, "- spell record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
||||
hasBaseId ? record.baseId.c_str() : "empty");
|
||||
|
||||
RecordHelper::overrideSpellRecord(record);
|
||||
|
@ -56,7 +56,7 @@ void Worldstate::addRecords()
|
|||
{
|
||||
bool hasBaseId = !record.baseId.empty();
|
||||
|
||||
LOG_APPEND(Log::LOG_ERROR, "- potion record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
||||
LOG_APPEND(Log::LOG_INFO, "- potion record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
||||
hasBaseId ? record.baseId.c_str() : "empty");
|
||||
|
||||
RecordHelper::overridePotionRecord(record);
|
||||
|
@ -68,7 +68,7 @@ void Worldstate::addRecords()
|
|||
{
|
||||
bool hasBaseId = !record.baseId.empty();
|
||||
|
||||
LOG_APPEND(Log::LOG_ERROR, "- enchantment record %s, %i\n-- baseId is %s", record.data.mId.c_str(), record.data.mData.mType,
|
||||
LOG_APPEND(Log::LOG_INFO, "- enchantment record %s, %i\n-- baseId is %s", record.data.mId.c_str(), record.data.mData.mType,
|
||||
hasBaseId ? record.baseId.c_str() : "empty");
|
||||
|
||||
RecordHelper::overrideEnchantmentRecord(record);
|
||||
|
@ -80,7 +80,7 @@ void Worldstate::addRecords()
|
|||
{
|
||||
bool hasBaseId = !record.baseId.empty();
|
||||
|
||||
LOG_APPEND(Log::LOG_ERROR, "- creature record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
||||
LOG_APPEND(Log::LOG_INFO, "- creature record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
||||
hasBaseId ? record.baseId.c_str() : "empty");
|
||||
|
||||
RecordHelper::overrideCreatureRecord(record);
|
||||
|
@ -92,7 +92,7 @@ void Worldstate::addRecords()
|
|||
{
|
||||
bool hasBaseId = !record.baseId.empty();
|
||||
|
||||
LOG_APPEND(Log::LOG_ERROR, "- NPC record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
||||
LOG_APPEND(Log::LOG_INFO, "- NPC record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
||||
hasBaseId ? record.baseId.c_str() : "empty");
|
||||
|
||||
RecordHelper::overrideNpcRecord(record);
|
||||
|
@ -104,7 +104,7 @@ void Worldstate::addRecords()
|
|||
{
|
||||
bool hasBaseId = !record.baseId.empty();
|
||||
|
||||
LOG_APPEND(Log::LOG_ERROR, "- armor record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
||||
LOG_APPEND(Log::LOG_INFO, "- armor record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
||||
hasBaseId ? record.baseId.c_str() : "empty");
|
||||
|
||||
RecordHelper::overrideArmorRecord(record);
|
||||
|
@ -116,7 +116,7 @@ void Worldstate::addRecords()
|
|||
{
|
||||
bool hasBaseId = !record.baseId.empty();
|
||||
|
||||
LOG_APPEND(Log::LOG_ERROR, "- book record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
||||
LOG_APPEND(Log::LOG_INFO, "- book record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
||||
hasBaseId ? record.baseId.c_str() : "empty");
|
||||
|
||||
RecordHelper::overrideBookRecord(record);
|
||||
|
@ -128,7 +128,7 @@ void Worldstate::addRecords()
|
|||
{
|
||||
bool hasBaseId = !record.baseId.empty();
|
||||
|
||||
LOG_APPEND(Log::LOG_ERROR, "- clothing record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
||||
LOG_APPEND(Log::LOG_INFO, "- clothing record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
||||
hasBaseId ? record.baseId.c_str() : "empty");
|
||||
|
||||
RecordHelper::overrideClothingRecord(record);
|
||||
|
@ -140,7 +140,7 @@ void Worldstate::addRecords()
|
|||
{
|
||||
bool hasBaseId = !record.baseId.empty();
|
||||
|
||||
LOG_APPEND(Log::LOG_ERROR, "- miscellaneous record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
||||
LOG_APPEND(Log::LOG_INFO, "- miscellaneous record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
||||
hasBaseId ? record.baseId.c_str() : "empty");
|
||||
|
||||
RecordHelper::overrideMiscellaneousRecord(record);
|
||||
|
@ -152,7 +152,7 @@ void Worldstate::addRecords()
|
|||
{
|
||||
bool hasBaseId = !record.baseId.empty();
|
||||
|
||||
LOG_APPEND(Log::LOG_ERROR, "- weapon record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
||||
LOG_APPEND(Log::LOG_INFO, "- weapon record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
|
||||
hasBaseId ? record.baseId.c_str() : "empty");
|
||||
|
||||
RecordHelper::overrideWeaponRecord(record);
|
||||
|
@ -251,7 +251,7 @@ void Worldstate::sendEnchantmentRecord(const ESM::Enchantment* enchantment)
|
|||
{
|
||||
enchantmentRecords.clear();
|
||||
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Sending ID_RECORD_DYNAMIC with enchantment");
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_RECORD_DYNAMIC with enchantment");
|
||||
|
||||
recordsType = mwmp::RECORD_TYPE::ENCHANTMENT;
|
||||
|
||||
|
@ -267,7 +267,7 @@ void Worldstate::sendPotionRecord(const ESM::Potion* potion)
|
|||
{
|
||||
potionRecords.clear();
|
||||
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Sending ID_RECORD_DYNAMIC with potion %s", potion->mName.c_str());
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_RECORD_DYNAMIC with potion %s", potion->mName.c_str());
|
||||
|
||||
recordsType = mwmp::RECORD_TYPE::POTION;
|
||||
|
||||
|
@ -283,7 +283,7 @@ void Worldstate::sendSpellRecord(const ESM::Spell* spell)
|
|||
{
|
||||
spellRecords.clear();
|
||||
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Sending ID_RECORD_DYNAMIC with spell %s", spell->mName.c_str());
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_RECORD_DYNAMIC with spell %s", spell->mName.c_str());
|
||||
|
||||
recordsType = mwmp::RECORD_TYPE::SPELL;
|
||||
|
||||
|
@ -299,7 +299,7 @@ void Worldstate::sendArmorRecord(const ESM::Armor* armor, std::string baseId)
|
|||
{
|
||||
armorRecords.clear();
|
||||
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Sending ID_RECORD_DYNAMIC with armor %s", armor->mName.c_str());
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_RECORD_DYNAMIC with armor %s", armor->mName.c_str());
|
||||
|
||||
recordsType = mwmp::RECORD_TYPE::ARMOR;
|
||||
|
||||
|
@ -319,7 +319,7 @@ void Worldstate::sendBookRecord(const ESM::Book* book, std::string baseId)
|
|||
{
|
||||
bookRecords.clear();
|
||||
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Sending ID_RECORD_DYNAMIC with book %s", book->mName.c_str());
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_RECORD_DYNAMIC with book %s", book->mName.c_str());
|
||||
|
||||
recordsType = mwmp::RECORD_TYPE::BOOK;
|
||||
|
||||
|
@ -339,7 +339,7 @@ void Worldstate::sendClothingRecord(const ESM::Clothing* clothing, std::string b
|
|||
{
|
||||
clothingRecords.clear();
|
||||
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Sending ID_RECORD_DYNAMIC with clothing %s", clothing->mName.c_str());
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_RECORD_DYNAMIC with clothing %s", clothing->mName.c_str());
|
||||
|
||||
recordsType = mwmp::RECORD_TYPE::CLOTHING;
|
||||
|
||||
|
@ -359,7 +359,7 @@ void Worldstate::sendWeaponRecord(const ESM::Weapon* weapon, std::string baseId)
|
|||
{
|
||||
weaponRecords.clear();
|
||||
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Sending ID_RECORD_DYNAMIC with weapon %s", weapon->mName.c_str());
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_RECORD_DYNAMIC with weapon %s", weapon->mName.c_str());
|
||||
|
||||
recordsType = mwmp::RECORD_TYPE::WEAPON;
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@
|
|||
|
||||
#include "WorldstateProcessor.hpp"
|
||||
#include "worldstate/ProcessorCellCreate.hpp"
|
||||
#include "worldstate/ProcessorCellReplace.hpp"
|
||||
#include "worldstate/ProcessorCellReset.hpp"
|
||||
#include "worldstate/ProcessorRecordDynamic.hpp"
|
||||
#include "worldstate/ProcessorWorldCollisionOverride.hpp"
|
||||
#include "worldstate/ProcessorWorldMap.hpp"
|
||||
|
@ -186,7 +186,7 @@ void ProcessorInitializer()
|
|||
ActorProcessor::AddProcessor(new ProcessorActorTest());
|
||||
|
||||
WorldstateProcessor::AddProcessor(new ProcessorCellCreate());
|
||||
WorldstateProcessor::AddProcessor(new ProcessorCellReplace());
|
||||
WorldstateProcessor::AddProcessor(new ProcessorCellReset());
|
||||
WorldstateProcessor::AddProcessor(new ProcessorRecordDynamic());
|
||||
WorldstateProcessor::AddProcessor(new ProcessorWorldCollisionOverride());
|
||||
WorldstateProcessor::AddProcessor(new ProcessorWorldMap());
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace mwmp
|
|||
|
||||
if (!isRequest())
|
||||
{
|
||||
LOG_APPEND(Log::LOG_INFO, "- refId: %s, count: %i, charge: %f, enchantmentCharge: %f, soul: %s",
|
||||
LOG_APPEND(Log::LOG_INFO, "- refId: %s, count: %i, charge: %i, enchantmentCharge: %f, soul: %s",
|
||||
player->usedItem.refId.c_str(), player->usedItem.count, player->usedItem.charge,
|
||||
player->usedItem.enchantmentCharge, player->usedItem.soul.c_str());
|
||||
|
||||
|
@ -36,7 +36,11 @@ namespace mwmp
|
|||
MWWorld::InventoryStore &inventoryStore = playerPtr.getClass().getInventoryStore(playerPtr);
|
||||
|
||||
MWWorld::Ptr itemPtr = MechanicsHelper::getItemPtrFromStore(player->usedItem, inventoryStore);
|
||||
|
||||
if (itemPtr)
|
||||
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->useItem(itemPtr);
|
||||
else
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Cannot use non-existent item %s", player->usedItem.refId.c_str());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
#ifndef OPENMW_PROCESSORCELLREPLACE_HPP
|
||||
#define OPENMW_PROCESSORCELLREPLACE_HPP
|
||||
|
||||
#include "../WorldstateProcessor.hpp"
|
||||
|
||||
namespace mwmp
|
||||
{
|
||||
class ProcessorCellReplace : public WorldstateProcessor
|
||||
{
|
||||
public:
|
||||
ProcessorCellReplace()
|
||||
{
|
||||
BPP_INIT(ID_CELL_REPLACE)
|
||||
}
|
||||
|
||||
virtual void Do(WorldstatePacket &packet, Worldstate &worldstate)
|
||||
{
|
||||
// Placeholder
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //OPENMW_PROCESSORCELLREPLACE_HPP
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef OPENMW_PROCESSORCELLRESET_HPP
|
||||
#define OPENMW_PROCESSORCELLRESET_HPP
|
||||
|
||||
#include "../WorldstateProcessor.hpp"
|
||||
|
||||
namespace mwmp
|
||||
{
|
||||
class ProcessorCellReset : public WorldstateProcessor
|
||||
{
|
||||
public:
|
||||
ProcessorCellReset()
|
||||
{
|
||||
BPP_INIT(ID_CELL_RESET)
|
||||
}
|
||||
|
||||
virtual void Do(WorldstatePacket &packet, Worldstate &worldstate)
|
||||
{
|
||||
// Placeholder
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //OPENMW_PROCESSORCELLRESET_HPP
|
|
@ -80,18 +80,24 @@ namespace MWScript
|
|||
/*
|
||||
Start of tes3mp change (major)
|
||||
|
||||
Disable unilateral item addition on this client and expect the server's reply to our
|
||||
packet to do it instead, except for changes to player inventories which still require
|
||||
the PlayerInventory to be reworked
|
||||
Allow unilateral item removal on this client from client scripts and dialogue (but not console commands)
|
||||
to prevent infinite loops in certain mods. Otherwise, expect the server's reply to our packet to do the
|
||||
removal instead, except for changes to player inventories which still require the PlayerInventory to be
|
||||
reworked.
|
||||
*/
|
||||
// Spawn a messagebox (only for items added to player's inventory and if player is talking to someone)
|
||||
if (ptr == MWBase::Environment::get().getWorld ()->getPlayerPtr() )
|
||||
{
|
||||
MWWorld::Ptr itemPtr = *ptr.getClass().getContainerStore(ptr).add(item, count, ptr);
|
||||
unsigned char packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());
|
||||
|
||||
MWWorld::Ptr itemPtr;
|
||||
|
||||
if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr() || packetOrigin != mwmp::CLIENT_CONSOLE)
|
||||
itemPtr = *ptr.getClass().getContainerStore(ptr).add(item, count, ptr);
|
||||
/*
|
||||
End of tes3mp change (major)
|
||||
*/
|
||||
|
||||
// Spawn a messagebox (only for items added to player's inventory and if player is talking to someone)
|
||||
if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr())
|
||||
{
|
||||
// The two GMST entries below expand to strings informing the player of what, and how many of it has been added to their inventory
|
||||
std::string msgBox;
|
||||
std::string itemName = itemPtr.getClass().getName(itemPtr);
|
||||
|
@ -113,11 +119,12 @@ namespace MWScript
|
|||
Send an ID_CONTAINER packet every time an item is added to a Ptr
|
||||
that doesn't belong to a DedicatedPlayer
|
||||
*/
|
||||
else if (!ptr.getClass().isActor() || !mwmp::PlayerList::isDedicatedPlayer(ptr))
|
||||
else if (mwmp::Main::get().getLocalPlayer()->isLoggedIn() &&
|
||||
(!ptr.getClass().isActor() || !mwmp::PlayerList::isDedicatedPlayer(ptr)))
|
||||
{
|
||||
mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();
|
||||
objectList->reset();
|
||||
objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());
|
||||
objectList->packetOrigin = packetOrigin;
|
||||
objectList->cell = *ptr.getCell()->getCell();
|
||||
objectList->action = mwmp::BaseObjectList::ADD;
|
||||
objectList->containerSubAction = mwmp::BaseObjectList::NONE;
|
||||
|
@ -202,13 +209,15 @@ namespace MWScript
|
|||
/*
|
||||
Start of tes3mp change (major)
|
||||
|
||||
Disable unilateral item removal on this client and expect the server's reply to our
|
||||
packet to do it instead, except for changes to player inventories which still require
|
||||
the PlayerInventory to be reworked
|
||||
Allow unilateral item removal on this client from client scripts and dialogue (but not console commands)
|
||||
to prevent infinite loops in certain mods. Otherwise, expect the server's reply to our packet to do the
|
||||
removal instead, except for changes to player inventories which still require the PlayerInventory to be
|
||||
reworked.
|
||||
*/
|
||||
unsigned char packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());
|
||||
int numRemoved = 0;
|
||||
|
||||
if (ptr == MWMechanics::getPlayer())
|
||||
if (ptr == MWMechanics::getPlayer() || packetOrigin != mwmp::CLIENT_CONSOLE)
|
||||
numRemoved = store.remove(item, count, ptr);
|
||||
|
||||
// Spawn a messagebox (only for items removed from player's inventory)
|
||||
|
@ -240,11 +249,12 @@ namespace MWScript
|
|||
Send an ID_CONTAINER packet every time an item is removed from a Ptr
|
||||
that doesn't belong to a DedicatedPlayer
|
||||
*/
|
||||
else if (!ptr.getClass().isActor() || !mwmp::PlayerList::isDedicatedPlayer(ptr))
|
||||
else if (mwmp::Main::get().getLocalPlayer()->isLoggedIn() &&
|
||||
(!ptr.getClass().isActor() || !mwmp::PlayerList::isDedicatedPlayer(ptr)))
|
||||
{
|
||||
mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();
|
||||
objectList->reset();
|
||||
objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());
|
||||
objectList->packetOrigin = packetOrigin;
|
||||
objectList->cell = *ptr.getCell()->getCell();
|
||||
objectList->action = mwmp::BaseObjectList::REMOVE;
|
||||
objectList->containerSubAction = mwmp::BaseObjectList::NONE;
|
||||
|
|
|
@ -63,7 +63,7 @@ namespace MWScript
|
|||
Send an ID_PLAYER_JOURNAL packet every time a new journal entry is added
|
||||
through a script
|
||||
*/
|
||||
if (!MWBase::Environment::get().getJournal()->hasEntry(quest, index))
|
||||
if (mwmp::Main::get().getLocalPlayer()->isLoggedIn() && !MWBase::Environment::get().getJournal()->hasEntry(quest, index))
|
||||
mwmp::Main::get().getLocalPlayer()->sendJournalEntry(quest, index, ptr);
|
||||
/*
|
||||
End of tes3mp addition
|
||||
|
@ -99,6 +99,7 @@ namespace MWScript
|
|||
Send an ID_PLAYER_JOURNAL packet every time a journal index is set
|
||||
through a script
|
||||
*/
|
||||
if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())
|
||||
mwmp::Main::get().getLocalPlayer()->sendJournalIndex(quest, index);
|
||||
/*
|
||||
End of tes3mp addition
|
||||
|
@ -137,7 +138,8 @@ namespace MWScript
|
|||
Send an ID_PLAYER_TOPIC packet every time a new topic is added
|
||||
through a script
|
||||
*/
|
||||
if (MWBase::Environment::get().getDialogueManager()->isNewTopic(Misc::StringUtils::lowerCase(topic)))
|
||||
if (mwmp::Main::get().getLocalPlayer()->isLoggedIn() &&
|
||||
MWBase::Environment::get().getDialogueManager()->isNewTopic(Misc::StringUtils::lowerCase(topic)))
|
||||
mwmp::Main::get().getLocalPlayer()->sendTopic(Misc::StringUtils::lowerCase(topic));
|
||||
/*
|
||||
End of tes3mp addition
|
||||
|
|
|
@ -613,13 +613,17 @@ namespace MWScript
|
|||
Start of tes3mp addition
|
||||
|
||||
Send an ID_OBJECT_STATE packet whenever an object is enabled, as long as
|
||||
the player has finished character generation and the object wasn't already
|
||||
enabled previously
|
||||
the player is logged in on the server, the object is still disabled, and our last
|
||||
packet regarding its state did not already attempt to enable it (to prevent
|
||||
packet spam)
|
||||
*/
|
||||
if (mwmp::Main::get().getLocalPlayer()->hasFinishedCharGen())
|
||||
if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())
|
||||
{
|
||||
if (ref.isInCell() && !ref.getRefData().isEnabled())
|
||||
if (ref.isInCell() && !ref.getRefData().isEnabled() &&
|
||||
ref.getRefData().getLastCommunicatedState() != MWWorld::RefData::StateCommunication::Enabled)
|
||||
{
|
||||
ref.getRefData().setLastCommunicatedState(MWWorld::RefData::StateCommunication::Enabled);
|
||||
|
||||
mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();
|
||||
objectList->reset();
|
||||
objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(getContextType());
|
||||
|
@ -650,14 +654,18 @@ namespace MWScript
|
|||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
Send an ID_OBJECT_STATE packet whenever an object is disabled, as long as
|
||||
the player has finished character generation and the object wasn't already
|
||||
disabled previously
|
||||
Send an ID_OBJECT_STATE packet whenever an object should be disabled, as long as
|
||||
the player is logged in on the server, the object is still enabled, and our last
|
||||
packet regarding its state did not already attempt to disable it (to prevent
|
||||
packet spam)
|
||||
*/
|
||||
if (mwmp::Main::get().getLocalPlayer()->hasFinishedCharGen())
|
||||
if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())
|
||||
{
|
||||
if (ref.isInCell() && ref.getRefData().isEnabled())
|
||||
if (ref.isInCell() && ref.getRefData().isEnabled() &&
|
||||
ref.getRefData().getLastCommunicatedState() != MWWorld::RefData::StateCommunication::Disabled)
|
||||
{
|
||||
ref.getRefData().setLastCommunicatedState(MWWorld::RefData::StateCommunication::Disabled);
|
||||
|
||||
mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();
|
||||
objectList->reset();
|
||||
objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(getContextType());
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
*/
|
||||
#include "../mwmp/Main.hpp"
|
||||
#include "../mwmp/Networking.hpp"
|
||||
#include "../mwmp/LocalPlayer.hpp"
|
||||
#include "../mwmp/ObjectList.hpp"
|
||||
#include "../mwmp/ScriptController.hpp"
|
||||
/*
|
||||
|
@ -105,11 +106,14 @@ namespace MWScript
|
|||
Send an ID_VIDEO_PLAY packet every time a video is played
|
||||
through a script
|
||||
*/
|
||||
if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())
|
||||
{
|
||||
mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();
|
||||
objectList->reset();
|
||||
objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());
|
||||
objectList->addVideoPlay(name, allowSkipping);
|
||||
objectList->sendVideoPlay();
|
||||
}
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
@ -216,11 +220,14 @@ namespace MWScript
|
|||
Send an ID_OBJECT_LOCK packet every time an object is locked
|
||||
through a script
|
||||
*/
|
||||
if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())
|
||||
{
|
||||
mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();
|
||||
objectList->reset();
|
||||
objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());
|
||||
objectList->addObjectLock(ptr, lockLevel);
|
||||
objectList->sendObjectLock();
|
||||
}
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
@ -266,11 +273,14 @@ namespace MWScript
|
|||
Send an ID_OBJECT_LOCK packet every time an object is unlocked
|
||||
through a script
|
||||
*/
|
||||
if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())
|
||||
{
|
||||
mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();
|
||||
objectList->reset();
|
||||
objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());
|
||||
objectList->addObjectLock(ptr, 0);
|
||||
objectList->sendObjectLock();
|
||||
}
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
@ -768,13 +778,20 @@ namespace MWScript
|
|||
Start of tes3mp addition
|
||||
|
||||
Send an ID_OBJECT_DELETE packet every time an object is deleted
|
||||
through a script
|
||||
through a script, as long as we haven't already communicated
|
||||
a deletion for it
|
||||
*/
|
||||
if (mwmp::Main::get().getLocalPlayer()->isLoggedIn() &&
|
||||
ptr.getRefData().getLastCommunicatedState() != MWWorld::RefData::StateCommunication::Deleted)
|
||||
{
|
||||
ptr.getRefData().setLastCommunicatedState(MWWorld::RefData::StateCommunication::Deleted);
|
||||
|
||||
mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();
|
||||
objectList->reset();
|
||||
objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());
|
||||
objectList->addObjectDelete(ptr);
|
||||
objectList->sendObjectDelete();
|
||||
}
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
|
|
@ -470,18 +470,24 @@ namespace MWScript
|
|||
// make sure a spell with this ID actually exists.
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find (id);
|
||||
|
||||
ptr.getClass().getCreatureStats (ptr).getSpells().add (id);
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
Start of tes3mp change (major)
|
||||
|
||||
Send an ID_PLAYER_SPELLBOOK packet every time a player gains a spell
|
||||
through a script
|
||||
Only add the spell if the target doesn't already have it
|
||||
|
||||
Send an ID_PLAYER_SPELLBOOK packet every time a player gains a spell here
|
||||
*/
|
||||
if (ptr == MWMechanics::getPlayer())
|
||||
MWMechanics::Spells &spells = ptr.getClass().getCreatureStats(ptr).getSpells();
|
||||
|
||||
if (!spells.hasSpell(id))
|
||||
{
|
||||
spells.add(id);
|
||||
|
||||
if (mwmp::Main::get().getLocalPlayer()->isLoggedIn() && ptr == MWMechanics::getPlayer())
|
||||
mwmp::Main::get().getLocalPlayer()->sendSpellChange(id, mwmp::SpellbookChanges::ADD);
|
||||
}
|
||||
/*
|
||||
End of tes3mp addition
|
||||
End of tes3mp change (major)
|
||||
*/
|
||||
}
|
||||
};
|
||||
|
@ -498,26 +504,32 @@ namespace MWScript
|
|||
std::string id = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
|
||||
ptr.getClass().getCreatureStats (ptr).getSpells().remove (id);
|
||||
/*
|
||||
Start of tes3mp change (major)
|
||||
|
||||
Only remove the spell if the target has it
|
||||
|
||||
Send an ID_PLAYER_SPELLBOOK packet every time a player loses a spell here
|
||||
*/
|
||||
MWMechanics::Spells &spells = ptr.getClass().getCreatureStats(ptr).getSpells();
|
||||
|
||||
if (spells.hasSpell(id))
|
||||
{
|
||||
ptr.getClass().getCreatureStats(ptr).getSpells().remove(id);
|
||||
|
||||
if (ptr == MWMechanics::getPlayer())
|
||||
{
|
||||
MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager();
|
||||
|
||||
if (ptr == MWMechanics::getPlayer() &&
|
||||
id == wm->getSelectedSpell())
|
||||
{
|
||||
if (id == wm->getSelectedSpell())
|
||||
wm->unsetSelectedSpell();
|
||||
}
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
Send an ID_PLAYER_SPELLBOOK packet every time a player loses a spell
|
||||
through a script
|
||||
*/
|
||||
if (ptr == MWMechanics::getPlayer())
|
||||
if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())
|
||||
mwmp::Main::get().getLocalPlayer()->sendSpellChange(id, mwmp::SpellbookChanges::REMOVE);
|
||||
}
|
||||
}
|
||||
/*
|
||||
End of tes3mp addition
|
||||
End of tes3mp change (major)
|
||||
*/
|
||||
}
|
||||
};
|
||||
|
|
|
@ -63,15 +63,14 @@ namespace MWScript
|
|||
|
||||
Prevent players from changing their own scale
|
||||
|
||||
Send an ID_OBJECT_SCALE every time an object's
|
||||
scale is changed through a script
|
||||
Send an ID_OBJECT_SCALE every time an object's scale is changed through a script
|
||||
*/
|
||||
if (ptr == MWMechanics::getPlayer())
|
||||
{
|
||||
MWBase::Environment::get().getWindowManager()->
|
||||
messageBox("You can't change your own scale in multiplayer. Only the server can.");
|
||||
}
|
||||
else if (ptr.isInCell() && ptr.getCellRef().getScale() != scale)
|
||||
else if (mwmp::Main::get().getLocalPlayer()->isLoggedIn() && ptr.isInCell() && ptr.getCellRef().getScale() != scale)
|
||||
{
|
||||
// Ignore attempts to change another player's scale
|
||||
if (mwmp::PlayerList::isDedicatedPlayer(ptr))
|
||||
|
@ -504,6 +503,44 @@ namespace MWScript
|
|||
ref.getPtr().getCellRef().setPosition(pos);
|
||||
MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->placeObject(ref.getPtr(),store,pos);
|
||||
placed.getClass().adjustPosition(placed, true);
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
Send an ID_OBJECT_PLACE or ID_OBJECT_SPAWN packet every time an object is placed
|
||||
in the world through a script
|
||||
*/
|
||||
if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())
|
||||
{
|
||||
mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();
|
||||
objectList->reset();
|
||||
objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());
|
||||
|
||||
if (placed.getClass().isActor())
|
||||
{
|
||||
objectList->addObjectSpawn(placed);
|
||||
objectList->sendObjectSpawn();
|
||||
}
|
||||
else
|
||||
{
|
||||
objectList->addObjectPlace(placed);
|
||||
objectList->sendObjectPlace();
|
||||
}
|
||||
}
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
/*
|
||||
Start of tes3mp change (major)
|
||||
|
||||
Instead of actually keeping this object as is, delete it after sending the packet
|
||||
and wait for the server to send it back with a unique mpNum of its own
|
||||
*/
|
||||
MWBase::Environment::get().getWorld()->deleteObject(placed);
|
||||
/*
|
||||
End of tes3mp change (major)
|
||||
*/
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -552,6 +589,44 @@ namespace MWScript
|
|||
ref.getPtr().getCellRef().setPosition(pos);
|
||||
MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->placeObject(ref.getPtr(),store,pos);
|
||||
placed.getClass().adjustPosition(placed, true);
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
Send an ID_OBJECT_PLACE or ID_OBJECT_SPAWN packet every time an object is placed
|
||||
in the world through a script
|
||||
*/
|
||||
if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())
|
||||
{
|
||||
mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();
|
||||
objectList->reset();
|
||||
objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());
|
||||
|
||||
if (placed.getClass().isActor())
|
||||
{
|
||||
objectList->addObjectSpawn(placed);
|
||||
objectList->sendObjectSpawn();
|
||||
}
|
||||
else
|
||||
{
|
||||
objectList->addObjectPlace(placed);
|
||||
objectList->sendObjectPlace();
|
||||
}
|
||||
}
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
/*
|
||||
Start of tes3mp change (major)
|
||||
|
||||
Instead of actually keeping this object as is, delete it after sending the packet
|
||||
and wait for the server to send it back with a unique mpNum of its own
|
||||
*/
|
||||
MWBase::Environment::get().getWorld()->deleteObject(placed);
|
||||
/*
|
||||
End of tes3mp change (major)
|
||||
*/
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -599,6 +674,8 @@ namespace MWScript
|
|||
Send an ID_OBJECT_PLACE or ID_OBJECT_SPAWN packet every time an object is placed
|
||||
in the world through a script
|
||||
*/
|
||||
if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())
|
||||
{
|
||||
mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();
|
||||
objectList->reset();
|
||||
objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(runtime.getContext().getContextType());
|
||||
|
@ -613,6 +690,7 @@ namespace MWScript
|
|||
objectList->addObjectPlace(ptr);
|
||||
objectList->sendObjectPlace();
|
||||
}
|
||||
}
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
|
|
@ -194,6 +194,25 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::unstack(const Ptr &ptr,
|
|||
if (ptr.getRefData().getCount() <= count)
|
||||
return end();
|
||||
MWWorld::ContainerStoreIterator it = addNewStack(ptr, ptr.getRefData().getCount()-count);
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
Send an ID_PLAYER_INVENTORY packet every time an item stack gets added for a player here
|
||||
*/
|
||||
Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
|
||||
if (container == player && this == &player.getClass().getContainerStore(player))
|
||||
{
|
||||
mwmp::LocalPlayer *localPlayer = mwmp::Main::get().getLocalPlayer();
|
||||
|
||||
if (!localPlayer->isReceivingInventory)
|
||||
localPlayer->sendItemChange(ptr, ptr.getRefData().getCount() - count, mwmp::InventoryChanges::ADD);
|
||||
}
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
const std::string script = it->getClass().getScript(*it);
|
||||
if (!script.empty())
|
||||
MWBase::Environment::get().getWorld()->getLocalScripts().add(script, *it);
|
||||
|
@ -366,8 +385,18 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr
|
|||
item.getRefData().getLocals().setVarByInt(script, "onpcadd", 1);
|
||||
}
|
||||
|
||||
if (mListener)
|
||||
mListener->itemAdded(item, count);
|
||||
/*
|
||||
Start of tes3mp change (major)
|
||||
|
||||
Disable the listener here because it keeps causing crashes; this should only be
|
||||
a temporary solution that doesn't affect much anyway given that the listener is
|
||||
only used in relation to light-emitting items
|
||||
*/
|
||||
//if (mListener)
|
||||
// mListener->itemAdded(item, count);
|
||||
/*
|
||||
End of tes3mp change (major)
|
||||
*/
|
||||
|
||||
return it;
|
||||
}
|
||||
|
@ -493,8 +522,18 @@ int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor
|
|||
|
||||
flagAsModified();
|
||||
|
||||
if (mListener)
|
||||
mListener->itemRemoved(item, count - toRemove);
|
||||
/*
|
||||
Start of tes3mp change (major)
|
||||
|
||||
Disable the listener here because it keeps causing crashes; this should only be
|
||||
a temporary solution that doesn't affect much anyway given that the listener is
|
||||
only used in relation to light-emitting items
|
||||
*/
|
||||
//if (mListener)
|
||||
// mListener->itemRemoved(item, count - toRemove);
|
||||
/*
|
||||
End of tes3mp change (major)
|
||||
*/
|
||||
|
||||
// number of removed items
|
||||
return count - toRemove;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
Include additional headers for multiplayer purposes
|
||||
*/
|
||||
#include <components/openmw-mp/Log.hpp>
|
||||
#include "../mwmp/Main.hpp"
|
||||
#include "../mwmp/CellController.hpp"
|
||||
#include "../mwmp/PlayerList.hpp"
|
||||
|
@ -251,7 +252,19 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::findSlot (int slot) con
|
|||
{
|
||||
// Object has been deleted
|
||||
// This should no longer happen, since the new remove function will unequip first
|
||||
throw std::runtime_error("Invalid slot, make sure you are not calling RefData::setCount for a container object");
|
||||
|
||||
/*
|
||||
Start of tes3mp change (major)
|
||||
|
||||
Instead of throwing an error, display an error log message with information about
|
||||
the item
|
||||
*/
|
||||
//throw std::runtime_error("Invalid slot, make sure you are not calling RefData::setCount for a container object");
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Invalid slot, make sure you are not calling RefData::setCount for a container object\n- item was %s",
|
||||
mSlots[slot]->getCellRef().getRefId().c_str());
|
||||
/*
|
||||
End of tes3mp change (major)
|
||||
*/
|
||||
}
|
||||
|
||||
return mSlots[slot];
|
||||
|
|
|
@ -138,6 +138,35 @@ namespace MWWorld
|
|||
|
||||
const ESM::AnimationState& getAnimationState() const;
|
||||
ESM::AnimationState& getAnimationState();
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
Track the last state communicated to the server for this reference,
|
||||
to avoid packet spam when the server denies our state change request or
|
||||
is slow to reply
|
||||
*/
|
||||
enum StateCommunication
|
||||
{
|
||||
None = 0,
|
||||
Enabled = 1,
|
||||
Disabled = 2,
|
||||
Deleted = 3
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
short mLastCommunicatedState = StateCommunication::None;
|
||||
|
||||
public:
|
||||
|
||||
short getLastCommunicatedState() { return mLastCommunicatedState; };
|
||||
|
||||
void setLastCommunicatedState(short communicationState) { mLastCommunicatedState = communicationState; };
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -489,9 +489,9 @@ namespace MWWorld
|
|||
Start of tes3mp addition
|
||||
|
||||
Send an ID_PLAYER_CELL_STATE packet with all cell states stored in LocalPlayer
|
||||
and then clear them, but only if the player has finished character generation
|
||||
and then clear them, but only if the player is logged in on the server
|
||||
*/
|
||||
if (mwmp::Main::get().getLocalPlayer()->hasFinishedCharGen())
|
||||
if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())
|
||||
{
|
||||
mwmp::Main::get().getLocalPlayer()->sendCellStates();
|
||||
mwmp::Main::get().getLocalPlayer()->clearCellStates();
|
||||
|
@ -631,9 +631,9 @@ namespace MWWorld
|
|||
Start of tes3mp addition
|
||||
|
||||
Send an ID_PLAYER_CELL_STATE packet with all cell states stored in LocalPlayer
|
||||
and then clear them, but only if the player has finished character generation
|
||||
and then clear them, but only if the player is logged in on the server
|
||||
*/
|
||||
if (mwmp::Main::get().getLocalPlayer()->hasFinishedCharGen())
|
||||
if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())
|
||||
{
|
||||
mwmp::Main::get().getLocalPlayer()->sendCellStates();
|
||||
mwmp::Main::get().getLocalPlayer()->clearCellStates();
|
||||
|
|
|
@ -282,24 +282,16 @@ namespace MWWorld
|
|||
/*
|
||||
Start of tes3mp change (major)
|
||||
|
||||
If Pelagiad exists, spawn there; otherwise, spawn at 0 ,0
|
||||
Spawn at 0, -7 by default
|
||||
*/
|
||||
if (findExteriorPosition("Pelagiad", pos))
|
||||
{
|
||||
changeToExteriorCell(pos, true);
|
||||
fixPosition(getPlayerPtr());
|
||||
}
|
||||
else
|
||||
{
|
||||
const int cellSize = 8192;
|
||||
pos.pos[0] = cellSize / 2;
|
||||
pos.pos[1] = cellSize / 2;
|
||||
pos.pos[1] = cellSize * -7 + cellSize / 2;
|
||||
pos.pos[2] = 0;
|
||||
pos.rot[0] = 0;
|
||||
pos.rot[1] = 0;
|
||||
pos.rot[2] = 0;
|
||||
mWorldScene->changeToExteriorCell(pos, true);
|
||||
}
|
||||
/*
|
||||
End of tes3mp change (major)
|
||||
*/
|
||||
|
@ -3130,6 +3122,18 @@ namespace MWWorld
|
|||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
Always start spells cast by DedicatedPlayers and DedicatedActors,
|
||||
without unilaterally deducting any magicka for them on this client
|
||||
*/
|
||||
if (mwmp::PlayerList::isDedicatedPlayer(actor) || mwmp::Main::get().getCellController()->isDedicatedActor(actor))
|
||||
return true;
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
|
||||
const ESM::Spell* spell = getStore().get<ESM::Spell>().find(selectedSpell);
|
||||
|
||||
// Check mana
|
||||
|
@ -3266,10 +3270,11 @@ namespace MWWorld
|
|||
|
||||
If this actor is a LocalPlayer or LocalActor, get their Attack and prepare
|
||||
it for sending
|
||||
|
||||
Set the attack details before going through with the casting, in case it's
|
||||
a one use item that would get removed through the casting (like a scroll)
|
||||
*/
|
||||
{
|
||||
cast.cast(*inv.getSelectedEnchantItem());
|
||||
|
||||
mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(actor);
|
||||
|
||||
if (localAttack)
|
||||
|
@ -3279,6 +3284,8 @@ namespace MWWorld
|
|||
localAttack->itemId = inv.getSelectedEnchantItem()->getCellRef().getRefId();
|
||||
localAttack->shouldSend = true;
|
||||
}
|
||||
|
||||
cast.cast(*inv.getSelectedEnchantItem());
|
||||
}
|
||||
/*
|
||||
End of tes3mp addition
|
||||
|
|
|
@ -202,7 +202,7 @@ add_component_dir (openmw-mp/Packets/Object
|
|||
add_component_dir (openmw-mp/Packets/Worldstate
|
||||
WorldstatePacket
|
||||
|
||||
PacketCellCreate PacketCellReplace PacketRecordDynamic PacketWorldCollisionOverride PacketWorldMap
|
||||
PacketCellCreate PacketCellReset PacketRecordDynamic PacketWorldCollisionOverride PacketWorldMap
|
||||
PacketWorldRegionAuthority PacketWorldTime PacketWorldWeather
|
||||
)
|
||||
|
||||
|
|
|
@ -232,6 +232,8 @@ namespace mwmp
|
|||
}
|
||||
|
||||
RakNet::RakNetGUID guid;
|
||||
std::string serverPassword;
|
||||
|
||||
GUIMessageBox guiMessageBox;
|
||||
|
||||
// Track only the indexes of the attributes that have been changed,
|
||||
|
@ -295,7 +297,6 @@ namespace mwmp
|
|||
std::string birthsign;
|
||||
std::string chatMessage;
|
||||
CharGenState charGenState;
|
||||
std::string passw;
|
||||
|
||||
std::string sound;
|
||||
Animation animation;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include "../Packets/Worldstate/PacketCellCreate.hpp"
|
||||
#include "../Packets/Worldstate/PacketCellReplace.hpp"
|
||||
#include "../Packets/Worldstate/PacketCellReset.hpp"
|
||||
#include "../Packets/Worldstate/PacketRecordDynamic.hpp"
|
||||
#include "../Packets/Worldstate/PacketWorldCollisionOverride.hpp"
|
||||
#include "../Packets/Worldstate/PacketWorldMap.hpp"
|
||||
|
@ -20,7 +20,7 @@ inline void AddPacket(mwmp::WorldstatePacketController::packets_t *packets, RakN
|
|||
mwmp::WorldstatePacketController::WorldstatePacketController(RakNet::RakPeerInterface *peer)
|
||||
{
|
||||
AddPacket<PacketCellCreate>(&packets, peer);
|
||||
AddPacket<PacketCellReplace>(&packets, peer);
|
||||
AddPacket<PacketCellReset>(&packets, peer);
|
||||
AddPacket<PacketRecordDynamic>(&packets, peer);
|
||||
AddPacket<PacketWorldCollisionOverride>(&packets, peer);
|
||||
AddPacket<PacketWorldMap>(&packets, peer);
|
||||
|
|
|
@ -104,7 +104,7 @@ enum GameMessages
|
|||
ID_GAME_PREINIT,
|
||||
|
||||
ID_CELL_CREATE,
|
||||
ID_CELL_REPLACE,
|
||||
ID_CELL_RESET,
|
||||
ID_RECORD_DYNAMIC,
|
||||
ID_WORLD_COLLISION_OVERRIDE,
|
||||
ID_WORLD_MAP,
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
//
|
||||
// Created by koncord on 28.04.16.
|
||||
//
|
||||
|
||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
#include "PacketHandshake.hpp"
|
||||
|
||||
|
@ -17,8 +13,8 @@ void PacketHandshake::Packet(RakNet::BitStream *bs, bool send)
|
|||
{
|
||||
PlayerPacket::Packet(bs, send);
|
||||
|
||||
if (!RW(player->npc.mName, send, true, maxNameLen) ||
|
||||
!RW(player->passw, send, true, maxPasswLen))
|
||||
if (!RW(player->npc.mName, send, true, maxNameLength) ||
|
||||
!RW(player->serverPassword, send, true, maxPasswordLength))
|
||||
{
|
||||
packetValid = false;
|
||||
return;
|
||||
|
|
|
@ -16,8 +16,8 @@ namespace mwmp
|
|||
|
||||
virtual void Packet(RakNet::BitStream *bs, bool send);
|
||||
|
||||
const static uint32_t maxNameLen = 256;
|
||||
const static uint32_t maxPasswLen = 256;
|
||||
const static uint32_t maxNameLength = 256;
|
||||
const static uint32_t maxPasswordLength = 256;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue