forked from mirror/openmw-tes3mp
Compare commits
314 commits
0.7.0
...
sol2-serve
Author | SHA1 | Date | |
---|---|---|---|
|
9029533ec1 | ||
|
7fadc533c7 | ||
|
cf33695447 | ||
|
dbd6ccaf97 | ||
|
702d5b44c9 | ||
|
8b482e19ec | ||
|
d5b3d08ec9 | ||
|
524363702c | ||
|
b3c398605f | ||
|
176aa62b15 | ||
|
6bb2a7e24a | ||
|
058d67dbd0 | ||
|
49bf605e8e | ||
|
07b781cd32 | ||
|
08915c44a0 | ||
|
e12330b038 | ||
|
5c41350532 | ||
|
fc4d3fe3fa | ||
|
569243c987 | ||
|
7180b009bd | ||
|
3f7e163c92 | ||
|
7083cb0359 | ||
|
ad4214d3e2 | ||
|
83fe29d10f | ||
|
3a68f4bd8f | ||
|
8a393d2984 | ||
|
b5922d18fd | ||
|
98eece808b | ||
|
d4dbfdfdb6 | ||
|
672bb707a7 | ||
|
7c8dd7380f | ||
|
c5f33e451f | ||
|
4846497078 | ||
|
502751cae0 | ||
|
d58efde3f1 | ||
|
e355dca4dd | ||
|
ed15d9ebf5 | ||
|
2390744b45 | ||
|
c3b44e11fb | ||
|
f50637bdd4 | ||
|
6131ada0ba | ||
|
5bb09d3bed | ||
|
2aaf9105af | ||
|
3d5860d6f4 | ||
|
7ec08e125b | ||
|
b2a3dd9d60 | ||
|
2ac01dc02a | ||
|
4aff1f1833 | ||
|
017956366f | ||
|
afbafdf806 | ||
|
d0eef7c98e | ||
|
7deff7a42a | ||
|
23da0b16ea | ||
|
6f7771d97e | ||
|
24ba4ae404 | ||
|
73d030b779 | ||
|
4e869a2974 | ||
|
e85d0db771 | ||
|
44c549211e | ||
|
2bfd4627ed | ||
|
9dae748a76 | ||
|
bb7c5ee34c | ||
|
54945b537d | ||
|
4bde7d80f5 | ||
|
f2a88e6a37 | ||
|
410eb353e8 | ||
|
a9614ad28e | ||
|
69436714f9 | ||
|
3b865244d0 | ||
|
4e9cac96c7 | ||
|
ac374a8ef9 | ||
|
bdafa8e9ab | ||
|
f1ba9253b0 | ||
|
5858e05362 | ||
|
1a8a518897 | ||
|
569911121d | ||
|
5cf71a4e67 | ||
|
d70b93e095 | ||
|
6197636fac | ||
|
edd883853d | ||
|
0e97b769f9 | ||
|
4cfb04aa7f | ||
|
7e5b929ea2 | ||
|
29ba07fe8c | ||
|
de0bb3cdab | ||
|
cb86557aca | ||
|
0780329a59 | ||
|
1f1cbf53f9 | ||
|
b63bf258ff | ||
|
c2578918f2 | ||
|
e2e197d84a | ||
|
f1e2bc01f6 | ||
|
1e25a122c9 | ||
|
a037193e79 | ||
|
05fac2f67d | ||
|
043eb224e2 | ||
|
35b771b19e | ||
|
92060bd6b6 | ||
|
1de9f30449 | ||
|
45d3b24c17 | ||
|
7248a5d037 | ||
|
ba4d2bd5fe | ||
|
b6a7377692 | ||
|
fcd4d8b842 | ||
|
e2103d0bea | ||
|
35922e4898 | ||
|
ef0384b296 | ||
|
5b8f4f3e92 | ||
|
647661daf9 | ||
|
2f6e3b4cda | ||
|
c4949ac5d9 | ||
|
8f5d31cb03 | ||
|
4ab338bbb1 | ||
|
5777759aae | ||
|
2019128d92 | ||
|
51a92bcf8f | ||
|
09958681cd | ||
|
8bb764c6d6 | ||
|
dddd2f1cc7 | ||
|
a84c4c7ecc | ||
|
f2eca2566f | ||
|
077a3d06b3 | ||
|
f9c4b847aa | ||
|
c5388e49f2 | ||
|
ba07d7820f | ||
|
77d14211c9 | ||
|
ecbe0127b0 | ||
|
fd721143e2 | ||
|
25b7095396 | ||
|
44dc153ebe | ||
|
1ef6ad6215 | ||
|
aff1859759 | ||
|
122a30c183 | ||
|
2726d94d10 | ||
|
d98bb74b80 | ||
|
bfdf348a6c | ||
|
f11473da87 | ||
|
4d0072a74c | ||
|
cd620e17ec | ||
|
dd352f0a91 | ||
|
f35d35741e | ||
|
585c24cee8 | ||
|
ff8b5061b4 | ||
|
e97dac7793 | ||
|
7748e582a8 | ||
|
2cb0ea20f0 | ||
|
de77ee3126 | ||
|
ad61d88cb1 | ||
|
051f65a4d5 | ||
|
7eecbfd08e | ||
|
1c7330635b | ||
|
392e645fe5 | ||
|
91398c5dcc | ||
|
aa183e6844 | ||
|
c55f0f73b8 | ||
|
85a9181c12 | ||
|
94f3eaa980 | ||
|
456bcee68a | ||
|
cfb5835e17 | ||
|
cd03d59056 | ||
|
2bc79fcdf4 | ||
|
c2f330d4f1 | ||
|
fc5e883160 | ||
|
993cc3dfd6 | ||
|
d1ad0c91f8 | ||
|
7e322f1f8b | ||
|
bd9e8bd10f | ||
|
711bdf187a | ||
|
720ef5f6c5 | ||
|
eff3504b05 | ||
|
b55bb445dd | ||
|
a546d99000 | ||
|
1841562553 | ||
|
744b8cf168 | ||
|
e44fcdc0b3 | ||
|
901fe72471 | ||
|
a796f81444 | ||
|
46d55816b8 | ||
|
4ebfcc4a21 | ||
|
729f6e745e | ||
|
fef3764eb1 | ||
|
382f56178c | ||
|
e657934cef | ||
|
c276ff4bd9 | ||
|
0a35b897be | ||
|
b0965f094a | ||
|
ef79a98544 | ||
|
64b57983f0 | ||
|
606ddff813 | ||
|
cac4684986 | ||
|
07d75abdf8 | ||
|
1ee460bba8 | ||
|
d33254f287 | ||
|
010a80ceca | ||
|
947b3f76be | ||
|
6f822f54aa | ||
|
a3e2ab4d4e | ||
|
4cc0216e0a | ||
|
80be664139 | ||
|
57a0415ba3 | ||
|
494b10b97e | ||
|
ba161ddddd | ||
|
7788821a69 | ||
|
068f733d1e | ||
|
1272b03f25 | ||
|
b4e8560698 | ||
|
926106cf8c | ||
|
ac7a588632 | ||
|
a21f5d18d6 | ||
|
a8261bb385 | ||
|
700e4d032e | ||
|
4dbada69bf | ||
|
ca7f3f7450 | ||
|
64b531aa3c | ||
|
f377164db9 | ||
|
7ab01b66e4 | ||
|
d15c674584 | ||
|
0da44f69ad | ||
|
062d6a1824 | ||
|
29cb51cdce | ||
|
1d111fdca8 | ||
|
bd7082f57e | ||
|
71c921faa7 | ||
|
5653d07c7b | ||
|
948090676a | ||
|
61db22f5ae | ||
|
b7e5e77166 | ||
|
378d30834b | ||
|
14d47213ef | ||
|
3495fd43f4 | ||
|
e7a5919477 | ||
|
1aa630e4a9 | ||
|
e8915f8ec5 | ||
|
14fdec2478 | ||
|
b801cf2c9e | ||
|
878294e4fe | ||
|
d44848ecbb | ||
|
05abb8ace3 | ||
|
04a844a9c0 | ||
|
dad0b38f25 | ||
|
a3d5fbbdcd | ||
|
916ada108f | ||
|
bece095579 | ||
|
d6dc75e94b | ||
|
76a4abd7c0 | ||
|
0e73571111 | ||
|
8a93631e08 | ||
|
ba8613a179 | ||
|
fb67180809 | ||
|
4530370e52 | ||
|
ce6a4e4032 | ||
|
fc3f2483ee | ||
|
dffd3bfa7d | ||
|
7a0b45d456 | ||
|
66283943c5 | ||
|
d702845026 | ||
|
0a0c9893b1 | ||
|
6e7c033a5d | ||
|
bbac26294f | ||
|
b20df4b7cd | ||
|
17a8e32782 | ||
|
1fd16ba69c | ||
|
62588ce088 | ||
|
6cb9c3c713 | ||
|
3839a2dcfd | ||
|
ed75563a94 | ||
|
01a5196a92 | ||
|
15723adb9a | ||
|
57353cdfff | ||
|
fe9a3088bd | ||
|
5c79e7106f | ||
|
4845599bda | ||
|
7a38a0b223 | ||
|
cbabc91b06 | ||
|
846f83e3e4 | ||
|
841a4f90c1 | ||
|
3284769fef | ||
|
b5ce3cebbc | ||
|
73aa83aa03 | ||
|
5fcdff843c | ||
|
76f1a61538 | ||
|
d591180e99 | ||
|
381a5fabc4 | ||
|
9838cc680a | ||
|
98195b5e3c | ||
|
3124e627cf | ||
|
c2286482ae | ||
|
f814ae795d | ||
|
50ef9677fe | ||
|
838e05521e | ||
|
cccbe753d7 | ||
|
6decd148e5 | ||
|
045dc566ea | ||
|
bb183457a6 | ||
|
dc18916f46 | ||
|
f99dafbf51 | ||
|
aee6fb1265 | ||
|
d1388cdf84 | ||
|
b869fe0b76 | ||
|
0e2817da88 | ||
|
fc8232f943 | ||
|
26324c2578 | ||
|
4e93905350 | ||
|
510e657c93 | ||
|
060ebe3d4a | ||
|
1c0adc47ee | ||
|
66fdba957b | ||
|
1d16958910 | ||
|
b18c6dec9d | ||
|
991a1fe8d8 | ||
|
60fc0bedb8 | ||
|
7717f9bece | ||
|
fe2dd1bad4 | ||
|
2d0840cb3a |
565 changed files with 13845 additions and 37406 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,3 +1,6 @@
|
|||
[submodule "extern/breakpad"]
|
||||
path = extern/breakpad
|
||||
url = https://chromium.googlesource.com/breakpad/breakpad
|
||||
[submodule "extern/sol"]
|
||||
path = extern/sol
|
||||
url = https://github.com/ThePhD/sol2
|
||||
|
|
|
@ -35,7 +35,7 @@ addons:
|
|||
# The other ones from OpenMW ppa
|
||||
libbullet-dev, libswresample-dev, libopenscenegraph-3.4-dev, libmygui-dev,
|
||||
# tes3mp stuff
|
||||
libboost1.61-dev, libqt5opengl5-dev
|
||||
libboost1.61-dev, libqt5opengl5-dev, libluajit-5.1-dev
|
||||
]
|
||||
|
||||
coverity_scan:
|
||||
|
@ -60,8 +60,6 @@ matrix:
|
|||
env:
|
||||
- MATRIX_CC="CC=clang-3.8 && CXX=clang++-3.8"
|
||||
allow_failures:
|
||||
- env:
|
||||
- MATRIX_CC="CC=clang-3.8 && CXX=clang++-3.8"
|
||||
- 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"
|
||||
|
|
|
@ -19,15 +19,3 @@ git clone https://github.com/TES3MP/RakNet
|
|||
cd RakNet
|
||||
cmake . -DRAKNET_ENABLE_DLL=OFF -DRAKNET_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
|
||||
|
|
|
@ -10,7 +10,6 @@ if [ ! -z "${MATRIX_CC}" ]; then
|
|||
fi
|
||||
|
||||
export RAKNET_ROOT=~/RakNet
|
||||
export Terra_ROOT=~/terra-Linux-x86_64-332a506
|
||||
|
||||
export CODE_COVERAGE=0
|
||||
if [ ! -z "${ANALYZE}" ]; then
|
||||
|
@ -38,5 +37,3 @@ ${ANALYZE}cmake .. \
|
|||
-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
|
||||
|
|
|
@ -140,6 +140,16 @@ endif()
|
|||
find_package(RakNet REQUIRED)
|
||||
include_directories(${RakNet_INCLUDES})
|
||||
|
||||
if (BUILD_OPENMW_MP OR BUILD_MASTER)
|
||||
find_package(LuaJit REQUIRED)
|
||||
find_package(Sol2 REQUIRED)
|
||||
include_directories(${LuaJit_INCLUDE_DIRS} ${Sol2_INCLUDE_DIRS})
|
||||
endif()
|
||||
if (BUILD_MASTER)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
include_directories(${OpenSSL_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
# Dependencies
|
||||
find_package(OpenGL REQUIRED)
|
||||
|
||||
|
@ -209,7 +219,6 @@ endif()
|
|||
IF(BUILD_OPENMW OR BUILD_OPENCS)
|
||||
|
||||
find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgParticle osgUtil osgFX)
|
||||
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
|
||||
|
||||
set(USED_OSG_PLUGINS
|
||||
osgdb_bmp
|
||||
|
@ -253,6 +262,8 @@ IF(BUILD_OPENMW OR BUILD_OPENCS)
|
|||
|
||||
ENDIF(BUILD_OPENMW OR BUILD_OPENCS)
|
||||
|
||||
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}) # HACK: DON'T TOUCH IT!
|
||||
|
||||
|
||||
set(BOOST_COMPONENTS system filesystem program_options)
|
||||
if(WIN32)
|
||||
|
|
|
@ -5,7 +5,7 @@ TES3MP
|
|||
|
||||
TES3MP is a project aiming to add multiplayer functionality to [OpenMW](https://github.com/OpenMW/openmw), a free and open source engine recreation of the popular Bethesda Softworks game "The Elder Scrolls III: Morrowind".
|
||||
|
||||
* TES3MP version: 0.6.3
|
||||
* TES3MP version: TBD-rewrite
|
||||
* OpenMW version: 0.44.0
|
||||
* License: GPLv3 (see [LICENSE](https://github.com/TES3MP/openmw-tes3mp/blob/master/LICENSE) for more information)
|
||||
|
||||
|
@ -17,10 +17,12 @@ 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.6, TES3MP is 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.
|
||||
|
||||
[Serverside Lua scripts](https://github.com/TES3MP/CoreScripts) are used to save and load the state of most of the aforementioned.
|
||||
|
||||
Please note that the master branch is an unfinished rewrite of a large part of the project and is not yet playable. You should use the [latest 0.6 release](https://github.com/TES3MP/openmw-tes3mp/releases/latest) for now.
|
||||
|
||||
Contributing
|
||||
--------------
|
||||
|
||||
|
@ -35,7 +37,7 @@ 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)
|
||||
* [TES3MP section on OpenMW forums](https://forum.openmw.org/viewforum.php?f=45)
|
||||
* [Subreddit](https://www.reddit.com/r/tes3mp)
|
||||
* [Known issues and bug reports](https://github.com/TES3MP/openmw-tes3mp/issues)
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
set (CMAKE_CXX_STANDARD 14)
|
||||
option(OPTION_TES3MP_PRE07 "Temporary. Pre 0.7.0 compatible mode." OFF)
|
||||
|
||||
set(BROWSER_UI
|
||||
${CMAKE_SOURCE_DIR}/files/tes3mp/ui/Main.ui
|
||||
|
@ -17,6 +16,7 @@ set(BROWSER
|
|||
PingUpdater.cpp
|
||||
PingHelper.cpp
|
||||
QueryHelper.cpp
|
||||
Settings.cpp
|
||||
${CMAKE_SOURCE_DIR}/files/tes3mp/browser.rc
|
||||
)
|
||||
|
||||
|
@ -36,6 +36,7 @@ set(BROWSER_HEADER
|
|||
netutils/Utils.hpp
|
||||
netutils/QueryClient.hpp
|
||||
Types.hpp
|
||||
Settings.hpp
|
||||
)
|
||||
|
||||
source_group(browser FILES ${BROWSER} ${BROWSER_HEADER})
|
||||
|
@ -75,6 +76,13 @@ add_executable(tes3mp-browser
|
|||
${UI_HDRS}
|
||||
)
|
||||
|
||||
set_property(TARGET tes3mp-browser PROPERTY CXX_STANDARD 14)
|
||||
|
||||
if (OPTION_TES3MP_PRE07)
|
||||
target_compile_definitions(tes3mp-browser PRIVATE TES3MP_PRE07)
|
||||
endif (OPTION_TES3MP_PRE07)
|
||||
|
||||
|
||||
if (WIN32)
|
||||
INSTALL(TARGETS tes3mp-browser RUNTIME DESTINATION ".")
|
||||
endif (WIN32)
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include <QJsonArray>
|
||||
#include <QFile>
|
||||
#include <QJsonDocument>
|
||||
#include <apps/browser/netutils/Utils.hpp>
|
||||
#include <QtWidgets/QFileDialog>
|
||||
|
||||
|
||||
using namespace Process;
|
||||
|
@ -31,9 +33,6 @@ MainWindow::MainWindow(QWidget *parent)
|
|||
tblServerBrowser->setModel(proxyModel);
|
||||
tblFavorites->setModel(proxyModel);
|
||||
|
||||
// Remove Favorites tab while it remains broken
|
||||
tabWidget->removeTab(1);
|
||||
|
||||
tblServerBrowser->hideColumn(ServerData::ADDR);
|
||||
tblFavorites->hideColumn(ServerData::ADDR);
|
||||
|
||||
|
@ -54,22 +53,45 @@ MainWindow::MainWindow(QWidget *parent)
|
|||
connect(tblServerBrowser, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(play()));
|
||||
connect(cBoxNotFull, SIGNAL(toggled(bool)), this, SLOT(notFullSwitch(bool)));
|
||||
connect(cBoxWithPlayers, SIGNAL(toggled(bool)), this, SLOT(havePlayersSwitch(bool)));
|
||||
connect(cBBoxWOPass, SIGNAL(toggled(bool)), this, SLOT(noPasswordSwitch(bool)));
|
||||
connect(comboLatency, SIGNAL(currentIndexChanged(int)), this, SLOT(maxLatencyChanged(int)));
|
||||
connect(leGamemode, SIGNAL(textChanged(const QString &)), this, SLOT(gamemodeChanged(const QString &)));
|
||||
connect(pbModulePath, &QPushButton::clicked, [this](bool) {
|
||||
QString str = QFileDialog::getExistingDirectory(this, tr("Module path"),
|
||||
leModulePath->text(), QFileDialog::ShowDirsOnly);
|
||||
if(!str.isEmpty())
|
||||
leModulePath->setText(str);
|
||||
});
|
||||
loadFavorites();
|
||||
queryHelper->refresh();
|
||||
settingsMgr.loadBrowserSettings(*this);
|
||||
|
||||
#ifdef TES3MP_PRE07
|
||||
gbModules->setTitle(tr("Plugins Path"));
|
||||
chbAutosort->setVisible(false);
|
||||
listModules->setVisible(false);
|
||||
leAddModule->setVisible(false);
|
||||
pbAddModule->setVisible(false);
|
||||
pbUpModule->setVisible(false);
|
||||
pbDownModule->setVisible(false);
|
||||
pbRemModule->setVisible(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
settingsMgr.saveBrowserSettings(*this);
|
||||
delete mGameInvoker;
|
||||
}
|
||||
|
||||
void MainWindow::addServerAndUpdate(QString addr)
|
||||
void MainWindow::addServerAndUpdate(const QString &addr)
|
||||
{
|
||||
favorites->insertRow(0);
|
||||
QModelIndex mi = favorites->index(0, ServerData::ADDR);
|
||||
favorites->setData(mi, addr, Qt::EditRole);
|
||||
/*auto address = addr.split(":");
|
||||
auto data = getExtendedData(address[0].toLatin1(), address[1].toUShort());*/
|
||||
|
||||
//NetController::get()->updateInfo(favorites, mi);
|
||||
//QueryClient::Update(RakNet::SystemAddress())
|
||||
/*auto data = QueryClient::Get().Query();
|
||||
|
@ -121,28 +143,26 @@ void MainWindow::play()
|
|||
if (id < 0)
|
||||
return;
|
||||
|
||||
ServerInfoDialog infoDialog(this);
|
||||
|
||||
ServerModel *sm = ((ServerModel*)proxyModel->sourceModel());
|
||||
|
||||
int sourceId = proxyModel->mapToSource(proxyModel->index(id, ServerData::ADDR)).row();
|
||||
infoDialog.Server(sm->myData[sourceId].addr);
|
||||
|
||||
if (!infoDialog.refresh())
|
||||
{
|
||||
queryHelper->refresh();
|
||||
return;
|
||||
}
|
||||
ServerInfoDialog infoDialog(sm->myData[sourceId].addr, this);
|
||||
|
||||
if (!infoDialog.exec())
|
||||
return;
|
||||
|
||||
if (!infoDialog.isUpdated())
|
||||
return;
|
||||
|
||||
QStringList arguments;
|
||||
arguments.append(QLatin1String("--connect=") + sm->myData[sourceId].addr.toLatin1());
|
||||
|
||||
if (sm->myData[sourceId].GetPassword() == 1)
|
||||
{
|
||||
bool ok;
|
||||
QString passw = QInputDialog::getText(this, "Connecting to: " + sm->myData[sourceId].addr, "Password: ", QLineEdit::Password, "", &ok);
|
||||
QString passw = QInputDialog::getText(this, tr("Connecting to: ") + sm->myData[sourceId].addr, tr("Password: "),
|
||||
QLineEdit::Password, "", &ok);
|
||||
if (!ok)
|
||||
return;
|
||||
arguments.append(QLatin1String("--password=") + passw.toLatin1());
|
||||
|
@ -228,6 +248,11 @@ void MainWindow::havePlayersSwitch(bool state)
|
|||
proxyModel->filterEmptyServers(state);
|
||||
}
|
||||
|
||||
void MainWindow::noPasswordSwitch(bool state)
|
||||
{
|
||||
proxyModel->filterPassworded(state);
|
||||
}
|
||||
|
||||
void MainWindow::maxLatencyChanged(int index)
|
||||
{
|
||||
int maxLatency = index * 50;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "ui_Main.h"
|
||||
#include "ServerModel.hpp"
|
||||
#include "MySortFilterProxyModel.hpp"
|
||||
#include "Settings.hpp"
|
||||
#include <components/process/processinvoker.hpp>
|
||||
|
||||
class QueryHelper;
|
||||
|
@ -17,11 +18,11 @@ class MainWindow : public QMainWindow, private Ui::MainWindow
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = 0);
|
||||
virtual ~MainWindow();
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow() override;
|
||||
protected:
|
||||
void closeEvent(QCloseEvent * event) Q_DECL_OVERRIDE;
|
||||
void addServerAndUpdate(QString addr);
|
||||
void addServerAndUpdate(const QString &addr);
|
||||
protected slots:
|
||||
void tabSwitched(int index);
|
||||
void addServer();
|
||||
|
@ -31,6 +32,7 @@ protected slots:
|
|||
void serverSelected();
|
||||
void notFullSwitch(bool state);
|
||||
void havePlayersSwitch(bool state);
|
||||
void noPasswordSwitch(bool state);
|
||||
void maxLatencyChanged(int index);
|
||||
void gamemodeChanged(const QString &text);
|
||||
private:
|
||||
|
@ -38,6 +40,7 @@ private:
|
|||
Process::ProcessInvoker *mGameInvoker;
|
||||
ServerModel *browser, *favorites;
|
||||
MySortFilterProxyModel *proxyModel;
|
||||
SettingsMgr settingsMgr;
|
||||
void loadFavorites();
|
||||
};
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "ServerModel.hpp"
|
||||
|
||||
#include <qdebug.h>
|
||||
#include <apps/browser/netutils/Utils.hpp>
|
||||
|
||||
bool MySortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||
{
|
||||
|
@ -13,26 +14,49 @@ bool MySortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &
|
|||
QModelIndex pingIndex = sourceModel()->index(sourceRow, ServerData::PING, sourceParent);
|
||||
QModelIndex plIndex = sourceModel()->index(sourceRow, ServerData::PLAYERS, sourceParent);
|
||||
QModelIndex maxPlIndex = sourceModel()->index(sourceRow, ServerData::MAX_PLAYERS, sourceParent);
|
||||
QModelIndex passwordIndex = sourceModel()->index(sourceRow, ServerData::PASSW, sourceParent);
|
||||
|
||||
int ping = sourceModel()->data(pingIndex).toInt();
|
||||
bool pingOk;
|
||||
int ping = sourceModel()->data(pingIndex).toInt(&pingOk);
|
||||
int players = sourceModel()->data(plIndex).toInt();
|
||||
int maxPlayers = sourceModel()->data(maxPlIndex).toInt();
|
||||
|
||||
if (maxPing > 0 && (ping == -1 || ping > maxPing))
|
||||
if (maxPing > 0 && (ping == -1 || ping > maxPing || !pingOk))
|
||||
return false;
|
||||
if (filterEmpty && players == 0)
|
||||
return false;
|
||||
if (filterFull && players >= maxPlayers)
|
||||
return false;
|
||||
if(filterPasswEnabled && sourceModel()->data(passwordIndex).toString() == "Yes")
|
||||
return false;
|
||||
|
||||
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
|
||||
}
|
||||
|
||||
bool MySortFilterProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
|
||||
{
|
||||
if(sortColumn() == ServerData::PING)
|
||||
{
|
||||
bool valid;
|
||||
|
||||
int pingright = sourceModel()->data(source_right).toInt(&valid);
|
||||
pingright = valid ? pingright : PING_UNREACHABLE;
|
||||
|
||||
int pingleft = sourceModel()->data(source_left).toInt(&valid);
|
||||
pingleft = valid ? pingleft : PING_UNREACHABLE;
|
||||
return pingleft < pingright;
|
||||
}
|
||||
else
|
||||
return QSortFilterProxyModel::lessThan(source_left, source_right);
|
||||
}
|
||||
|
||||
MySortFilterProxyModel::MySortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent)
|
||||
{
|
||||
filterEmpty = false;
|
||||
filterFull = false;
|
||||
filterPasswEnabled = false;
|
||||
maxPing = 0;
|
||||
setSortCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);
|
||||
}
|
||||
|
||||
void MySortFilterProxyModel::filterEmptyServers(bool state)
|
||||
|
@ -52,3 +76,9 @@ void MySortFilterProxyModel::pingLessThan(int maxPing)
|
|||
this->maxPing = maxPing;
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
void MySortFilterProxyModel::filterPassworded(bool state)
|
||||
{
|
||||
filterPasswEnabled = state;
|
||||
invalidateFilter();
|
||||
}
|
||||
|
|
|
@ -13,13 +13,15 @@ class MySortFilterProxyModel : public QSortFilterProxyModel
|
|||
Q_OBJECT
|
||||
protected:
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const Q_DECL_FINAL;
|
||||
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const Q_DECL_FINAL;
|
||||
public:
|
||||
MySortFilterProxyModel(QObject *parent);
|
||||
explicit MySortFilterProxyModel(QObject *parent);
|
||||
void filterFullServer(bool state);
|
||||
void filterEmptyServers(bool state);
|
||||
void filterPassworded(bool state);
|
||||
void pingLessThan(int maxPing);
|
||||
private:
|
||||
bool filterEmpty, filterFull;
|
||||
bool filterEmpty, filterFull, filterPasswEnabled;
|
||||
int maxPing;
|
||||
};
|
||||
|
||||
|
|
|
@ -7,13 +7,19 @@
|
|||
#include <QDebug>
|
||||
#include "PingUpdater.hpp"
|
||||
|
||||
void PingHelper::Add(int row, AddrPair addrPair)
|
||||
void PingHelper::Add(int row, const AddrPair &addrPair)
|
||||
{
|
||||
pingUpdater->addServer(row, addrPair);
|
||||
if (!pingThread->isRunning())
|
||||
pingThread->start();
|
||||
}
|
||||
|
||||
void PingHelper::Reset()
|
||||
{
|
||||
//if (pingThread->isRunning())
|
||||
Stop();
|
||||
}
|
||||
|
||||
void PingHelper::Stop()
|
||||
{
|
||||
emit pingUpdater->stop();
|
||||
|
@ -35,9 +41,8 @@ PingHelper &PingHelper::Get()
|
|||
return helper;
|
||||
}
|
||||
|
||||
PingHelper::PingHelper()
|
||||
PingHelper::PingHelper() : QObject()
|
||||
{
|
||||
QObject();
|
||||
pingThread = new QThread;
|
||||
pingUpdater = new PingUpdater;
|
||||
pingUpdater->moveToThread(pingThread);
|
||||
|
@ -48,11 +53,4 @@ PingHelper::PingHelper()
|
|||
connect(this, SIGNAL(stop()), pingUpdater, SLOT(stop()));
|
||||
//connect(pingUpdater, SIGNAL(finished()), pingUpdater, SLOT(deleteLater()));
|
||||
connect(pingUpdater, SIGNAL(updateModel(int, unsigned)), this, SLOT(update(int, unsigned)));
|
||||
|
||||
|
||||
}
|
||||
|
||||
PingHelper::~PingHelper()
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -17,17 +17,17 @@ class PingHelper : public QObject
|
|||
Q_OBJECT
|
||||
public:
|
||||
|
||||
void Add(int row, AddrPair addrPair);
|
||||
void Reset();
|
||||
void Add(int row, const AddrPair &addrPair);
|
||||
void Stop();
|
||||
void SetModel(QAbstractTableModel *model);
|
||||
//void UpdateImmedialy(PingUpdater::AddrPair addrPair);
|
||||
static PingHelper &Get();
|
||||
private:
|
||||
PingHelper();
|
||||
~PingHelper();
|
||||
|
||||
PingHelper(const PingHelper&) = delete;
|
||||
PingHelper& operator=(const PingHelper&) = delete;
|
||||
private:
|
||||
PingHelper();
|
||||
signals:
|
||||
void stop();
|
||||
public slots:
|
||||
|
|
|
@ -14,7 +14,7 @@ void PingUpdater::stop()
|
|||
run = false;
|
||||
}
|
||||
|
||||
void PingUpdater::addServer(int row, AddrPair addr)
|
||||
void PingUpdater::addServer(int row, const AddrPair &addr)
|
||||
{
|
||||
servers.push_back({row, addr});
|
||||
run = true;
|
||||
|
@ -42,7 +42,7 @@ void PingUpdater::process()
|
|||
unsigned ping = PingRakNetServer(server.second.first.toLatin1(), server.second.second);
|
||||
|
||||
qDebug() << "Pong from" << server.second.first + "|" + QString::number(server.second.second)
|
||||
<< ":" << ping << "ms";
|
||||
<< ":" << ping << "ms" << "Sizeof servers: " << servers.size();
|
||||
|
||||
emit updateModel(server.first, ping);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ class PingUpdater : public QObject
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
void addServer(int row, AddrPair addrPair);
|
||||
void addServer(int row, const AddrPair &addrPair);
|
||||
public slots:
|
||||
void stop();
|
||||
void process();
|
||||
|
|
|
@ -19,8 +19,8 @@ QueryHelper::QueryHelper(QAbstractItemModel *model)
|
|||
connect(queryThread, SIGNAL(started()), queryUpdate, SLOT(process()));
|
||||
connect(queryUpdate, SIGNAL(finished()), queryThread, SLOT(quit()));
|
||||
connect(queryUpdate, &QueryUpdate::finished, [this](){emit finished();});
|
||||
connect(queryUpdate, SIGNAL(updateModel(QString, unsigned short, QueryData)),
|
||||
this, SLOT(update(QString, unsigned short, QueryData)));
|
||||
connect(queryUpdate, SIGNAL(updateModel(const QString&, unsigned short, const QueryData&)),
|
||||
this, SLOT(update(const QString&, unsigned short, const QueryData&)));
|
||||
queryUpdate->moveToThread(queryThread);
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@ void QueryHelper::refresh()
|
|||
if (!queryThread->isRunning())
|
||||
{
|
||||
_model->removeRows(0, _model->rowCount());
|
||||
PingHelper::Get().Stop();
|
||||
queryThread->start();
|
||||
emit started();
|
||||
}
|
||||
|
@ -39,7 +40,7 @@ void QueryHelper::terminate()
|
|||
queryThread->terminate();
|
||||
}
|
||||
|
||||
void QueryHelper::update(QString addr, unsigned short port, QueryData data)
|
||||
void QueryHelper::update(const QString &addr, unsigned short port, const QueryData& data)
|
||||
{
|
||||
ServerModel *model = ((ServerModel*)_model);
|
||||
model->insertRow(model->rowCount());
|
||||
|
@ -80,7 +81,7 @@ void QueryUpdate::process()
|
|||
return;
|
||||
}
|
||||
|
||||
for (auto server : data)
|
||||
for (const auto &server : data)
|
||||
emit updateModel(server.first.ToString(false), server.first.GetPort(), server.second);
|
||||
emit finished();
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ public slots:
|
|||
void refresh();
|
||||
void terminate();
|
||||
private slots:
|
||||
void update(QString addr, unsigned short port, QueryData data);
|
||||
void update(const QString &addr, unsigned short port, const QueryData& data);
|
||||
signals:
|
||||
void finished();
|
||||
void started();
|
||||
|
@ -38,7 +38,7 @@ class QueryUpdate : public QObject
|
|||
Q_OBJECT
|
||||
signals:
|
||||
void finished();
|
||||
void updateModel(QString addr, unsigned short port, QueryData data);
|
||||
void updateModel(const QString &addr, unsigned short port, const QueryData& data);
|
||||
public slots:
|
||||
void process();
|
||||
};
|
||||
|
|
|
@ -6,32 +6,65 @@
|
|||
#include "qdebug.h"
|
||||
|
||||
#include "ServerInfoDialog.hpp"
|
||||
#include <apps/browser/netutils/Utils.hpp>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <QThread>
|
||||
|
||||
using namespace std;
|
||||
using namespace RakNet;
|
||||
|
||||
ServerInfoDialog::ServerInfoDialog(QWidget *parent): QDialog(parent)
|
||||
ThrWorker::ThrWorker(ServerInfoDialog *dialog, QString addr, unsigned short port): addr(std::move(addr)), port(port), stopped(false)
|
||||
{
|
||||
this->dialog = dialog;
|
||||
}
|
||||
|
||||
void ThrWorker::process()
|
||||
{
|
||||
stopped = false;
|
||||
auto newSD = QueryClient::Get().Update(SystemAddress(addr.toUtf8(), port));
|
||||
if (dialog != nullptr)
|
||||
dialog->setData(newSD);
|
||||
stopped = true;
|
||||
emit finished();
|
||||
}
|
||||
|
||||
ServerInfoDialog::ServerInfoDialog(const QString &addr, QWidget *parent): QDialog(parent)
|
||||
{
|
||||
setupUi(this);
|
||||
connect(btnRefresh, SIGNAL(clicked()), this, SLOT(refresh()));
|
||||
refreshThread = new QThread;
|
||||
|
||||
QStringList list = addr.split(':');
|
||||
worker = new ThrWorker(this, list[0].toLatin1(), list[1].toUShort());
|
||||
worker->moveToThread(refreshThread);
|
||||
connect(refreshThread, SIGNAL(started()), worker, SLOT(process()));
|
||||
connect(worker, SIGNAL(finished()), refreshThread, SLOT(quit()));
|
||||
connect(refreshThread, SIGNAL(finished()), this, SLOT(refresh()));
|
||||
|
||||
connect(btnRefresh, &QPushButton::clicked, [this]{
|
||||
if (!refreshThread->isRunning())
|
||||
refreshThread->start();
|
||||
});
|
||||
}
|
||||
|
||||
ServerInfoDialog::~ServerInfoDialog()
|
||||
{
|
||||
|
||||
worker->dialog = nullptr;
|
||||
if (!refreshThread->isRunning())
|
||||
refreshThread->terminate();
|
||||
}
|
||||
|
||||
void ServerInfoDialog::Server(QString addr)
|
||||
bool ServerInfoDialog::isUpdated()
|
||||
{
|
||||
this->addr = addr;
|
||||
return sd.first != UNASSIGNED_SYSTEM_ADDRESS;
|
||||
}
|
||||
|
||||
bool ServerInfoDialog::refresh()
|
||||
void ServerInfoDialog::setData(std::pair<RakNet::SystemAddress, QueryData> &newSD)
|
||||
{
|
||||
sd = newSD;
|
||||
}
|
||||
|
||||
void ServerInfoDialog::refresh()
|
||||
{
|
||||
QStringList list = addr.split(':');
|
||||
auto sd = QueryClient::Get().Update(SystemAddress(list[0].toLatin1(), list[1].toUShort()));
|
||||
if (sd.first != UNASSIGNED_SYSTEM_ADDRESS)
|
||||
{
|
||||
leAddr->setText(sd.first.ToString(true, ':'));
|
||||
|
@ -41,17 +74,16 @@ bool ServerInfoDialog::refresh()
|
|||
btnConnect->setDisabled(ping == PING_UNREACHABLE);
|
||||
|
||||
listPlayers->clear();
|
||||
|
||||
for (auto player : sd.second.players)
|
||||
for (const auto &player : sd.second.players)
|
||||
listPlayers->addItem(QString::fromStdString(player));
|
||||
|
||||
listPlugins->clear();
|
||||
for (auto plugin : sd.second.plugins)
|
||||
for (const auto &plugin : sd.second.plugins)
|
||||
listPlugins->addItem(QString::fromStdString(plugin.name));
|
||||
|
||||
listRules->clear();
|
||||
const static vector<std::string> defaultRules {"gamemode", "maxPlayers", "name", "passw", "players", "version"};
|
||||
for (auto rule : sd.second.rules)
|
||||
for (auto &rule : sd.second.rules)
|
||||
{
|
||||
if (::find(defaultRules.begin(), defaultRules.end(), rule.first) != defaultRules.end())
|
||||
continue;
|
||||
|
@ -64,7 +96,12 @@ bool ServerInfoDialog::refresh()
|
|||
}
|
||||
|
||||
lblPlayers->setText(QString::number(sd.second.players.size()) + " / " + QString::number(sd.second.GetMaxPlayers()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int ServerInfoDialog::exec()
|
||||
{
|
||||
if (!refreshThread->isRunning())
|
||||
refreshThread->start();
|
||||
return QDialog::exec();
|
||||
}
|
||||
|
|
|
@ -6,18 +6,45 @@
|
|||
#define NEWLAUNCHER_SERVERINFODIALOG_HPP
|
||||
|
||||
#include "ui_ServerInfo.h"
|
||||
#include <apps/browser/netutils/Utils.hpp>
|
||||
#include <RakNetTypes.h>
|
||||
#include <components/openmw-mp/Master/MasterData.hpp>
|
||||
|
||||
class ThrWorker;
|
||||
|
||||
class ServerInfoDialog : public QDialog, public Ui::Dialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ServerInfoDialog(QWidget *parent = 0);
|
||||
virtual ~ServerInfoDialog();
|
||||
void Server(QString addr);
|
||||
explicit ServerInfoDialog(const QString &addr, QWidget *parent = nullptr);
|
||||
~ServerInfoDialog() override;
|
||||
bool isUpdated();
|
||||
void setData(std::pair<RakNet::SystemAddress, QueryData> &newSD);
|
||||
public slots:
|
||||
bool refresh();
|
||||
void refresh();
|
||||
int exec() Q_DECL_OVERRIDE;
|
||||
private:
|
||||
QThread *refreshThread;
|
||||
ThrWorker* worker;
|
||||
std::pair<RakNet::SystemAddress, QueryData> sd;
|
||||
};
|
||||
|
||||
class ThrWorker: public QObject
|
||||
{
|
||||
friend class ServerInfoDialog;
|
||||
Q_OBJECT
|
||||
public:
|
||||
ThrWorker(ServerInfoDialog *dialog, QString addr, unsigned short port);
|
||||
|
||||
public slots:
|
||||
void process();
|
||||
signals:
|
||||
void finished();
|
||||
private:
|
||||
QString addr;
|
||||
unsigned short port;
|
||||
bool stopped;
|
||||
ServerInfoDialog *dialog;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1,16 +1,11 @@
|
|||
#include <qmessagebox.h>
|
||||
#include "ServerModel.hpp"
|
||||
#include <qdebug.h>
|
||||
#include <apps/browser/netutils/Utils.hpp>
|
||||
|
||||
ServerModel::ServerModel(QObject *parent) : QAbstractTableModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
ServerModel::~ServerModel()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*QHash<int, QByteArray> ServerModel::roleNames() const
|
||||
{
|
||||
return roles;
|
||||
|
@ -49,7 +44,7 @@ QVariant ServerModel::data(const QModelIndex &index, int role) const
|
|||
var = QString(sd.rules.at("name").str.c_str());
|
||||
break;
|
||||
case ServerData::PING:
|
||||
var = sd.ping;
|
||||
var = sd.ping == PING_UNREACHABLE ? QVariant("Unreachable") : sd.ping;
|
||||
break;
|
||||
case ServerData::MODNAME:
|
||||
if (sd.rules.at("gamemode").str == "")
|
||||
|
@ -136,7 +131,7 @@ bool ServerModel::setData(const QModelIndex &index, const QVariant &value, int r
|
|||
sd.SetPassword(value.toBool());
|
||||
break;
|
||||
case ServerData::VERSION:
|
||||
sd.SetVersion(value.toString().toLatin1());
|
||||
sd.SetVersion(value.toString().toUtf8());
|
||||
ok = !sd.addr.isEmpty();
|
||||
break;
|
||||
case ServerData::PLAYERS:
|
||||
|
@ -146,14 +141,14 @@ bool ServerModel::setData(const QModelIndex &index, const QVariant &value, int r
|
|||
sd.SetMaxPlayers(value.toInt(&ok));
|
||||
break;
|
||||
case ServerData::HOSTNAME:
|
||||
sd.SetName(value.toString().toLatin1());
|
||||
sd.SetName(value.toString().toUtf8());
|
||||
ok = !sd.addr.isEmpty();
|
||||
break;
|
||||
case ServerData::PING:
|
||||
sd.ping = value.toInt(&ok);
|
||||
break;
|
||||
case ServerData::MODNAME:
|
||||
sd.SetGameMode(value.toString().toLatin1());
|
||||
sd.SetGameMode(value.toString().toUtf8());
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
|
@ -170,9 +165,7 @@ bool ServerModel::insertRows(int position, int count, const QModelIndex &index)
|
|||
Q_UNUSED(index);
|
||||
beginInsertRows(QModelIndex(), position, position + count - 1);
|
||||
|
||||
for (int row = 0; row < count; ++row) {
|
||||
myData.insert(position, {});
|
||||
}
|
||||
myData.insert(position, count, {});
|
||||
|
||||
endInsertRows();
|
||||
return true;
|
||||
|
|
|
@ -29,8 +29,7 @@ class ServerModel: public QAbstractTableModel
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ServerModel(QObject *parent = 0);
|
||||
~ServerModel();
|
||||
explicit ServerModel(QObject *parent = nullptr);
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_FINAL;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_FINAL;
|
||||
int columnCount(const QModelIndex &parent) const Q_DECL_FINAL;
|
||||
|
|
198
apps/browser/Settings.cpp
Normal file
198
apps/browser/Settings.cpp
Normal file
|
@ -0,0 +1,198 @@
|
|||
//
|
||||
// Created by koncord on 31.03.18.
|
||||
//
|
||||
|
||||
#include "Settings.hpp"
|
||||
#include <QDebug>
|
||||
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#include <apps/browser/netutils/QueryClient.hpp>
|
||||
#include "MainWindow.hpp"
|
||||
|
||||
std::string loadSettings (Settings::Manager & settings, const std::string &cfgName)
|
||||
{
|
||||
Files::ConfigurationManager mCfgMgr;
|
||||
// Create the settings manager and load default settings file
|
||||
const std::string localdefault = (mCfgMgr.getLocalPath() / (cfgName + "-default.cfg")).string();
|
||||
const std::string globaldefault = (mCfgMgr.getGlobalPath() / (cfgName + "-default.cfg")).string();
|
||||
|
||||
// prefer local
|
||||
if (boost::filesystem::exists(localdefault))
|
||||
settings.loadDefault(localdefault);
|
||||
else if (boost::filesystem::exists(globaldefault))
|
||||
settings.loadDefault(globaldefault);
|
||||
else
|
||||
throw std::runtime_error ("No default settings file found! Make sure the file \"" + cfgName + "-default.cfg\" was properly installed.");
|
||||
|
||||
// load user settings if they exist
|
||||
const std::string settingspath = (mCfgMgr.getUserConfigPath() / (cfgName + ".cfg")).string();
|
||||
if (boost::filesystem::exists(settingspath))
|
||||
settings.loadUser(settingspath);
|
||||
|
||||
return settingspath;
|
||||
}
|
||||
|
||||
SettingsMgr::SettingsMgr()
|
||||
{
|
||||
clientCfg = loadSettings(clientMgr, "tes3mp-client");
|
||||
switchMgr();
|
||||
serverCfg = loadSettings(serverMgr, "tes3mp-server");
|
||||
switchMgr();
|
||||
|
||||
std::string addr = clientMgr.getString("address", "Master");
|
||||
int port = clientMgr.getInt("port", "Master");
|
||||
QueryClient::Get().SetServer(addr, port);
|
||||
|
||||
}
|
||||
|
||||
void SettingsMgr::loadBrowserSettings(Ui::MainWindow &mw)
|
||||
{
|
||||
mw.comboLatency->setCurrentIndex(clientMgr.getInt("maxLatency", "Browser"));
|
||||
mw.leGamemode->setText(QString::fromStdString(clientMgr.getString("gameMode", "Browser")));
|
||||
mw.cBoxNotFull->setCheckState(clientMgr.getBool("notFull", "Browser") ? Qt::Checked : Qt::Unchecked);
|
||||
mw.cBoxWithPlayers->setCheckState(clientMgr.getBool("withPlayers", "Browser") ? Qt::Checked : Qt::Unchecked);
|
||||
mw.cBBoxWOPass->setCheckState(clientMgr.getBool("noPassword", "Browser") ? Qt::Checked : Qt::Unchecked);
|
||||
|
||||
mw.tblServerBrowser->sortByColumn(clientMgr.getInt("sortByCol", "Browser"),
|
||||
clientMgr.getBool("sortByColAscending", "Browser") ? Qt::AscendingOrder : Qt::DescendingOrder);
|
||||
|
||||
loadClientSettings(mw);
|
||||
switchMgr();
|
||||
loadServerSettings(mw);
|
||||
switchMgr();
|
||||
}
|
||||
|
||||
void SettingsMgr::saveBrowserSettings(Ui::MainWindow &mw)
|
||||
{
|
||||
clientMgr.setInt("maxLatency", "Browser", mw.comboLatency->currentIndex());
|
||||
clientMgr.setString("gameMode", "Browser", mw.leGamemode->text().toStdString());
|
||||
clientMgr.setBool("notFull", "Browser", mw.cBoxNotFull->checkState() == Qt::Checked);
|
||||
clientMgr.setBool("withPlayers", "Browser", mw.cBoxWithPlayers->checkState() == Qt::Checked);
|
||||
clientMgr.setBool("noPassword", "Browser", mw.cBBoxWOPass->checkState() == Qt::Checked);
|
||||
|
||||
clientMgr.setInt("sortByCol", "Browser", mw.tblServerBrowser->horizontalHeader()->sortIndicatorSection());
|
||||
clientMgr.setBool("sortByColAscending", "Browser", mw.tblServerBrowser->horizontalHeader()->sortIndicatorOrder() == Qt::AscendingOrder);
|
||||
|
||||
saveClientSettings(mw);
|
||||
|
||||
clientMgr.saveUser(clientCfg);
|
||||
switchMgr();
|
||||
saveServerSettings(mw);
|
||||
switchMgr();
|
||||
}
|
||||
|
||||
void SettingsMgr::loadClientSettings(Ui::MainWindow &mw)
|
||||
{
|
||||
mw.leClientAddress->setText(QString::fromStdString(clientMgr.getString("destinationAddress", "General")));
|
||||
mw.leClientPort->setText(QString::fromStdString(clientMgr.getString("port", "General")));
|
||||
mw.leClientPassword->setText(QString::fromStdString(clientMgr.getString("password", "General")));
|
||||
mw.combLoglevel->setCurrentIndex(clientMgr.getInt("logLevel", "General"));
|
||||
|
||||
mw.leClientMAddress->setText(QString::fromStdString(clientMgr.getString("address", "Master")));
|
||||
mw.leClientMPort->setText(QString::fromStdString(clientMgr.getString("port", "Master")));
|
||||
|
||||
mw.pbChatKey->setText(QString::fromStdString(clientMgr.getString("keySay", "Chat")));
|
||||
mw.pbModeKey->setText(QString::fromStdString(clientMgr.getString("keyChatMode", "Chat")));
|
||||
|
||||
mw.sbPosX->setValue(clientMgr.getInt("x", "Chat"));
|
||||
mw.sbPosY->setValue(clientMgr.getInt("y", "Chat"));
|
||||
mw.sbPosW->setValue(clientMgr.getInt("w", "Chat"));
|
||||
mw.sbPosH->setValue(clientMgr.getInt("h", "Chat"));
|
||||
|
||||
mw.sbDelay->setValue(clientMgr.getFloat("delay", "Chat"));
|
||||
}
|
||||
|
||||
void SettingsMgr::saveClientSettings(Ui::MainWindow &mw)
|
||||
{
|
||||
clientMgr.setString("destinationAddress", "General", mw.leClientAddress->text().toStdString());
|
||||
clientMgr.setString("port", "General", mw.leClientPort->text().toStdString());
|
||||
clientMgr.setString("password", "General", mw.leClientPassword->text().toStdString());
|
||||
clientMgr.setInt("logLevel", "General", mw.combLoglevel->currentIndex());
|
||||
|
||||
clientMgr.setString("address", "Master", mw.leClientMAddress->text().toStdString());
|
||||
clientMgr.setString("port", "Master", mw.leClientMPort->text().toStdString());
|
||||
|
||||
clientMgr.setString("keySay", "Chat", mw.pbChatKey->text().toStdString());
|
||||
clientMgr.setString("keyChatMode", "Chat", mw.pbModeKey->text().toStdString());
|
||||
|
||||
clientMgr.setInt("x", "Chat", mw.sbPosX->value());
|
||||
clientMgr.setInt("y", "Chat", mw.sbPosY->value());
|
||||
clientMgr.setInt("w", "Chat", mw.sbPosW->value());
|
||||
clientMgr.setInt("h", "Chat", mw.sbPosH->value());
|
||||
|
||||
clientMgr.setFloat("delay", "Chat", mw.sbDelay->value());
|
||||
|
||||
}
|
||||
|
||||
void SettingsMgr::loadServerSettings(Ui::MainWindow &mw)
|
||||
{
|
||||
mw.leServerAddress->setText(QString::fromStdString(serverMgr.getString("localAddress", "General")));
|
||||
mw.leServerPort->setText(QString::fromStdString(serverMgr.getString("port", "General")));
|
||||
mw.sbMaxPlayers->setValue(serverMgr.getInt("maximumPlayers", "General"));
|
||||
mw.leHostname->setText(QString::fromStdString(serverMgr.getString("hostname", "General")));
|
||||
mw.combServerLoglevel->setCurrentIndex(serverMgr.getInt("logLevel", "General"));
|
||||
mw.leServerPassword->setText(QString::fromStdString(serverMgr.getString("password", "General")));
|
||||
|
||||
mw.chbMSenabled->setCheckState(serverMgr.getBool("enabled", "MasterServer") ? Qt::Checked : Qt::Unchecked);
|
||||
mw.leServerMaddress->setText(QString::fromStdString(serverMgr.getString("address", "MasterServer")));
|
||||
mw.leServerMPort->setText(QString::fromStdString(serverMgr.getString("port", "MasterServer")));
|
||||
mw.sbRate->setValue(serverMgr.getInt("rate", "MasterServer"));
|
||||
|
||||
#ifndef TES3MP_PRE07
|
||||
mw.leModulePath->setText(QString::fromStdString(serverMgr.getString("home", "Modules")));
|
||||
mw.chbAutosort->setCheckState(serverMgr.getBool("autoSort", "Modules") ? Qt::Checked : Qt::Unchecked);
|
||||
#else
|
||||
mw.leModulePath->setText(QString::fromStdString(serverMgr.getString("home", "Plugins")));
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void SettingsMgr::saveServerSettings(Ui::MainWindow &mw)
|
||||
{
|
||||
serverMgr.setString("localAddress", "General", mw.leServerAddress->text().toStdString());
|
||||
serverMgr.setString("port", "General", mw.leServerPort->text().toStdString());
|
||||
serverMgr.setInt("maximumPlayers", "General", mw.sbMaxPlayers->value());
|
||||
serverMgr.setString("hostname", "General", mw.leHostname->text().toStdString());
|
||||
serverMgr.setInt("logLevel", "General", mw.combServerLoglevel->currentIndex());
|
||||
serverMgr.setString("password", "General", mw.leServerPassword->text().toStdString());
|
||||
|
||||
serverMgr.setBool("enabled", "MasterServer", mw.chbMSenabled->checkState() == Qt::Checked);
|
||||
serverMgr.setString("address", "MasterServer", mw.leServerMaddress->text().toStdString());
|
||||
serverMgr.setString("port", "MasterServer", mw.leServerMPort->text().toStdString());
|
||||
serverMgr.setInt("rate", "MasterServer", mw.sbRate->value());
|
||||
|
||||
#ifndef TES3MP_PRE07
|
||||
serverMgr.setString("home", "Modules", mw.leModulePath->text().toStdString());
|
||||
serverMgr.setBool("autoSort", "Modules", mw.chbAutosort->checkState() == Qt::Checked);
|
||||
#else
|
||||
serverMgr.setString("home", "Plugins", mw.leModulePath->text().toStdString());
|
||||
#endif
|
||||
|
||||
serverMgr.saveUser(serverCfg);
|
||||
}
|
||||
|
||||
void SettingsMgr::switchMgr()
|
||||
{
|
||||
static Settings::CategorySettingValueMap saveUserSettings;
|
||||
static Settings::CategorySettingValueMap saveDefaultSettings;
|
||||
static Settings::CategorySettingVector saveChangedSettings;
|
||||
static bool currentMgrIsClient = true;
|
||||
|
||||
if(!currentMgrIsClient)
|
||||
{
|
||||
saveUserSettings.swap(clientMgr.mUserSettings);
|
||||
saveDefaultSettings.swap(clientMgr.mDefaultSettings);
|
||||
saveChangedSettings.swap(clientMgr.mChangedSettings);
|
||||
|
||||
qDebug() << "Manager switched to Client config";
|
||||
}
|
||||
else
|
||||
{
|
||||
saveUserSettings.swap(serverMgr.mUserSettings);
|
||||
saveDefaultSettings.swap(serverMgr.mDefaultSettings);
|
||||
saveChangedSettings.swap(serverMgr.mChangedSettings);
|
||||
|
||||
qDebug() << "Manager switched to Server config";
|
||||
}
|
||||
currentMgrIsClient = !currentMgrIsClient;
|
||||
}
|
32
apps/browser/Settings.hpp
Normal file
32
apps/browser/Settings.hpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// Created by koncord on 31.03.18.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
class SettingsMgr
|
||||
{
|
||||
public:
|
||||
SettingsMgr();
|
||||
|
||||
void loadBrowserSettings(Ui::MainWindow &mw);
|
||||
void saveBrowserSettings(Ui::MainWindow &mw);
|
||||
|
||||
void loadClientSettings(Ui::MainWindow &mw);
|
||||
void saveClientSettings(Ui::MainWindow &mw);
|
||||
|
||||
void loadServerSettings(Ui::MainWindow &mw);
|
||||
void saveServerSettings(Ui::MainWindow &mw);
|
||||
|
||||
private:
|
||||
Settings::Manager serverMgr, clientMgr;
|
||||
std::string serverCfg, clientCfg;
|
||||
void switchMgr();
|
||||
};
|
|
@ -4,42 +4,8 @@
|
|||
#include <apps/browser/netutils/QueryClient.hpp>
|
||||
#include "MainWindow.hpp"
|
||||
|
||||
std::string loadSettings (Settings::Manager & settings)
|
||||
{
|
||||
Files::ConfigurationManager mCfgMgr;
|
||||
// Create the settings manager and load default settings file
|
||||
const std::string localdefault = (mCfgMgr.getLocalPath() / "tes3mp-client-default.cfg").string();
|
||||
const std::string globaldefault = (mCfgMgr.getGlobalPath() / "tes3mp-client-default.cfg").string();
|
||||
|
||||
// prefer local
|
||||
if (boost::filesystem::exists(localdefault))
|
||||
settings.loadDefault(localdefault);
|
||||
else if (boost::filesystem::exists(globaldefault))
|
||||
settings.loadDefault(globaldefault);
|
||||
else
|
||||
throw std::runtime_error ("No default settings file found! Make sure the file \"tes3mp-client-default.cfg\" was properly installed.");
|
||||
|
||||
// load user settings if they exist
|
||||
const std::string settingspath = (mCfgMgr.getUserConfigPath() / "tes3mp-client.cfg").string();
|
||||
if (boost::filesystem::exists(settingspath))
|
||||
settings.loadUser(settingspath);
|
||||
|
||||
return settingspath;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
Settings::Manager mgr;
|
||||
|
||||
loadSettings(mgr);
|
||||
|
||||
std::string addr = mgr.getString("address", "Master");
|
||||
int port = mgr.getInt("port", "Master");
|
||||
|
||||
// initialize resources, if needed
|
||||
// Q_INIT_RESOURCE(resfile);
|
||||
|
||||
QueryClient::Get().SetServer(addr, port);
|
||||
QApplication app(argc, argv);
|
||||
MainWindow d;
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ QueryClient::~QueryClient()
|
|||
RakPeerInterface::DestroyInstance(peer);
|
||||
}
|
||||
|
||||
void QueryClient::SetServer(std::string addr, unsigned short port)
|
||||
void QueryClient::SetServer(const string &addr, unsigned short port)
|
||||
{
|
||||
masterAddr = SystemAddress(addr.c_str(), port);
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ map<SystemAddress, QueryData> QueryClient::Query()
|
|||
return query;
|
||||
}
|
||||
|
||||
pair<SystemAddress, QueryData> QueryClient::Update(RakNet::SystemAddress addr)
|
||||
pair<SystemAddress, QueryData> QueryClient::Update(const RakNet::SystemAddress &addr)
|
||||
{
|
||||
qDebug() << "Locking mutex in QueryClient::Update(RakNet::SystemAddress addr)";
|
||||
pair<SystemAddress, QueryData> server;
|
||||
|
@ -179,7 +179,7 @@ ConnectionState QueryClient::Connect()
|
|||
{
|
||||
|
||||
ConnectionAttemptResult car = peer->Connect(masterAddr.ToString(false), masterAddr.GetPort(), TES3MP_MASTERSERVER_PASSW,
|
||||
strlen(TES3MP_MASTERSERVER_PASSW), 0, 0, 5, 500);
|
||||
strlen(TES3MP_MASTERSERVER_PASSW), nullptr, 0, 5, 500);
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
|
|
@ -14,16 +14,16 @@
|
|||
|
||||
class QueryClient
|
||||
{
|
||||
private:
|
||||
public:
|
||||
QueryClient(QueryClient const &) = delete;
|
||||
QueryClient(QueryClient &&) = delete;
|
||||
QueryClient &operator=(QueryClient const &) = delete;
|
||||
QueryClient &operator=(QueryClient &&) = delete;
|
||||
public:
|
||||
|
||||
static QueryClient &Get();
|
||||
void SetServer(std::string addr, unsigned short port);
|
||||
void SetServer(const std::string &addr, unsigned short port);
|
||||
std::map<RakNet::SystemAddress, QueryData> Query();
|
||||
std::pair<RakNet::SystemAddress, QueryData> Update(RakNet::SystemAddress addr);
|
||||
std::pair<RakNet::SystemAddress, QueryData> Update(const RakNet::SystemAddress &addr);
|
||||
int Status();
|
||||
private:
|
||||
RakNet::ConnectionState Connect();
|
||||
|
|
|
@ -44,11 +44,15 @@ unsigned int PingRakNetServer(const char *addr, unsigned short port)
|
|||
break;
|
||||
case ID_CONNECTED_PING:
|
||||
case ID_UNCONNECTED_PONG:
|
||||
{
|
||||
RakNet::BitStream bsIn(&packet->data[1], packet->length, false);
|
||||
bsIn.Read(time);
|
||||
time = now - time;
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
peer->DeallocatePacket(packet);
|
||||
}
|
||||
|
@ -69,9 +73,9 @@ ServerExtendedData getExtendedData(const char *addr, unsigned short port)
|
|||
sstr << TES3MP_VERSION;
|
||||
sstr << TES3MP_PROTO_VERSION;
|
||||
|
||||
std::string msg = "";
|
||||
std::string msg;
|
||||
|
||||
if (peer->Connect(addr, port, sstr.str().c_str(), (int)(sstr.str().size()), 0, 0, 3, 500, 0) != RakNet::CONNECTION_ATTEMPT_STARTED)
|
||||
if (peer->Connect(addr, port, sstr.str().c_str(), (int)(sstr.str().size()), nullptr, 0, 3, 500, 0) != RakNet::CONNECTION_ATTEMPT_STARTED)
|
||||
msg = "Connection attempt failed.\n";
|
||||
|
||||
|
||||
|
@ -142,14 +146,14 @@ ServerExtendedData getExtendedData(const char *addr, unsigned short port)
|
|||
{
|
||||
RakNet::RakString str;
|
||||
bs.Read(str);
|
||||
data.players.push_back(str.C_String());
|
||||
data.players.emplace_back(str.C_String());
|
||||
}
|
||||
bs.Read(length);
|
||||
for (size_t i = 0; i < length; i++)
|
||||
{
|
||||
RakNet::RakString str;
|
||||
bs.Read(str);
|
||||
data.plugins.push_back(str.C_String());
|
||||
data.plugins.emplace_back(str.C_String());
|
||||
}
|
||||
done = true;
|
||||
}
|
||||
|
|
69
apps/master/AdminRest.cpp
Normal file
69
apps/master/AdminRest.cpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
//
|
||||
// Created by koncord on 04.09.17.
|
||||
//
|
||||
|
||||
#include "AdminRest.hpp"
|
||||
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
|
||||
#include "RestUtils.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace chrono;
|
||||
using namespace boost::property_tree;
|
||||
|
||||
|
||||
AdminRest::AdminRest(const std::string &cert, const std::string &key, const std::string &verifyFile,
|
||||
unsigned short port, std::shared_ptr<MasterServer> master) : httpServer(cert, key, verifyFile), master(master)
|
||||
{
|
||||
httpServer.config.port = port;
|
||||
}
|
||||
|
||||
void AdminRest::start()
|
||||
{
|
||||
static const string AdminArea = "^/api/admin?";
|
||||
|
||||
httpServer.resource[AdminArea]["POST"] = [this](auto response, auto request) {
|
||||
cout << request->method << endl;
|
||||
cout << request->path << endl;
|
||||
cout << request->http_version << endl;
|
||||
|
||||
for (auto &header : request->header)
|
||||
cout << header.first << ": " << header.second << endl;
|
||||
|
||||
string resp;
|
||||
master->luaStuff([&request, &response, &resp](sol::state &state) {
|
||||
sol::protected_function func = state["OnAdminRequest"];
|
||||
|
||||
sol::protected_function_result result = func.call(request->remote_endpoint_address, request->content.string());
|
||||
if (result.valid())
|
||||
*response << result.get<string>();
|
||||
else
|
||||
{
|
||||
cerr << "Error: " << result.get<string>() << endl;
|
||||
*response << response500;
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/*httpServer.on_error = [](auto request, const boost::system::error_code& err)
|
||||
{
|
||||
std::cerr << "Error: " << err.message() << " " << err.category().name() << std::endl;
|
||||
};*/
|
||||
|
||||
httpServer.default_resource["GET"] = [](auto response, auto /*request*/) {
|
||||
cout << "Default request" << endl;
|
||||
*response << response400;
|
||||
};
|
||||
|
||||
thr = thread([this](){httpServer.start();});
|
||||
}
|
||||
|
||||
void AdminRest::stop()
|
||||
{
|
||||
httpServer.stop();
|
||||
if(thr.joinable())
|
||||
thr.join();
|
||||
}
|
25
apps/master/AdminRest.hpp
Normal file
25
apps/master/AdminRest.hpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// Created by koncord on 04.09.17.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SimpleWeb/https_server.hpp"
|
||||
#include "MasterServer.hpp"
|
||||
|
||||
typedef SimpleWeb::Server<SimpleWeb::HTTPS> HttpsServer;
|
||||
|
||||
class AdminRest
|
||||
{
|
||||
public:
|
||||
AdminRest(const std::string &cert, const std::string &key, const std::string &verifyFile, unsigned short port, std::shared_ptr<MasterServer> master);
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
private:
|
||||
HttpsServer httpServer;
|
||||
std::shared_ptr<MasterServer> master;
|
||||
std::thread thr;
|
||||
};
|
||||
|
||||
|
|
@ -1,14 +1,13 @@
|
|||
project(masterserver)
|
||||
|
||||
#set(CMAKE_CXX_STANDARD 14)
|
||||
add_definitions(-std=gnu++14)
|
||||
|
||||
include_directories("./")
|
||||
|
||||
set(SOURCE_FILES main.cpp MasterServer.cpp MasterServer.hpp RestServer.cpp RestServer.hpp)
|
||||
set(SOURCE_FILES main.cpp MasterServer.cpp MasterServer.hpp RestServer.cpp RestServer.hpp AdminRest.cpp)
|
||||
|
||||
add_executable(masterserver ${SOURCE_FILES})
|
||||
target_link_libraries(masterserver ${RakNet_LIBRARY} components)
|
||||
target_link_libraries(masterserver ${RakNet_LIBRARY} ${LuaJit_LIBRARIES} ${OPENSSL_LIBRARIES} components)
|
||||
|
||||
set_property(TARGET masterserver PROPERTY CXX_STANDARD 14)
|
||||
|
||||
option(BUILD_MASTER_TEST "build master server test program" OFF)
|
||||
|
||||
|
|
|
@ -12,19 +12,54 @@
|
|||
#include <components/openmw-mp/Master/PacketMasterUpdate.hpp>
|
||||
#include <components/openmw-mp/Master/PacketMasterAnnounce.hpp>
|
||||
#include <components/openmw-mp/Version.hpp>
|
||||
#include <components/openmw-mp/Utils.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
using namespace RakNet;
|
||||
using namespace std;
|
||||
using namespace mwmp;
|
||||
using namespace chrono;
|
||||
|
||||
MasterServer::MasterServer(unsigned short maxConnections, unsigned short port)
|
||||
MasterServer::MasterServer(const std::string &luaScript)
|
||||
{
|
||||
peer = RakPeerInterface::GetInstance();
|
||||
sockdescr = SocketDescriptor(port, 0);
|
||||
peer->Startup(maxConnections, &sockdescr, 1, 1000);
|
||||
state.open_libraries();
|
||||
|
||||
peer->SetMaximumIncomingConnections(maxConnections);
|
||||
boost::filesystem::path absPath = boost::filesystem::absolute(luaScript);
|
||||
|
||||
std::string package_path = state["package"]["path"];
|
||||
state["package"]["path"] = Utils::convertPath(absPath.parent_path().string() + "/?.lua") + ";" + package_path;
|
||||
|
||||
state.set_function("BanAddress", &MasterServer::ban, this);
|
||||
state.set_function("UnbanAddress", &MasterServer::unban, this);
|
||||
|
||||
state.script_file(luaScript);
|
||||
|
||||
sol::table config = state["config"];
|
||||
|
||||
if (config.get_type() != sol::type::table)
|
||||
throw runtime_error("config is not correct");
|
||||
|
||||
sol::object maxConnections = config["maxConnections"];
|
||||
if (maxConnections.get_type() != sol::type::number)
|
||||
throw runtime_error("config.maxConnections is not correct");
|
||||
|
||||
sol::object port = config["port"];
|
||||
if (port.get_type() != sol::type::number)
|
||||
throw runtime_error("config.port is not correct");
|
||||
|
||||
state.new_usertype<MasterServer::SServer>("Server",
|
||||
"name", sol::property(&MasterServer::SServer::GetName),
|
||||
"gamemode", sol::property(&MasterServer::SServer::GetGameMode),
|
||||
"version", sol::property(&MasterServer::SServer::GetVersion)
|
||||
);
|
||||
|
||||
|
||||
peer = RakPeerInterface::GetInstance();
|
||||
sockdescr = SocketDescriptor(port.as<unsigned short>(), nullptr);
|
||||
peer->Startup(maxConnections.as<unsigned short>(), &sockdescr, 1, 1000);
|
||||
peer->SetLimitIPConnectionFrequency(true);
|
||||
|
||||
peer->SetMaximumIncomingConnections(maxConnections.as<unsigned short>());
|
||||
peer->SetIncomingPassword(TES3MP_MASTERSERVER_PASSW, (int) strlen(TES3MP_MASTERSERVER_PASSW));
|
||||
run = false;
|
||||
}
|
||||
|
@ -52,6 +87,13 @@ void MasterServer::Thread()
|
|||
PacketMasterAnnounce pma(peer);
|
||||
pma.SetSendStream(&send);
|
||||
|
||||
luaStuff([](sol::state &state) {
|
||||
sol::protected_function func = state["OnInit"];
|
||||
sol::protected_function_result result = func.call();
|
||||
if (!result.valid())
|
||||
cerr << "Error: " << result.get<string>() << endl;
|
||||
});
|
||||
|
||||
while (run)
|
||||
{
|
||||
Packet *packet = peer->Receive();
|
||||
|
@ -67,9 +109,9 @@ void MasterServer::Thread()
|
|||
servers.erase(it++);
|
||||
else ++it;
|
||||
}
|
||||
for(auto id = pendingACKs.begin(); id != pendingACKs.end();)
|
||||
for (auto id = pendingACKs.begin(); id != pendingACKs.end();)
|
||||
{
|
||||
if(now - id->second >= 30s)
|
||||
if (now - id->second >= 30s)
|
||||
{
|
||||
cout << "timeout: " << peer->GetSystemAddressFromGuid(id->first).ToString() << endl;
|
||||
peer->CloseConnection(id->first, true);
|
||||
|
@ -113,7 +155,7 @@ void MasterServer::Thread()
|
|||
SystemAddress addr;
|
||||
data.Read(addr); // update 1 server
|
||||
|
||||
ServerIter it = servers.find(addr);
|
||||
auto it = servers.find(addr);
|
||||
if (it != servers.end())
|
||||
{
|
||||
pair<SystemAddress, QueryData> pairPtr(it->first, static_cast<QueryData>(it->second));
|
||||
|
@ -127,7 +169,7 @@ void MasterServer::Thread()
|
|||
}
|
||||
case ID_MASTER_ANNOUNCE:
|
||||
{
|
||||
ServerIter iter = servers.find(packet->systemAddress);
|
||||
auto iter = servers.find(packet->systemAddress);
|
||||
|
||||
pma.SetReadStream(&data);
|
||||
SServer server;
|
||||
|
@ -141,6 +183,26 @@ void MasterServer::Thread()
|
|||
pendingACKs[packet->guid] = steady_clock::now();
|
||||
};
|
||||
|
||||
auto isServerValid = [&](const SServer &sserver) {
|
||||
bool ret = false;
|
||||
auto addr = packet->systemAddress.ToString(false);
|
||||
|
||||
lock_guard<mutex> lock(banMutex);
|
||||
|
||||
if (peer->IsBanned(addr)) // check if address is banned
|
||||
return false;
|
||||
|
||||
luaStuff([&ret, &packet, &sserver, &addr](sol::state &state) {
|
||||
sol::protected_function func = state["OnServerAnnounce"];
|
||||
sol::protected_function_result result = func.call(addr, sserver);
|
||||
if (result.valid())
|
||||
ret = result.get<bool>();
|
||||
else
|
||||
cerr << "Error: " << result.get<string>() << endl;
|
||||
});
|
||||
return ret;
|
||||
};
|
||||
|
||||
if (iter != servers.end())
|
||||
{
|
||||
if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_DELETE)
|
||||
|
@ -152,9 +214,19 @@ void MasterServer::Thread()
|
|||
}
|
||||
else if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_ANNOUNCE)
|
||||
{
|
||||
cout << "Updated";
|
||||
iter->second = server;
|
||||
keepAliveFunc();
|
||||
|
||||
if (isServerValid(server))
|
||||
{
|
||||
cout << "Updated";
|
||||
iter->second = server;
|
||||
keepAliveFunc();
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Update rejected";
|
||||
servers.erase(iter);
|
||||
pendingACKs.erase(packet->guid);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -164,9 +236,14 @@ void MasterServer::Thread()
|
|||
}
|
||||
else if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_ANNOUNCE)
|
||||
{
|
||||
cout << "Added";
|
||||
iter = servers.insert({packet->systemAddress, server}).first;
|
||||
keepAliveFunc();
|
||||
if (isServerValid(server))
|
||||
{
|
||||
cout << "Added";
|
||||
iter = servers.insert({packet->systemAddress, server}).first;
|
||||
keepAliveFunc();
|
||||
}
|
||||
else
|
||||
cout << "Adding rejected";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -186,7 +263,8 @@ void MasterServer::Thread()
|
|||
peer->CloseConnection(packet->systemAddress, true);
|
||||
break;
|
||||
default:
|
||||
cout << "Wrong packet. id " << (unsigned) packet->data[0] << " packet length " << packet->length << " from " << packet->systemAddress.ToString() << endl;
|
||||
cout << "Wrong packet. id " << (unsigned) packet->data[0] << " packet length "
|
||||
<< packet->length << " from " << packet->systemAddress.ToString() << endl;
|
||||
peer->CloseConnection(packet->systemAddress, true);
|
||||
}
|
||||
}
|
||||
|
@ -234,3 +312,22 @@ MasterServer::ServerMap *MasterServer::GetServers()
|
|||
{
|
||||
return &servers;
|
||||
}
|
||||
|
||||
|
||||
void MasterServer::luaStuff(std::function<void(sol::state &)> f)
|
||||
{
|
||||
lock_guard<mutex> lock(luaMutex);
|
||||
f(state);
|
||||
}
|
||||
|
||||
void MasterServer::ban(const std::string &addr)
|
||||
{
|
||||
lock_guard<mutex> lock(banMutex);
|
||||
peer->AddToBanList(addr.c_str());
|
||||
}
|
||||
|
||||
void MasterServer::unban(const std::string &addr)
|
||||
{
|
||||
lock_guard<mutex> lock(banMutex);
|
||||
peer->RemoveFromBanList(addr.c_str());
|
||||
}
|
||||
|
|
|
@ -9,18 +9,12 @@
|
|||
#include <chrono>
|
||||
#include <RakPeerInterface.h>
|
||||
#include <components/openmw-mp/Master/MasterData.hpp>
|
||||
#include <sol.hpp>
|
||||
#include <mutex>
|
||||
|
||||
class MasterServer
|
||||
{
|
||||
public:
|
||||
struct Ban
|
||||
{
|
||||
RakNet::SystemAddress sa;
|
||||
bool permanent;
|
||||
struct Date
|
||||
{
|
||||
} date;
|
||||
};
|
||||
struct SServer : QueryData
|
||||
{
|
||||
std::chrono::steady_clock::time_point lastUpdate;
|
||||
|
@ -29,7 +23,7 @@ public:
|
|||
//typedef ServerMap::const_iterator ServerCIter;
|
||||
typedef ServerMap::iterator ServerIter;
|
||||
|
||||
MasterServer(unsigned short maxConnections, unsigned short port);
|
||||
explicit MasterServer(const std::string &luaScript);
|
||||
~MasterServer();
|
||||
|
||||
void Start();
|
||||
|
@ -38,6 +32,10 @@ public:
|
|||
void Wait();
|
||||
|
||||
ServerMap* GetServers();
|
||||
void luaStuff(std::function<void(sol::state &)> f);
|
||||
|
||||
void ban(const std::string &addr);
|
||||
void unban(const std::string &addr);
|
||||
|
||||
private:
|
||||
void Thread();
|
||||
|
@ -49,6 +47,9 @@ private:
|
|||
ServerMap servers;
|
||||
bool run;
|
||||
std::map<RakNet::RakNetGUID, std::chrono::steady_clock::time_point> pendingACKs;
|
||||
sol::state state;
|
||||
std::mutex luaMutex;
|
||||
std::mutex banMutex;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -7,22 +7,12 @@
|
|||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
|
||||
#include "RestUtils.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace chrono;
|
||||
using namespace boost::property_tree;
|
||||
|
||||
static string response201 = "HTTP/1.1 201 Created\r\nContent-Length: 7\r\n\r\nCreated";
|
||||
static string response202 = "HTTP/1.1 202 Accepted\r\nContent-Length: 8\r\n\r\nAccepted";
|
||||
static string response400 = "HTTP/1.1 400 Bad Request\r\nContent-Length: 11\r\n\r\nbad request";
|
||||
|
||||
inline void ResponseStr(HttpServer::Response &response, string content, string type = "", string code = "200 OK")
|
||||
{
|
||||
response << "HTTP/1.1 " << code << "\r\n";
|
||||
if (!type.empty())
|
||||
response << "Content-Type: " << type <<"\r\n";
|
||||
response << "Content-Length: " << content.length() << "\r\n\r\n" << content;
|
||||
}
|
||||
|
||||
inline void ptreeToServer(boost::property_tree::ptree &pt, MasterServer::SServer &server)
|
||||
{
|
||||
server.SetName(pt.get<string>("hostname").c_str());
|
||||
|
@ -70,7 +60,7 @@ void RestServer::start()
|
|||
auto port = (unsigned short)stoi(&(addr[addr.find(':')+1]));
|
||||
queryToStringStream(ss, "server", serverMap->at(RakNet::SystemAddress(addr.c_str(), port)));
|
||||
ss << "}";
|
||||
ResponseStr(*response, ss.str(), "application/json");
|
||||
ResponseStr<SimpleWeb::HTTP>(response, ss.str(), "application/json");
|
||||
}
|
||||
catch(out_of_range e)
|
||||
{
|
||||
|
@ -93,7 +83,7 @@ void RestServer::start()
|
|||
ss << ", ";
|
||||
}
|
||||
ss << "}}";
|
||||
ResponseStr(*response, ss.str(), "application/json");
|
||||
ResponseStr<SimpleWeb::HTTP>(response, ss.str(), "application/json");
|
||||
updatedCache = false;
|
||||
}
|
||||
*response << str;
|
||||
|
@ -110,7 +100,7 @@ void RestServer::start()
|
|||
MasterServer::SServer server;
|
||||
ptreeToServer(pt, server);
|
||||
|
||||
unsigned short port = pt.get<unsigned short>("port");
|
||||
auto port = pt.get<unsigned short>("port");
|
||||
server.lastUpdate = steady_clock::now();
|
||||
serverMap->insert({RakNet::SystemAddress(request->remote_endpoint_address.c_str(), port), server});
|
||||
updatedCache = true;
|
||||
|
@ -137,7 +127,6 @@ void RestServer::start()
|
|||
*response << response400;
|
||||
return;
|
||||
}
|
||||
|
||||
if (request->content.size() != 0)
|
||||
{
|
||||
try
|
||||
|
@ -171,14 +160,14 @@ void RestServer::start()
|
|||
ss << ", \"players\": " << players;
|
||||
ss << "}";
|
||||
|
||||
ResponseStr(*response, ss.str(), "application/json");
|
||||
ResponseStr<SimpleWeb::HTTP>(response, ss.str(), "application/json");
|
||||
};
|
||||
|
||||
httpServer.default_resource["GET"]=[](auto response, auto /*request*/) {
|
||||
*response << response400;
|
||||
};
|
||||
|
||||
httpServer.start();
|
||||
thr = thread([this](){httpServer.start();});
|
||||
}
|
||||
|
||||
void RestServer::cacheUpdated()
|
||||
|
@ -189,4 +178,6 @@ void RestServer::cacheUpdated()
|
|||
void RestServer::stop()
|
||||
{
|
||||
httpServer.stop();
|
||||
if(thr.joinable())
|
||||
thr.join();
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ private:
|
|||
HttpServer httpServer;
|
||||
MasterServer::ServerMap *serverMap;
|
||||
bool updatedCache = true;
|
||||
std::thread thr;
|
||||
};
|
||||
|
||||
|
||||
|
|
25
apps/master/RestUtils.hpp
Normal file
25
apps/master/RestUtils.hpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// Created by koncord on 04.09.17.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "SimpleWeb/base_server.hpp"
|
||||
|
||||
static std::string response201 = "HTTP/1.1 201 Created\r\nContent-Length: 7\r\n\r\nCreated";
|
||||
static std::string response202 = "HTTP/1.1 202 Accepted\r\nContent-Length: 8\r\n\r\nAccepted";
|
||||
static std::string response400 = "HTTP/1.1 400 Bad Request\r\nContent-Length: 11\r\n\r\nbad request";
|
||||
|
||||
static std::string response403 = "HTTP/1.1 403 Forbidden\r\nContent-Length: 9\r\n\r\nForbidden";
|
||||
static std::string response500 = "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 21\r\n\r\nInternal Server Error";
|
||||
|
||||
template <class Protocol>
|
||||
inline void ResponseStr(std::shared_ptr<typename SimpleWeb::ServerBase<Protocol>::Response> response,
|
||||
std::string content, std::string type = "", std::string code = "200 OK")
|
||||
{
|
||||
*response << "HTTP/1.1 " << code << "\r\n";
|
||||
if (!type.empty())
|
||||
*response << "Content-Type: " << type <<"\r\n";
|
||||
*response << "Content-Length: " << content.length() << "\r\n\r\n" << content;
|
||||
}
|
21
apps/master/SimpleWeb/LICENSE
Normal file
21
apps/master/SimpleWeb/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2016 Ole Christian Eidheim
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,72 +1,137 @@
|
|||
#ifndef BASE_SERVER_HPP
|
||||
#define BASE_SERVER_HPP
|
||||
#pragma once
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <thread>
|
||||
#include "utility.hpp"
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
#include <regex>
|
||||
|
||||
#ifndef CASE_INSENSITIVE_EQUALS_AND_HASH
|
||||
#define CASE_INSENSITIVE_EQUALS_AND_HASH
|
||||
|
||||
//Based on http://www.boost.org/doc/libs/1_60_0/doc/html/unordered/hash_equality.html
|
||||
struct case_insensitive_equals
|
||||
{
|
||||
bool operator()(const std::string &key1, const std::string &key2) const
|
||||
{
|
||||
return boost::algorithm::iequals(key1, key2);
|
||||
}
|
||||
};
|
||||
|
||||
struct case_insensitive_hash
|
||||
{
|
||||
size_t operator()(const std::string &key) const
|
||||
{
|
||||
std::size_t seed = 0;
|
||||
for (auto &c: key)
|
||||
boost::hash_combine(seed, std::tolower(c));
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef USE_STANDALONE_ASIO
|
||||
#include <asio.hpp>
|
||||
#include <asio/steady_timer.hpp>
|
||||
namespace SimpleWeb {
|
||||
using error_code = std::error_code;
|
||||
using errc = std::errc;
|
||||
namespace make_error_code = std;
|
||||
} // namespace SimpleWeb
|
||||
#else
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
namespace SimpleWeb {
|
||||
namespace asio = boost::asio;
|
||||
using error_code = boost::system::error_code;
|
||||
namespace errc = boost::system::errc;
|
||||
namespace make_error_code = boost::system::errc;
|
||||
} // namespace SimpleWeb
|
||||
#endif
|
||||
|
||||
namespace SimpleWeb
|
||||
{
|
||||
template<class socket_type>
|
||||
namespace SimpleWeb {
|
||||
template <class socket_type>
|
||||
class Server;
|
||||
|
||||
template<class socket_type>
|
||||
class ServerBase
|
||||
{
|
||||
template <class socket_type>
|
||||
class ServerBase {
|
||||
protected:
|
||||
class Session;
|
||||
|
||||
public:
|
||||
virtual ~ServerBase()
|
||||
{}
|
||||
|
||||
class Response : public std::ostream
|
||||
{
|
||||
class Response : public std::enable_shared_from_this<Response>, public std::ostream {
|
||||
friend class ServerBase<socket_type>;
|
||||
friend class Server<socket_type>;
|
||||
|
||||
boost::asio::streambuf streambuf;
|
||||
asio::streambuf streambuf;
|
||||
|
||||
std::shared_ptr<socket_type> socket;
|
||||
std::shared_ptr<Session> session;
|
||||
long timeout_content;
|
||||
|
||||
Response(const std::shared_ptr<socket_type> &socket) : std::ostream(&streambuf), socket(socket)
|
||||
{}
|
||||
Response(std::shared_ptr<Session> session, long timeout_content) noexcept : std::ostream(&streambuf), session(std::move(session)), timeout_content(timeout_content) {}
|
||||
|
||||
template <typename size_type>
|
||||
void write_header(const CaseInsensitiveMultimap &header, size_type size) {
|
||||
bool content_length_written = false;
|
||||
bool chunked_transfer_encoding = false;
|
||||
for(auto &field : header) {
|
||||
if(!content_length_written && case_insensitive_equal(field.first, "content-length"))
|
||||
content_length_written = true;
|
||||
else if(!chunked_transfer_encoding && case_insensitive_equal(field.first, "transfer-encoding") && case_insensitive_equal(field.second, "chunked"))
|
||||
chunked_transfer_encoding = true;
|
||||
|
||||
*this << field.first << ": " << field.second << "\r\n";
|
||||
}
|
||||
if(!content_length_written && !chunked_transfer_encoding && !close_connection_after_response)
|
||||
*this << "Content-Length: " << size << "\r\n\r\n";
|
||||
else
|
||||
*this << "\r\n";
|
||||
}
|
||||
|
||||
public:
|
||||
size_t size()
|
||||
{
|
||||
size_t size() noexcept {
|
||||
return streambuf.size();
|
||||
}
|
||||
|
||||
/// Use this function if you need to recursively send parts of a longer message
|
||||
void send(const std::function<void(const error_code &)> &callback = nullptr) noexcept {
|
||||
session->connection->set_timeout(timeout_content);
|
||||
auto self = this->shared_from_this(); // Keep Response instance alive through the following async_write
|
||||
asio::async_write(*session->connection->socket, streambuf, [self, callback](const error_code &ec, size_t /*bytes_transferred*/) {
|
||||
self->session->connection->cancel_timeout();
|
||||
auto lock = self->session->connection->handler_runner->continue_lock();
|
||||
if(!lock)
|
||||
return;
|
||||
if(callback)
|
||||
callback(ec);
|
||||
});
|
||||
}
|
||||
|
||||
/// Write directly to stream buffer using std::ostream::write
|
||||
void write(const char_type *ptr, std::streamsize n) {
|
||||
std::ostream::write(ptr, n);
|
||||
}
|
||||
|
||||
/// Convenience function for writing status line, potential header fields, and empty content
|
||||
void write(StatusCode status_code = StatusCode::success_ok, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) {
|
||||
*this << "HTTP/1.1 " << SimpleWeb::status_code(status_code) << "\r\n";
|
||||
write_header(header, 0);
|
||||
}
|
||||
|
||||
/// Convenience function for writing status line, header fields, and content
|
||||
void write(StatusCode status_code, const std::string &content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) {
|
||||
*this << "HTTP/1.1 " << SimpleWeb::status_code(status_code) << "\r\n";
|
||||
write_header(header, content.size());
|
||||
if(!content.empty())
|
||||
*this << content;
|
||||
}
|
||||
|
||||
/// Convenience function for writing status line, header fields, and content
|
||||
void write(StatusCode status_code, std::istream &content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) {
|
||||
*this << "HTTP/1.1 " << SimpleWeb::status_code(status_code) << "\r\n";
|
||||
content.seekg(0, std::ios::end);
|
||||
auto size = content.tellg();
|
||||
content.seekg(0, std::ios::beg);
|
||||
write_header(header, size);
|
||||
if(size)
|
||||
*this << content.rdbuf();
|
||||
}
|
||||
|
||||
/// Convenience function for writing success status line, header fields, and content
|
||||
void write(const std::string &content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) {
|
||||
write(StatusCode::success_ok, content, header);
|
||||
}
|
||||
|
||||
/// Convenience function for writing success status line, header fields, and content
|
||||
void write(std::istream &content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) {
|
||||
write(StatusCode::success_ok, content, header);
|
||||
}
|
||||
|
||||
/// Convenience function for writing success status line, and header fields
|
||||
void write(const CaseInsensitiveMultimap &header) {
|
||||
write(StatusCode::success_ok, std::string(), header);
|
||||
}
|
||||
|
||||
/// If true, force server to close the connection after the response have been sent.
|
||||
///
|
||||
/// This is useful when implementing a HTTP/1.0-server sending content
|
||||
|
@ -74,438 +139,399 @@ namespace SimpleWeb
|
|||
bool close_connection_after_response = false;
|
||||
};
|
||||
|
||||
class Content : public std::istream
|
||||
{
|
||||
class Content : public std::istream {
|
||||
friend class ServerBase<socket_type>;
|
||||
|
||||
public:
|
||||
size_t size()
|
||||
{
|
||||
size_t size() noexcept {
|
||||
return streambuf.size();
|
||||
}
|
||||
|
||||
std::string string()
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << rdbuf();
|
||||
return ss.str();
|
||||
/// Convenience function to return std::string. The stream buffer is consumed.
|
||||
std::string string() noexcept {
|
||||
try {
|
||||
std::stringstream ss;
|
||||
ss << rdbuf();
|
||||
return ss.str();
|
||||
}
|
||||
catch(...) {
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
boost::asio::streambuf &streambuf;
|
||||
|
||||
Content(boost::asio::streambuf &streambuf) : std::istream(&streambuf), streambuf(streambuf)
|
||||
{}
|
||||
asio::streambuf &streambuf;
|
||||
Content(asio::streambuf &streambuf) noexcept : std::istream(&streambuf), streambuf(streambuf) {}
|
||||
};
|
||||
|
||||
class Request
|
||||
{
|
||||
class Request {
|
||||
friend class ServerBase<socket_type>;
|
||||
|
||||
friend class Server<socket_type>;
|
||||
friend class Session;
|
||||
|
||||
public:
|
||||
std::string method, path, http_version;
|
||||
std::string method, path, query_string, http_version;
|
||||
|
||||
Content content;
|
||||
|
||||
std::unordered_multimap<std::string, std::string, case_insensitive_hash, case_insensitive_equals> header;
|
||||
CaseInsensitiveMultimap header;
|
||||
|
||||
std::smatch path_match;
|
||||
|
||||
std::string remote_endpoint_address;
|
||||
unsigned short remote_endpoint_port;
|
||||
|
||||
private:
|
||||
Request(const socket_type &socket) : content(streambuf)
|
||||
{
|
||||
try
|
||||
{
|
||||
remote_endpoint_address = socket.lowest_layer().remote_endpoint().address().to_string();
|
||||
remote_endpoint_port = socket.lowest_layer().remote_endpoint().port();
|
||||
}
|
||||
catch (...)
|
||||
{}
|
||||
/// Returns query keys with percent-decoded values.
|
||||
CaseInsensitiveMultimap parse_query_string() noexcept {
|
||||
return SimpleWeb::QueryString::parse(query_string);
|
||||
}
|
||||
|
||||
boost::asio::streambuf streambuf;
|
||||
private:
|
||||
asio::streambuf streambuf;
|
||||
|
||||
Request(const std::string &remote_endpoint_address = std::string(), unsigned short remote_endpoint_port = 0) noexcept
|
||||
: content(streambuf), remote_endpoint_address(remote_endpoint_address), remote_endpoint_port(remote_endpoint_port) {}
|
||||
};
|
||||
|
||||
class Config
|
||||
{
|
||||
protected:
|
||||
class Connection : public std::enable_shared_from_this<Connection> {
|
||||
public:
|
||||
template <typename... Args>
|
||||
Connection(std::shared_ptr<ScopeRunner> handler_runner, Args &&... args) noexcept : handler_runner(std::move(handler_runner)), socket(new socket_type(std::forward<Args>(args)...)) {}
|
||||
|
||||
std::shared_ptr<ScopeRunner> handler_runner;
|
||||
|
||||
std::unique_ptr<socket_type> socket; // Socket must be unique_ptr since asio::ssl::stream<asio::ip::tcp::socket> is not movable
|
||||
std::mutex socket_close_mutex;
|
||||
|
||||
std::unique_ptr<asio::steady_timer> timer;
|
||||
|
||||
void close() noexcept {
|
||||
error_code ec;
|
||||
std::unique_lock<std::mutex> lock(socket_close_mutex); // The following operations seems to be needed to run sequentially
|
||||
socket->lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both, ec);
|
||||
socket->lowest_layer().close(ec);
|
||||
}
|
||||
|
||||
void set_timeout(long seconds) noexcept {
|
||||
if(seconds == 0) {
|
||||
timer = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
timer = std::unique_ptr<asio::steady_timer>(new asio::steady_timer(socket->get_io_service()));
|
||||
timer->expires_from_now(std::chrono::seconds(seconds));
|
||||
auto self = this->shared_from_this();
|
||||
timer->async_wait([self](const error_code &ec) {
|
||||
if(!ec)
|
||||
self->close();
|
||||
});
|
||||
}
|
||||
|
||||
void cancel_timeout() noexcept {
|
||||
if(timer) {
|
||||
error_code ec;
|
||||
timer->cancel(ec);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Session {
|
||||
public:
|
||||
Session(std::shared_ptr<Connection> connection) noexcept : connection(std::move(connection)) {
|
||||
try {
|
||||
auto remote_endpoint = this->connection->socket->lowest_layer().remote_endpoint();
|
||||
request = std::shared_ptr<Request>(new Request(remote_endpoint.address().to_string(), remote_endpoint.port()));
|
||||
}
|
||||
catch(...) {
|
||||
request = std::shared_ptr<Request>(new Request());
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Connection> connection;
|
||||
std::shared_ptr<Request> request;
|
||||
};
|
||||
|
||||
public:
|
||||
class Config {
|
||||
friend class ServerBase<socket_type>;
|
||||
|
||||
Config(unsigned short port) : port(port)
|
||||
{}
|
||||
Config(unsigned short port) noexcept : port(port) {}
|
||||
|
||||
public:
|
||||
/// Port number to use. Defaults to 80 for HTTP and 443 for HTTPS.
|
||||
unsigned short port;
|
||||
/// Number of threads that the server will use when start() is called. Defaults to 1 thread.
|
||||
/// If io_service is not set, number of threads that the server will use when start() is called.
|
||||
/// Defaults to 1 thread.
|
||||
size_t thread_pool_size = 1;
|
||||
/// Timeout on request handling. Defaults to 5 seconds.
|
||||
size_t timeout_request = 5;
|
||||
long timeout_request = 5;
|
||||
/// Timeout on content handling. Defaults to 300 seconds.
|
||||
size_t timeout_content = 300;
|
||||
long timeout_content = 300;
|
||||
/// IPv4 address in dotted decimal form or IPv6 address in hexadecimal notation.
|
||||
/// If empty, the address will be any address.
|
||||
std::string address;
|
||||
/// Set to false to avoid binding the socket to an address that is already in use. Defaults to true.
|
||||
bool reuse_address = true;
|
||||
};
|
||||
|
||||
///Set before calling start().
|
||||
/// Set before calling start().
|
||||
Config config;
|
||||
|
||||
private:
|
||||
class regex_orderable : public std::regex
|
||||
{
|
||||
class regex_orderable : public std::regex {
|
||||
std::string str;
|
||||
|
||||
public:
|
||||
regex_orderable(const char *regex_cstr) : std::regex(regex_cstr), str(regex_cstr)
|
||||
{}
|
||||
|
||||
regex_orderable(const std::string ®ex_str) : std::regex(regex_str), str(regex_str)
|
||||
{}
|
||||
|
||||
bool operator<(const regex_orderable &rhs) const
|
||||
{
|
||||
regex_orderable(const char *regex_cstr) : std::regex(regex_cstr), str(regex_cstr) {}
|
||||
regex_orderable(std::string regex_str) : std::regex(regex_str), str(std::move(regex_str)) {}
|
||||
bool operator<(const regex_orderable &rhs) const noexcept {
|
||||
return str < rhs.str;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
/// Warning: do not add or remove resources after start() is called
|
||||
std::map<regex_orderable, std::map<std::string,
|
||||
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>,
|
||||
std::shared_ptr<typename ServerBase<socket_type>::Request>)>>>
|
||||
resource;
|
||||
std::map<regex_orderable, std::map<std::string, std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>, std::shared_ptr<typename ServerBase<socket_type>::Request>)>>> resource;
|
||||
|
||||
std::map<std::string,
|
||||
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>,
|
||||
std::shared_ptr<typename ServerBase<socket_type>::Request>)>> default_resource;
|
||||
std::map<std::string, std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>, std::shared_ptr<typename ServerBase<socket_type>::Request>)>> default_resource;
|
||||
|
||||
std::function<
|
||||
void(std::shared_ptr<typename ServerBase<socket_type>::Request>,
|
||||
const boost::system::error_code &)>
|
||||
on_error;
|
||||
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Request>, const error_code &)> on_error;
|
||||
|
||||
std::function<void(std::shared_ptr<socket_type> socket,
|
||||
std::shared_ptr<typename ServerBase<socket_type>::Request>)> on_upgrade;
|
||||
std::function<void(std::unique_ptr<socket_type> &, std::shared_ptr<typename ServerBase<socket_type>::Request>)> on_upgrade;
|
||||
|
||||
virtual void start()
|
||||
{
|
||||
if (!io_service)
|
||||
io_service = std::make_shared<boost::asio::io_service>();
|
||||
/// If you have your own asio::io_service, store its pointer here before running start().
|
||||
std::shared_ptr<asio::io_service> io_service;
|
||||
|
||||
if (io_service->stopped())
|
||||
virtual void start() {
|
||||
if(!io_service) {
|
||||
io_service = std::make_shared<asio::io_service>();
|
||||
internal_io_service = true;
|
||||
}
|
||||
|
||||
if(io_service->stopped())
|
||||
io_service->reset();
|
||||
|
||||
boost::asio::ip::tcp::endpoint endpoint;
|
||||
if (config.address.size() > 0)
|
||||
endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(config.address),
|
||||
config.port);
|
||||
asio::ip::tcp::endpoint endpoint;
|
||||
if(config.address.size() > 0)
|
||||
endpoint = asio::ip::tcp::endpoint(asio::ip::address::from_string(config.address), config.port);
|
||||
else
|
||||
endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), config.port);
|
||||
endpoint = asio::ip::tcp::endpoint(asio::ip::tcp::v4(), config.port);
|
||||
|
||||
if (!acceptor)
|
||||
acceptor = std::unique_ptr<boost::asio::ip::tcp::acceptor>(
|
||||
new boost::asio::ip::tcp::acceptor(*io_service));
|
||||
if(!acceptor)
|
||||
acceptor = std::unique_ptr<asio::ip::tcp::acceptor>(new asio::ip::tcp::acceptor(*io_service));
|
||||
acceptor->open(endpoint.protocol());
|
||||
acceptor->set_option(boost::asio::socket_base::reuse_address(config.reuse_address));
|
||||
acceptor->set_option(asio::socket_base::reuse_address(config.reuse_address));
|
||||
acceptor->bind(endpoint);
|
||||
acceptor->listen();
|
||||
|
||||
accept();
|
||||
|
||||
//If thread_pool_size>1, start m_io_service.run() in (thread_pool_size-1) threads for thread-pooling
|
||||
threads.clear();
|
||||
for (size_t c = 1; c < config.thread_pool_size; c++)
|
||||
{
|
||||
threads.emplace_back([this]()
|
||||
{
|
||||
io_service->run();
|
||||
});
|
||||
}
|
||||
if(internal_io_service) {
|
||||
// If thread_pool_size>1, start m_io_service.run() in (thread_pool_size-1) threads for thread-pooling
|
||||
threads.clear();
|
||||
for(size_t c = 1; c < config.thread_pool_size; c++) {
|
||||
threads.emplace_back([this]() {
|
||||
this->io_service->run();
|
||||
});
|
||||
}
|
||||
|
||||
//Main thread
|
||||
if (config.thread_pool_size > 0)
|
||||
io_service->run();
|
||||
// Main thread
|
||||
if(config.thread_pool_size > 0)
|
||||
io_service->run();
|
||||
|
||||
//Wait for the rest of the threads, if any, to finish as well
|
||||
for (auto &t: threads)
|
||||
{
|
||||
t.join();
|
||||
// Wait for the rest of the threads, if any, to finish as well
|
||||
for(auto &t : threads)
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
acceptor->close();
|
||||
if (config.thread_pool_size > 0)
|
||||
io_service->stop();
|
||||
/// Stop accepting new requests, and close current connections.
|
||||
void stop() noexcept {
|
||||
if(acceptor) {
|
||||
error_code ec;
|
||||
acceptor->close(ec);
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(*connections_mutex);
|
||||
for(auto &connection : *connections)
|
||||
connection->close();
|
||||
connections->clear();
|
||||
}
|
||||
|
||||
if(internal_io_service)
|
||||
io_service->stop();
|
||||
}
|
||||
}
|
||||
|
||||
///Use this function if you need to recursively send parts of a longer message
|
||||
void send(const std::shared_ptr<Response> &response,
|
||||
const std::function<void(const boost::system::error_code &)> &callback = nullptr) const
|
||||
{
|
||||
boost::asio::async_write(*response->socket, response->streambuf, [this, response, callback]
|
||||
(const boost::system::error_code &ec, size_t /*bytes_transferred*/)
|
||||
{
|
||||
if (callback)
|
||||
callback(ec);
|
||||
});
|
||||
virtual ~ServerBase() noexcept {
|
||||
handler_runner->stop();
|
||||
stop();
|
||||
}
|
||||
|
||||
/// If you have your own boost::asio::io_service, store its pointer here before running start().
|
||||
/// You might also want to set config.thread_pool_size to 0.
|
||||
std::shared_ptr<boost::asio::io_service> io_service;
|
||||
protected:
|
||||
std::unique_ptr<boost::asio::ip::tcp::acceptor> acceptor;
|
||||
bool internal_io_service = false;
|
||||
|
||||
std::unique_ptr<asio::ip::tcp::acceptor> acceptor;
|
||||
std::vector<std::thread> threads;
|
||||
|
||||
ServerBase(unsigned short port) : config(port)
|
||||
{}
|
||||
std::shared_ptr<std::unordered_set<Connection *>> connections;
|
||||
std::shared_ptr<std::mutex> connections_mutex;
|
||||
|
||||
virtual void accept()=0;
|
||||
std::shared_ptr<ScopeRunner> handler_runner;
|
||||
|
||||
std::shared_ptr<boost::asio::deadline_timer>
|
||||
get_timeout_timer(const std::shared_ptr<socket_type> &socket, long seconds)
|
||||
{
|
||||
if (seconds == 0)
|
||||
return nullptr;
|
||||
ServerBase(unsigned short port) noexcept : config(port), connections(new std::unordered_set<Connection *>()), connections_mutex(new std::mutex()), handler_runner(new ScopeRunner()) {}
|
||||
|
||||
auto timer = std::make_shared<boost::asio::deadline_timer>(*io_service);
|
||||
timer->expires_from_now(boost::posix_time::seconds(seconds));
|
||||
timer->async_wait([socket](const boost::system::error_code &ec)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
|
||||
socket->lowest_layer().close();
|
||||
}
|
||||
});
|
||||
return timer;
|
||||
virtual void accept() = 0;
|
||||
|
||||
template <typename... Args>
|
||||
std::shared_ptr<Connection> create_connection(Args &&... args) noexcept {
|
||||
auto connections = this->connections;
|
||||
auto connections_mutex = this->connections_mutex;
|
||||
auto connection = std::shared_ptr<Connection>(new Connection(handler_runner, std::forward<Args>(args)...), [connections, connections_mutex](Connection *connection) {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(*connections_mutex);
|
||||
auto it = connections->find(connection);
|
||||
if(it != connections->end())
|
||||
connections->erase(it);
|
||||
}
|
||||
delete connection;
|
||||
});
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(*connections_mutex);
|
||||
connections->emplace(connection.get());
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
void read_request_and_content(const std::shared_ptr<socket_type> &socket)
|
||||
{
|
||||
//Create new streambuf (Request::streambuf) for async_read_until()
|
||||
//shared_ptr is used to pass temporary objects to the asynchronous functions
|
||||
std::shared_ptr<Request> request(new Request(*socket));
|
||||
void read_request_and_content(const std::shared_ptr<Session> &session) {
|
||||
session->connection->set_timeout(config.timeout_request);
|
||||
asio::async_read_until(*session->connection->socket, session->request->streambuf, "\r\n\r\n", [this, session](const error_code &ec, size_t bytes_transferred) {
|
||||
session->connection->cancel_timeout();
|
||||
auto lock = session->connection->handler_runner->continue_lock();
|
||||
if(!lock)
|
||||
return;
|
||||
if(!ec) {
|
||||
// request->streambuf.size() is not necessarily the same as bytes_transferred, from Boost-docs:
|
||||
// "After a successful async_read_until operation, the streambuf may contain additional data beyond the delimiter"
|
||||
// The chosen solution is to extract lines from the stream directly when parsing the header. What is left of the
|
||||
// streambuf (maybe some bytes of the content) is appended to in the async_read-function below (for retrieving content).
|
||||
size_t num_additional_bytes = session->request->streambuf.size() - bytes_transferred;
|
||||
|
||||
//Set timeout on the following boost::asio::async-read or write function
|
||||
auto timer = this->get_timeout_timer(socket, config.timeout_request);
|
||||
|
||||
boost::asio::async_read_until(*socket, request->streambuf, "\r\n\r\n", [this, socket, request, timer]
|
||||
(const boost::system::error_code &ec,
|
||||
size_t bytes_transferred)
|
||||
{
|
||||
if (timer)
|
||||
timer->cancel();
|
||||
if (!ec)
|
||||
{
|
||||
//request->streambuf.size() is not necessarily the same as bytes_transferred, from Boost-docs:
|
||||
//"After a successful async_read_until operation, the streambuf may contain additional data beyond the delimiter"
|
||||
//The chosen solution is to extract lines from the stream directly when parsing the header. What is left of the
|
||||
//streambuf (maybe some bytes of the content) is appended to in the async_read-function below (for retrieving content).
|
||||
size_t num_additional_bytes =
|
||||
request->streambuf.size() - bytes_transferred;
|
||||
|
||||
if (!this->parse_request(request))
|
||||
if(!RequestMessage::parse(session->request->content, session->request->method, session->request->path,
|
||||
session->request->query_string, session->request->http_version, session->request->header)) {
|
||||
if(this->on_error)
|
||||
this->on_error(session->request, make_error_code::make_error_code(errc::protocol_error));
|
||||
return;
|
||||
}
|
||||
|
||||
//If content, read that as well
|
||||
auto it = request->header.find("Content-Length");
|
||||
if (it != request->header.end())
|
||||
{
|
||||
unsigned long long content_length;
|
||||
try
|
||||
{
|
||||
// If content, read that as well
|
||||
auto it = session->request->header.find("Content-Length");
|
||||
if(it != session->request->header.end()) {
|
||||
unsigned long long content_length = 0;
|
||||
try {
|
||||
content_length = stoull(it->second);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
if (on_error)
|
||||
on_error(request, boost::system::error_code(
|
||||
boost::system::errc::protocol_error,
|
||||
boost::system::generic_category()));
|
||||
catch(const std::exception &e) {
|
||||
if(this->on_error)
|
||||
this->on_error(session->request, make_error_code::make_error_code(errc::protocol_error));
|
||||
return;
|
||||
}
|
||||
if (content_length > num_additional_bytes)
|
||||
{
|
||||
//Set timeout on the following boost::asio::async-read or write function
|
||||
auto timer = this->get_timeout_timer(socket,
|
||||
config.timeout_content);
|
||||
boost::asio::async_read(*socket, request->streambuf,
|
||||
boost::asio::transfer_exactly(
|
||||
content_length -
|
||||
num_additional_bytes),
|
||||
[this, socket, request, timer]
|
||||
(const boost::system::error_code &ec,
|
||||
size_t /*bytes_transferred*/)
|
||||
{
|
||||
if (timer)
|
||||
timer->cancel();
|
||||
if (!ec)
|
||||
this->find_resource(socket,
|
||||
request);
|
||||
else if (on_error)
|
||||
on_error(request, ec);
|
||||
});
|
||||
if(content_length > num_additional_bytes) {
|
||||
session->connection->set_timeout(config.timeout_content);
|
||||
asio::async_read(*session->connection->socket, session->request->streambuf, asio::transfer_exactly(content_length - num_additional_bytes), [this, session](const error_code &ec, size_t /*bytes_transferred*/) {
|
||||
session->connection->cancel_timeout();
|
||||
auto lock = session->connection->handler_runner->continue_lock();
|
||||
if(!lock)
|
||||
return;
|
||||
if(!ec)
|
||||
this->find_resource(session);
|
||||
else if(this->on_error)
|
||||
this->on_error(session->request, ec);
|
||||
});
|
||||
}
|
||||
else
|
||||
this->find_resource(socket, request);
|
||||
this->find_resource(session);
|
||||
}
|
||||
else
|
||||
this->find_resource(socket, request);
|
||||
this->find_resource(session);
|
||||
}
|
||||
else if (on_error)
|
||||
on_error(request, ec);
|
||||
else if(this->on_error)
|
||||
this->on_error(session->request, ec);
|
||||
});
|
||||
}
|
||||
|
||||
bool parse_request(const std::shared_ptr<Request> &request) const
|
||||
{
|
||||
std::string line;
|
||||
getline(request->content, line);
|
||||
size_t method_end;
|
||||
if ((method_end = line.find(' ')) != std::string::npos)
|
||||
{
|
||||
size_t path_end;
|
||||
if ((path_end = line.find(' ', method_end + 1)) != std::string::npos)
|
||||
{
|
||||
request->method = line.substr(0, method_end);
|
||||
request->path = line.substr(method_end + 1, path_end - method_end - 1);
|
||||
|
||||
size_t protocol_end;
|
||||
if ((protocol_end = line.find('/', path_end + 1)) != std::string::npos)
|
||||
void find_resource(const std::shared_ptr<Session> &session) {
|
||||
// Upgrade connection
|
||||
if(on_upgrade) {
|
||||
auto it = session->request->header.find("Upgrade");
|
||||
if(it != session->request->header.end()) {
|
||||
// remove connection from connections
|
||||
{
|
||||
if (line.compare(path_end + 1, protocol_end - path_end - 1, "HTTP") != 0)
|
||||
return false;
|
||||
request->http_version = line.substr(protocol_end + 1, line.size() - protocol_end - 2);
|
||||
std::unique_lock<std::mutex> lock(*connections_mutex);
|
||||
auto it = connections->find(session->connection.get());
|
||||
if(it != connections->end())
|
||||
connections->erase(it);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
getline(request->content, line);
|
||||
size_t param_end;
|
||||
while ((param_end = line.find(':')) != std::string::npos)
|
||||
{
|
||||
size_t value_start = param_end + 1;
|
||||
if ((value_start) < line.size())
|
||||
{
|
||||
if (line[value_start] == ' ')
|
||||
value_start++;
|
||||
if (value_start < line.size())
|
||||
request->header.emplace(line.substr(0, param_end),
|
||||
line.substr(value_start, line.size() - value_start - 1));
|
||||
}
|
||||
|
||||
getline(request->content, line);
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void find_resource(const std::shared_ptr<socket_type> &socket, const std::shared_ptr<Request> &request)
|
||||
{
|
||||
//Upgrade connection
|
||||
if (on_upgrade)
|
||||
{
|
||||
auto it = request->header.find("Upgrade");
|
||||
if (it != request->header.end())
|
||||
{
|
||||
on_upgrade(socket, request);
|
||||
on_upgrade(session->connection->socket, session->request);
|
||||
return;
|
||||
}
|
||||
}
|
||||
//Find path- and method-match, and call write_response
|
||||
for (auto ®ex_method: resource)
|
||||
{
|
||||
auto it = regex_method.second.find(request->method);
|
||||
if (it != regex_method.second.end())
|
||||
{
|
||||
// Find path- and method-match, and call write_response
|
||||
for(auto ®ex_method : resource) {
|
||||
auto it = regex_method.second.find(session->request->method);
|
||||
if(it != regex_method.second.end()) {
|
||||
std::smatch sm_res;
|
||||
if (std::regex_match(request->path, sm_res, regex_method.first))
|
||||
{
|
||||
request->path_match = std::move(sm_res);
|
||||
write_response(socket, request, it->second);
|
||||
if(std::regex_match(session->request->path, sm_res, regex_method.first)) {
|
||||
session->request->path_match = std::move(sm_res);
|
||||
write_response(session, it->second);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto it = default_resource.find(request->method);
|
||||
if (it != default_resource.end())
|
||||
{
|
||||
write_response(socket, request, it->second);
|
||||
}
|
||||
auto it = default_resource.find(session->request->method);
|
||||
if(it != default_resource.end())
|
||||
write_response(session, it->second);
|
||||
}
|
||||
|
||||
void write_response(const std::shared_ptr<socket_type> &socket, const std::shared_ptr<Request> &request,
|
||||
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>,
|
||||
std::shared_ptr<
|
||||
typename ServerBase<socket_type>::Request>)> &resource_function)
|
||||
{
|
||||
//Set timeout on the following boost::asio::async-read or write function
|
||||
auto timer = this->get_timeout_timer(socket, config.timeout_content);
|
||||
|
||||
auto response = std::shared_ptr<Response>(new Response(socket), [this, request, timer]
|
||||
(Response *response_ptr)
|
||||
{
|
||||
void write_response(const std::shared_ptr<Session> &session,
|
||||
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>, std::shared_ptr<typename ServerBase<socket_type>::Request>)> &resource_function) {
|
||||
session->connection->set_timeout(config.timeout_content);
|
||||
auto response = std::shared_ptr<Response>(new Response(session, config.timeout_content), [this](Response *response_ptr) {
|
||||
auto response = std::shared_ptr<Response>(response_ptr);
|
||||
this->send(response, [this, response, request, timer](
|
||||
const boost::system::error_code &ec)
|
||||
{
|
||||
if (timer)
|
||||
timer->cancel();
|
||||
if (!ec)
|
||||
{
|
||||
if (response->close_connection_after_response)
|
||||
response->send([this, response](const error_code &ec) {
|
||||
if(!ec) {
|
||||
if(response->close_connection_after_response)
|
||||
return;
|
||||
|
||||
auto range = request->header.equal_range(
|
||||
"Connection");
|
||||
for (auto it = range.first; it != range.second; it++)
|
||||
{
|
||||
if (boost::iequals(it->second, "close"))
|
||||
{
|
||||
auto range = response->session->request->header.equal_range("Connection");
|
||||
for(auto it = range.first; it != range.second; it++) {
|
||||
if(case_insensitive_equal(it->second, "close"))
|
||||
return;
|
||||
}
|
||||
else if (boost::iequals(it->second, "keep-alive"))
|
||||
{
|
||||
this->read_request_and_content(
|
||||
response->socket);
|
||||
else if(case_insensitive_equal(it->second, "keep-alive")) {
|
||||
auto new_session = std::make_shared<Session>(response->session->connection);
|
||||
this->read_request_and_content(new_session);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (request->http_version >= "1.1")
|
||||
this->read_request_and_content(response->socket);
|
||||
if(response->session->request->http_version >= "1.1") {
|
||||
auto new_session = std::make_shared<Session>(response->session->connection);
|
||||
this->read_request_and_content(new_session);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (on_error)
|
||||
on_error(request, ec);
|
||||
else if(this->on_error)
|
||||
this->on_error(response->session->request, ec);
|
||||
});
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
resource_function(response, request);
|
||||
try {
|
||||
resource_function(response, session->request);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
if (on_error)
|
||||
on_error(request, boost::system::error_code(boost::system::errc::operation_canceled,
|
||||
boost::system::generic_category()));
|
||||
catch(const std::exception &e) {
|
||||
if(on_error)
|
||||
on_error(session->request, make_error_code::make_error_code(errc::operation_canceled));
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif //BASE_SERVER_HPP
|
||||
}
|
|
@ -1,55 +1,42 @@
|
|||
/*
|
||||
* https://github.com/eidheim/Simple-Web-Server/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
* Copyright (c) 2014-2016 Ole Christian Eidheim
|
||||
*/
|
||||
|
||||
#ifndef SERVER_HTTP_HPP
|
||||
#define SERVER_HTTP_HPP
|
||||
#pragma once
|
||||
|
||||
#include "base_server.hpp"
|
||||
|
||||
namespace SimpleWeb
|
||||
{
|
||||
namespace SimpleWeb {
|
||||
|
||||
template<class socket_type>
|
||||
template <class socket_type>
|
||||
class Server : public ServerBase<socket_type> {};
|
||||
|
||||
typedef boost::asio::ip::tcp::socket HTTP;
|
||||
using HTTP = asio::ip::tcp::socket;
|
||||
|
||||
template<>
|
||||
class Server<HTTP> : public ServerBase<HTTP>
|
||||
{
|
||||
template <>
|
||||
class Server<HTTP> : public ServerBase<HTTP> {
|
||||
public:
|
||||
Server() : ServerBase<HTTP>::ServerBase(80)
|
||||
{}
|
||||
Server() noexcept : ServerBase<HTTP>::ServerBase(80) {}
|
||||
|
||||
protected:
|
||||
virtual void accept()
|
||||
{
|
||||
//Create new socket for this connection
|
||||
//Shared_ptr is used to pass temporary objects to the asynchronous functions
|
||||
auto socket = std::make_shared<HTTP>(*io_service);
|
||||
void accept() override {
|
||||
auto session = std::make_shared<Session>(create_connection(*io_service));
|
||||
|
||||
acceptor->async_accept(*socket, [this, socket](const boost::system::error_code &ec)
|
||||
{
|
||||
//Immediately start accepting a new connection (if io_service hasn't been stopped)
|
||||
if (ec != boost::asio::error::operation_aborted)
|
||||
accept();
|
||||
acceptor->async_accept(*session->connection->socket, [this, session](const error_code &ec) {
|
||||
auto lock = session->connection->handler_runner->continue_lock();
|
||||
if(!lock)
|
||||
return;
|
||||
|
||||
if (!ec)
|
||||
{
|
||||
boost::asio::ip::tcp::no_delay option(true);
|
||||
socket->set_option(option);
|
||||
// Immediately start accepting a new connection (unless io_service has been stopped)
|
||||
if(ec != asio::error::operation_aborted)
|
||||
this->accept();
|
||||
|
||||
this->read_request_and_content(socket);
|
||||
if(!ec) {
|
||||
asio::ip::tcp::no_delay option(true);
|
||||
error_code ec;
|
||||
session->connection->socket->set_option(option, ec);
|
||||
|
||||
this->read_request_and_content(session);
|
||||
}
|
||||
else if (on_error)
|
||||
on_error(std::shared_ptr<Request>(new Request(*socket)), ec);
|
||||
else if(this->on_error)
|
||||
this->on_error(session->request, ec);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //SERVER_HTTP_HPP
|
||||
} // namespace SimpleWeb
|
||||
|
|
|
@ -1,91 +1,82 @@
|
|||
#ifndef HTTPS_SERVER_HPP
|
||||
#define HTTPS_SERVER_HPP
|
||||
#pragma once
|
||||
|
||||
#include "base_server.hpp"
|
||||
|
||||
#ifdef USE_STANDALONE_ASIO
|
||||
#include <asio/ssl.hpp>
|
||||
#else
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <openssl/ssl.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
namespace SimpleWeb
|
||||
{
|
||||
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> HTTPS;
|
||||
namespace SimpleWeb {
|
||||
using HTTPS = asio::ssl::stream<asio::ip::tcp::socket>;
|
||||
|
||||
template<>
|
||||
class Server<HTTPS> : public ServerBase<HTTPS>
|
||||
{
|
||||
template <>
|
||||
class Server<HTTPS> : public ServerBase<HTTPS> {
|
||||
std::string session_id_context;
|
||||
bool set_session_id_context = false;
|
||||
public:
|
||||
Server(const std::string &cert_file, const std::string &private_key_file,
|
||||
const std::string &verify_file = std::string()) : ServerBase<HTTPS>::ServerBase(443),
|
||||
context(boost::asio::ssl::context::tlsv12)
|
||||
{
|
||||
context.use_certificate_chain_file(cert_file);
|
||||
context.use_private_key_file(private_key_file, boost::asio::ssl::context::pem);
|
||||
|
||||
if (verify_file.size() > 0)
|
||||
{
|
||||
public:
|
||||
Server(const std::string &cert_file, const std::string &private_key_file, const std::string &verify_file = std::string())
|
||||
: ServerBase<HTTPS>::ServerBase(443), context(asio::ssl::context::tlsv12) {
|
||||
context.use_certificate_chain_file(cert_file);
|
||||
context.use_private_key_file(private_key_file, asio::ssl::context::pem);
|
||||
|
||||
if(verify_file.size() > 0) {
|
||||
context.load_verify_file(verify_file);
|
||||
context.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert |
|
||||
boost::asio::ssl::verify_client_once);
|
||||
context.set_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert | asio::ssl::verify_client_once);
|
||||
set_session_id_context = true;
|
||||
}
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
if (set_session_id_context)
|
||||
{
|
||||
void start() override {
|
||||
if(set_session_id_context) {
|
||||
// Creating session_id_context from address:port but reversed due to small SSL_MAX_SSL_SESSION_ID_LENGTH
|
||||
session_id_context = std::to_string(config.port) + ':';
|
||||
session_id_context.append(config.address.rbegin(), config.address.rend());
|
||||
SSL_CTX_set_session_id_context(context.native_handle(),
|
||||
reinterpret_cast<const unsigned char *>(session_id_context.data()),
|
||||
std::min<size_t>(session_id_context.size(),
|
||||
SSL_MAX_SSL_SESSION_ID_LENGTH));
|
||||
SSL_CTX_set_session_id_context(context.native_handle(), reinterpret_cast<const unsigned char *>(session_id_context.data()),
|
||||
std::min<size_t>(session_id_context.size(), SSL_MAX_SSL_SESSION_ID_LENGTH));
|
||||
}
|
||||
ServerBase::start();
|
||||
}
|
||||
|
||||
protected:
|
||||
boost::asio::ssl::context context;
|
||||
asio::ssl::context context;
|
||||
|
||||
virtual void accept()
|
||||
{
|
||||
//Create new socket for this connection
|
||||
//Shared_ptr is used to pass temporary objects to the asynchronous functions
|
||||
auto socket = std::make_shared<HTTPS>(*io_service, context);
|
||||
void accept() override {
|
||||
auto session = std::make_shared<Session>(create_connection(*io_service, context));
|
||||
|
||||
acceptor->async_accept((*socket).lowest_layer(), [this, socket](const boost::system::error_code &ec)
|
||||
{
|
||||
//Immediately start accepting a new connection (if io_service hasn't been stopped)
|
||||
if (ec != boost::asio::error::operation_aborted)
|
||||
accept();
|
||||
acceptor->async_accept(session->connection->socket->lowest_layer(), [this, session](const error_code &ec) {
|
||||
auto lock = session->connection->handler_runner->continue_lock();
|
||||
if(!lock)
|
||||
return;
|
||||
|
||||
if(ec != asio::error::operation_aborted)
|
||||
this->accept();
|
||||
|
||||
if (!ec)
|
||||
{
|
||||
boost::asio::ip::tcp::no_delay option(true);
|
||||
socket->lowest_layer().set_option(option);
|
||||
if(!ec) {
|
||||
asio::ip::tcp::no_delay option(true);
|
||||
error_code ec;
|
||||
session->connection->socket->lowest_layer().set_option(option, ec);
|
||||
|
||||
//Set timeout on the following boost::asio::ssl::stream::async_handshake
|
||||
auto timer = get_timeout_timer(socket, config.timeout_request);
|
||||
socket->async_handshake(boost::asio::ssl::stream_base::server, [this, socket, timer]
|
||||
(const boost::system::error_code &ec)
|
||||
{
|
||||
if (timer)
|
||||
timer->cancel();
|
||||
if (!ec)
|
||||
read_request_and_content(socket);
|
||||
else if (on_error)
|
||||
on_error(std::shared_ptr<Request>(new Request(*socket)), ec);
|
||||
session->connection->set_timeout(config.timeout_request);
|
||||
session->connection->socket->async_handshake(asio::ssl::stream_base::server, [this, session](const error_code &ec) {
|
||||
session->connection->cancel_timeout();
|
||||
auto lock = session->connection->handler_runner->continue_lock();
|
||||
if(!lock)
|
||||
return;
|
||||
if(!ec)
|
||||
this->read_request_and_content(session);
|
||||
else if(this->on_error)
|
||||
this->on_error(session->request, ec);
|
||||
});
|
||||
}
|
||||
else if (on_error)
|
||||
on_error(std::shared_ptr<Request>(new Request(*socket)), ec);
|
||||
else if(this->on_error)
|
||||
this->on_error(session->request, ec);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //HTTPS_SERVER_HPP
|
||||
} // namespace SimpleWeb
|
||||
|
|
154
apps/master/SimpleWeb/status_code.hpp
Normal file
154
apps/master/SimpleWeb/status_code.hpp
Normal file
|
@ -0,0 +1,154 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace SimpleWeb {
|
||||
enum class StatusCode {
|
||||
unknown = 0,
|
||||
information_continue = 100,
|
||||
information_switching_protocols,
|
||||
information_processing,
|
||||
success_ok = 200,
|
||||
success_created,
|
||||
success_accepted,
|
||||
success_non_authoritative_information,
|
||||
success_no_content,
|
||||
success_reset_content,
|
||||
success_partial_content,
|
||||
success_multi_status,
|
||||
success_already_reported,
|
||||
success_im_used = 226,
|
||||
redirection_multiple_choices = 300,
|
||||
redirection_moved_permanently,
|
||||
redirection_found,
|
||||
redirection_see_other,
|
||||
redirection_not_modified,
|
||||
redirection_use_proxy,
|
||||
redirection_switch_proxy,
|
||||
redirection_temporary_redirect,
|
||||
redirection_permanent_redirect,
|
||||
client_error_bad_request = 400,
|
||||
client_error_unauthorized,
|
||||
client_error_payment_required,
|
||||
client_error_forbidden,
|
||||
client_error_not_found,
|
||||
client_error_method_not_allowed,
|
||||
client_error_not_acceptable,
|
||||
client_error_proxy_authentication_required,
|
||||
client_error_request_timeout,
|
||||
client_error_conflict,
|
||||
client_error_gone,
|
||||
client_error_length_required,
|
||||
client_error_precondition_failed,
|
||||
client_error_payload_too_large,
|
||||
client_error_uri_too_long,
|
||||
client_error_unsupported_media_type,
|
||||
client_error_range_not_satisfiable,
|
||||
client_error_expectation_failed,
|
||||
client_error_im_a_teapot,
|
||||
client_error_misdirection_required = 421,
|
||||
client_error_unprocessable_entity,
|
||||
client_error_locked,
|
||||
client_error_failed_dependency,
|
||||
client_error_upgrade_required = 426,
|
||||
client_error_precondition_required = 428,
|
||||
client_error_too_many_requests,
|
||||
client_error_request_header_fields_too_large = 431,
|
||||
client_error_unavailable_for_legal_reasons = 451,
|
||||
server_error_internal_server_error = 500,
|
||||
server_error_not_implemented,
|
||||
server_error_bad_gateway,
|
||||
server_error_service_unavailable,
|
||||
server_error_gateway_timeout,
|
||||
server_error_http_version_not_supported,
|
||||
server_error_variant_also_negotiates,
|
||||
server_error_insufficient_storage,
|
||||
server_error_loop_detected,
|
||||
server_error_not_extended = 510,
|
||||
server_error_network_authentication_required
|
||||
};
|
||||
|
||||
const static std::vector<std::pair<StatusCode, std::string>> &status_codes() noexcept {
|
||||
const static std::vector<std::pair<StatusCode, std::string>> status_codes = {
|
||||
{StatusCode::unknown, ""},
|
||||
{StatusCode::information_continue, "100 Continue"},
|
||||
{StatusCode::information_switching_protocols, "101 Switching Protocols"},
|
||||
{StatusCode::information_processing, "102 Processing"},
|
||||
{StatusCode::success_ok, "200 OK"},
|
||||
{StatusCode::success_created, "201 Created"},
|
||||
{StatusCode::success_accepted, "202 Accepted"},
|
||||
{StatusCode::success_non_authoritative_information, "203 Non-Authoritative Information"},
|
||||
{StatusCode::success_no_content, "204 No Content"},
|
||||
{StatusCode::success_reset_content, "205 Reset Content"},
|
||||
{StatusCode::success_partial_content, "206 Partial Content"},
|
||||
{StatusCode::success_multi_status, "207 Multi-Status"},
|
||||
{StatusCode::success_already_reported, "208 Already Reported"},
|
||||
{StatusCode::success_im_used, "226 IM Used"},
|
||||
{StatusCode::redirection_multiple_choices, "300 Multiple Choices"},
|
||||
{StatusCode::redirection_moved_permanently, "301 Moved Permanently"},
|
||||
{StatusCode::redirection_found, "302 Found"},
|
||||
{StatusCode::redirection_see_other, "303 See Other"},
|
||||
{StatusCode::redirection_not_modified, "304 Not Modified"},
|
||||
{StatusCode::redirection_use_proxy, "305 Use Proxy"},
|
||||
{StatusCode::redirection_switch_proxy, "306 Switch Proxy"},
|
||||
{StatusCode::redirection_temporary_redirect, "307 Temporary Redirect"},
|
||||
{StatusCode::redirection_permanent_redirect, "308 Permanent Redirect"},
|
||||
{StatusCode::client_error_bad_request, "400 Bad Request"},
|
||||
{StatusCode::client_error_unauthorized, "401 Unauthorized"},
|
||||
{StatusCode::client_error_payment_required, "402 Payment Required"},
|
||||
{StatusCode::client_error_forbidden, "403 Forbidden"},
|
||||
{StatusCode::client_error_not_found, "404 Not Found"},
|
||||
{StatusCode::client_error_method_not_allowed, "405 Method Not Allowed"},
|
||||
{StatusCode::client_error_not_acceptable, "406 Not Acceptable"},
|
||||
{StatusCode::client_error_proxy_authentication_required, "407 Proxy Authentication Required"},
|
||||
{StatusCode::client_error_request_timeout, "408 Request Timeout"},
|
||||
{StatusCode::client_error_conflict, "409 Conflict"},
|
||||
{StatusCode::client_error_gone, "410 Gone"},
|
||||
{StatusCode::client_error_length_required, "411 Length Required"},
|
||||
{StatusCode::client_error_precondition_failed, "412 Precondition Failed"},
|
||||
{StatusCode::client_error_payload_too_large, "413 Payload Too Large"},
|
||||
{StatusCode::client_error_uri_too_long, "414 URI Too Long"},
|
||||
{StatusCode::client_error_unsupported_media_type, "415 Unsupported Media Type"},
|
||||
{StatusCode::client_error_range_not_satisfiable, "416 Range Not Satisfiable"},
|
||||
{StatusCode::client_error_expectation_failed, "417 Expectation Failed"},
|
||||
{StatusCode::client_error_im_a_teapot, "418 I'm a teapot"},
|
||||
{StatusCode::client_error_misdirection_required, "421 Misdirected Request"},
|
||||
{StatusCode::client_error_unprocessable_entity, "422 Unprocessable Entity"},
|
||||
{StatusCode::client_error_locked, "423 Locked"},
|
||||
{StatusCode::client_error_failed_dependency, "424 Failed Dependency"},
|
||||
{StatusCode::client_error_upgrade_required, "426 Upgrade Required"},
|
||||
{StatusCode::client_error_precondition_required, "428 Precondition Required"},
|
||||
{StatusCode::client_error_too_many_requests, "429 Too Many Requests"},
|
||||
{StatusCode::client_error_request_header_fields_too_large, "431 Request Header Fields Too Large"},
|
||||
{StatusCode::client_error_unavailable_for_legal_reasons, "451 Unavailable For Legal Reasons"},
|
||||
{StatusCode::server_error_internal_server_error, "500 Internal Server Error"},
|
||||
{StatusCode::server_error_not_implemented, "501 Not Implemented"},
|
||||
{StatusCode::server_error_bad_gateway, "502 Bad Gateway"},
|
||||
{StatusCode::server_error_service_unavailable, "503 Service Unavailable"},
|
||||
{StatusCode::server_error_gateway_timeout, "504 Gateway Timeout"},
|
||||
{StatusCode::server_error_http_version_not_supported, "505 HTTP Version Not Supported"},
|
||||
{StatusCode::server_error_variant_also_negotiates, "506 Variant Also Negotiates"},
|
||||
{StatusCode::server_error_insufficient_storage, "507 Insufficient Storage"},
|
||||
{StatusCode::server_error_loop_detected, "508 Loop Detected"},
|
||||
{StatusCode::server_error_not_extended, "510 Not Extended"},
|
||||
{StatusCode::server_error_network_authentication_required, "511 Network Authentication Required"}};
|
||||
return status_codes;
|
||||
}
|
||||
|
||||
inline StatusCode status_code(const std::string &status_code_str) noexcept {
|
||||
for(auto &status_code : status_codes()) {
|
||||
if(status_code.second == status_code_str)
|
||||
return status_code.first;
|
||||
}
|
||||
return StatusCode::unknown;
|
||||
}
|
||||
|
||||
inline const std::string &status_code(StatusCode status_code_enum) noexcept {
|
||||
for(auto &status_code : status_codes()) {
|
||||
if(status_code.first == status_code_enum)
|
||||
return status_code.second;
|
||||
}
|
||||
return status_codes()[0].second;
|
||||
}
|
||||
} // namespace SimpleWeb
|
340
apps/master/SimpleWeb/utility.hpp
Normal file
340
apps/master/SimpleWeb/utility.hpp
Normal file
|
@ -0,0 +1,340 @@
|
|||
#pragma once
|
||||
|
||||
#include "status_code.hpp"
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace SimpleWeb {
|
||||
inline bool case_insensitive_equal(const std::string &str1, const std::string &str2) noexcept {
|
||||
return str1.size() == str2.size() &&
|
||||
std::equal(str1.begin(), str1.end(), str2.begin(), [](char a, char b) {
|
||||
return tolower(a) == tolower(b);
|
||||
});
|
||||
}
|
||||
class CaseInsensitiveEqual {
|
||||
public:
|
||||
bool operator()(const std::string &str1, const std::string &str2) const noexcept {
|
||||
return case_insensitive_equal(str1, str2);
|
||||
}
|
||||
};
|
||||
// Based on https://stackoverflow.com/questions/2590677/how-do-i-combine-hash-values-in-c0x/2595226#2595226
|
||||
class CaseInsensitiveHash {
|
||||
public:
|
||||
size_t operator()(const std::string &str) const noexcept {
|
||||
size_t h = 0;
|
||||
std::hash<int> hash;
|
||||
for(auto c : str)
|
||||
h ^= hash(tolower(c)) + 0x9e3779b9 + (h << 6) + (h >> 2);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
using CaseInsensitiveMultimap = std::unordered_multimap<std::string, std::string, CaseInsensitiveHash, CaseInsensitiveEqual>;
|
||||
|
||||
/// Percent encoding and decoding
|
||||
class Percent {
|
||||
public:
|
||||
/// Returns percent-encoded string
|
||||
static std::string encode(const std::string &value) noexcept {
|
||||
static auto hex_chars = "0123456789ABCDEF";
|
||||
|
||||
std::string result;
|
||||
result.reserve(value.size()); // Minimum size of result
|
||||
|
||||
for(auto &chr : value) {
|
||||
if(chr == ' ')
|
||||
result += '+';
|
||||
else if(chr == '!' || chr == '#' || chr == '$' || (chr >= '&' && chr <= ',') || (chr >= '/' && chr <= ';') || chr == '=' || chr == '?' || chr == '@' || chr == '[' || chr == ']')
|
||||
result += std::string("%") + hex_chars[chr >> 4] + hex_chars[chr & 15];
|
||||
else
|
||||
result += chr;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Returns percent-decoded string
|
||||
static std::string decode(const std::string &value) noexcept {
|
||||
std::string result;
|
||||
result.reserve(value.size() / 3 + (value.size() % 3)); // Minimum size of result
|
||||
|
||||
for(size_t i = 0; i < value.size(); ++i) {
|
||||
auto &chr = value[i];
|
||||
if(chr == '%' && i + 2 < value.size()) {
|
||||
auto hex = value.substr(i + 1, 2);
|
||||
auto decoded_chr = static_cast<char>(std::strtol(hex.c_str(), nullptr, 16));
|
||||
result += decoded_chr;
|
||||
i += 2;
|
||||
}
|
||||
else if(chr == '+')
|
||||
result += ' ';
|
||||
else
|
||||
result += chr;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/// Query string creation and parsing
|
||||
class QueryString {
|
||||
public:
|
||||
/// Returns query string created from given field names and values
|
||||
static std::string create(const CaseInsensitiveMultimap &fields) noexcept {
|
||||
std::string result;
|
||||
|
||||
bool first = true;
|
||||
for(auto &field : fields) {
|
||||
result += (!first ? "&" : "") + field.first + '=' + Percent::encode(field.second);
|
||||
first = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Returns query keys with percent-decoded values.
|
||||
static CaseInsensitiveMultimap parse(const std::string &query_string) noexcept {
|
||||
CaseInsensitiveMultimap result;
|
||||
|
||||
if(query_string.empty())
|
||||
return result;
|
||||
|
||||
size_t name_pos = 0;
|
||||
auto name_end_pos = std::string::npos;
|
||||
auto value_pos = std::string::npos;
|
||||
for(size_t c = 0; c < query_string.size(); ++c) {
|
||||
if(query_string[c] == '&') {
|
||||
auto name = query_string.substr(name_pos, (name_end_pos == std::string::npos ? c : name_end_pos) - name_pos);
|
||||
if(!name.empty()) {
|
||||
auto value = value_pos == std::string::npos ? std::string() : query_string.substr(value_pos, c - value_pos);
|
||||
result.emplace(std::move(name), Percent::decode(value));
|
||||
}
|
||||
name_pos = c + 1;
|
||||
name_end_pos = std::string::npos;
|
||||
value_pos = std::string::npos;
|
||||
}
|
||||
else if(query_string[c] == '=') {
|
||||
name_end_pos = c;
|
||||
value_pos = c + 1;
|
||||
}
|
||||
}
|
||||
if(name_pos < query_string.size()) {
|
||||
auto name = query_string.substr(name_pos, name_end_pos - name_pos);
|
||||
if(!name.empty()) {
|
||||
auto value = value_pos >= query_string.size() ? std::string() : query_string.substr(value_pos);
|
||||
result.emplace(std::move(name), Percent::decode(value));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
class HttpHeader {
|
||||
public:
|
||||
/// Parse header fields
|
||||
static CaseInsensitiveMultimap parse(std::istream &stream) noexcept {
|
||||
CaseInsensitiveMultimap result;
|
||||
std::string line;
|
||||
getline(stream, line);
|
||||
size_t param_end;
|
||||
while((param_end = line.find(':')) != std::string::npos) {
|
||||
size_t value_start = param_end + 1;
|
||||
if(value_start < line.size()) {
|
||||
if(line[value_start] == ' ')
|
||||
value_start++;
|
||||
if(value_start < line.size())
|
||||
result.emplace(line.substr(0, param_end), line.substr(value_start, line.size() - value_start - 1));
|
||||
}
|
||||
|
||||
getline(stream, line);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
class RequestMessage {
|
||||
public:
|
||||
/// Parse request line and header fields
|
||||
static bool parse(std::istream &stream, std::string &method, std::string &path, std::string &query_string, std::string &version, CaseInsensitiveMultimap &header) noexcept {
|
||||
header.clear();
|
||||
std::string line;
|
||||
getline(stream, line);
|
||||
size_t method_end;
|
||||
if((method_end = line.find(' ')) != std::string::npos) {
|
||||
method = line.substr(0, method_end);
|
||||
|
||||
size_t query_start = std::string::npos;
|
||||
size_t path_and_query_string_end = std::string::npos;
|
||||
for(size_t i = method_end + 1; i < line.size(); ++i) {
|
||||
if(line[i] == '?' && (i + 1) < line.size())
|
||||
query_start = i + 1;
|
||||
else if(line[i] == ' ') {
|
||||
path_and_query_string_end = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(path_and_query_string_end != std::string::npos) {
|
||||
if(query_start != std::string::npos) {
|
||||
path = line.substr(method_end + 1, query_start - method_end - 2);
|
||||
query_string = line.substr(query_start, path_and_query_string_end - query_start);
|
||||
}
|
||||
else
|
||||
path = line.substr(method_end + 1, path_and_query_string_end - method_end - 1);
|
||||
|
||||
size_t protocol_end;
|
||||
if((protocol_end = line.find('/', path_and_query_string_end + 1)) != std::string::npos) {
|
||||
if(line.compare(path_and_query_string_end + 1, protocol_end - path_and_query_string_end - 1, "HTTP") != 0)
|
||||
return false;
|
||||
version = line.substr(protocol_end + 1, line.size() - protocol_end - 2);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
header = HttpHeader::parse(stream);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class ResponseMessage {
|
||||
public:
|
||||
/// Parse status line and header fields
|
||||
static bool parse(std::istream &stream, std::string &version, std::string &status_code, CaseInsensitiveMultimap &header) noexcept {
|
||||
header.clear();
|
||||
std::string line;
|
||||
getline(stream, line);
|
||||
size_t version_end = line.find(' ');
|
||||
if(version_end != std::string::npos) {
|
||||
if(5 < line.size())
|
||||
version = line.substr(5, version_end - 5);
|
||||
else
|
||||
return false;
|
||||
if((version_end + 1) < line.size())
|
||||
status_code = line.substr(version_end + 1, line.size() - (version_end + 1) - 1);
|
||||
else
|
||||
return false;
|
||||
|
||||
header = HttpHeader::parse(stream);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class ContentDisposition {
|
||||
public:
|
||||
/// Can be used to parse the Content-Disposition header field value when
|
||||
/// clients are posting requests with enctype="multipart/form-data"
|
||||
static CaseInsensitiveMultimap parse(const std::string &line) {
|
||||
CaseInsensitiveMultimap result;
|
||||
|
||||
size_t para_start_pos = 0;
|
||||
size_t para_end_pos = std::string::npos;
|
||||
size_t value_start_pos = std::string::npos;
|
||||
for(size_t c = 0; c < line.size(); ++c) {
|
||||
if(para_start_pos != std::string::npos) {
|
||||
if(para_end_pos == std::string::npos) {
|
||||
if(line[c] == ';') {
|
||||
result.emplace(line.substr(para_start_pos, c - para_start_pos), std::string());
|
||||
para_start_pos = std::string::npos;
|
||||
}
|
||||
else if(line[c] == '=')
|
||||
para_end_pos = c;
|
||||
}
|
||||
else {
|
||||
if(value_start_pos == std::string::npos) {
|
||||
if(line[c] == '"' && c + 1 < line.size())
|
||||
value_start_pos = c + 1;
|
||||
}
|
||||
else if(line[c] == '"') {
|
||||
result.emplace(line.substr(para_start_pos, para_end_pos - para_start_pos), line.substr(value_start_pos, c - value_start_pos));
|
||||
para_start_pos = std::string::npos;
|
||||
para_end_pos = std::string::npos;
|
||||
value_start_pos = std::string::npos;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(line[c] != ' ' && line[c] != ';')
|
||||
para_start_pos = c;
|
||||
}
|
||||
if(para_start_pos != std::string::npos && para_end_pos == std::string::npos)
|
||||
result.emplace(line.substr(para_start_pos), std::string());
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
} // namespace SimpleWeb
|
||||
|
||||
#ifdef __SSE2__
|
||||
#include <emmintrin.h>
|
||||
namespace SimpleWeb {
|
||||
inline void spin_loop_pause() noexcept { _mm_pause(); }
|
||||
} // namespace SimpleWeb
|
||||
// TODO: need verification that the following checks are correct:
|
||||
#elif defined(_MSC_VER) && _MSC_VER >= 1800 && (defined(_M_X64) || defined(_M_IX86))
|
||||
#include <intrin.h>
|
||||
namespace SimpleWeb {
|
||||
inline void spin_loop_pause() noexcept { _mm_pause(); }
|
||||
} // namespace SimpleWeb
|
||||
#else
|
||||
namespace SimpleWeb {
|
||||
inline void spin_loop_pause() noexcept {}
|
||||
} // namespace SimpleWeb
|
||||
#endif
|
||||
|
||||
namespace SimpleWeb {
|
||||
/// Makes it possible to for instance cancel Asio handlers without stopping asio::io_service
|
||||
class ScopeRunner {
|
||||
/// Scope count that is set to -1 if scopes are to be canceled
|
||||
std::atomic<long> count;
|
||||
|
||||
public:
|
||||
class SharedLock {
|
||||
friend class ScopeRunner;
|
||||
std::atomic<long> &count;
|
||||
SharedLock(std::atomic<long> &count) noexcept : count(count) {}
|
||||
SharedLock &operator=(const SharedLock &) = delete;
|
||||
SharedLock(const SharedLock &) = delete;
|
||||
|
||||
public:
|
||||
~SharedLock() noexcept {
|
||||
count.fetch_sub(1);
|
||||
}
|
||||
};
|
||||
|
||||
ScopeRunner() noexcept : count(0) {}
|
||||
|
||||
/// Returns nullptr if scope should be exited, or a shared lock otherwise
|
||||
std::unique_ptr<SharedLock> continue_lock() noexcept {
|
||||
long expected = count;
|
||||
while(expected >= 0 && !count.compare_exchange_weak(expected, expected + 1))
|
||||
spin_loop_pause();
|
||||
|
||||
if(expected < 0)
|
||||
return nullptr;
|
||||
else
|
||||
return std::unique_ptr<SharedLock>(new SharedLock(count));
|
||||
}
|
||||
|
||||
/// Blocks until all shared locks are released, then prevents future shared locks
|
||||
void stop() noexcept {
|
||||
long expected = 0;
|
||||
while(!count.compare_exchange_weak(expected, -1)) {
|
||||
if(expected < 0)
|
||||
return;
|
||||
expected = 0;
|
||||
spin_loop_pause();
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace SimpleWeb
|
|
@ -1,36 +1,74 @@
|
|||
#include <iostream>
|
||||
#include <Kbhit.h>
|
||||
#include <RakSleep.h>
|
||||
#include <sol.hpp>
|
||||
#include "MasterServer.hpp"
|
||||
#include "RestServer.hpp"
|
||||
#include "AdminRest.hpp"
|
||||
|
||||
using namespace RakNet;
|
||||
using namespace std;
|
||||
|
||||
unique_ptr<RestServer> restServer;
|
||||
unique_ptr<MasterServer> masterServer;
|
||||
bool run = true;
|
||||
shared_ptr<MasterServer> masterServer;
|
||||
unique_ptr<AdminRest> restAdminServer;
|
||||
|
||||
int main()
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
masterServer.reset(new MasterServer(2000, 25560));
|
||||
restServer.reset(new RestServer(8080, masterServer->GetServers()));
|
||||
if (argc != 2)
|
||||
return 1;
|
||||
|
||||
string luaScript(argv[1]);
|
||||
|
||||
masterServer = make_shared<MasterServer>(luaScript);
|
||||
masterServer->luaStuff([](sol::state &state)
|
||||
{
|
||||
sol::table config = state["config"];
|
||||
sol::object restPort = config["restPort"];
|
||||
if (restPort.get_type() != sol::type::number)
|
||||
throw runtime_error("config.restPort is not correct");
|
||||
|
||||
|
||||
restServer = make_unique<RestServer>(restPort.as<unsigned short>(), masterServer->GetServers());
|
||||
|
||||
sol::object restAdminCert = config["restAdminCert"];
|
||||
if (restAdminCert.get_type() != sol::type::string)
|
||||
throw runtime_error("config.restAdminCert is not correct");
|
||||
|
||||
sol::object restAdminKey = config["restAdminKey"];
|
||||
if (restAdminKey.get_type() != sol::type::string)
|
||||
throw runtime_error("config.restAdminKey is not correct");
|
||||
|
||||
sol::object restAdminVerifyFile = config["restAdminVerifyFile"];
|
||||
if (restAdminVerifyFile.get_type() != sol::type::string)
|
||||
throw runtime_error("config.restAdminVerifyFile is not correct");
|
||||
|
||||
sol::object restAdminPort = config["restAdminPort"];
|
||||
if (restAdminPort.get_type() != sol::type::number)
|
||||
throw runtime_error("config.restAdminPort is not correct");
|
||||
|
||||
restAdminServer = make_unique<AdminRest>(restAdminCert.as<string>(), restAdminKey.as<string>(),
|
||||
restAdminVerifyFile.as<string>(), restAdminPort.as<unsigned short>(), masterServer);
|
||||
});
|
||||
|
||||
auto onExit = [](int /*sig*/){
|
||||
restServer->stop();
|
||||
restAdminServer->stop();
|
||||
masterServer->luaStuff([](sol::state &state) {
|
||||
sol::protected_function func = state["OnExit"];
|
||||
if (func.valid())
|
||||
func.call();
|
||||
});
|
||||
masterServer->Stop(false);
|
||||
masterServer->Wait();
|
||||
run = false;
|
||||
};
|
||||
|
||||
signal(SIGINT, onExit);
|
||||
signal(SIGTERM, onExit);
|
||||
|
||||
masterServer->Start();
|
||||
|
||||
thread server_thread([]() { restServer->start(); });
|
||||
|
||||
server_thread.join();
|
||||
restServer->start();
|
||||
restAdminServer->start();
|
||||
masterServer->Wait();
|
||||
|
||||
return 0;
|
||||
|
|
323
apps/openmw-mp/Actors.cpp
Normal file
323
apps/openmw-mp/Actors.cpp
Normal file
|
@ -0,0 +1,323 @@
|
|||
//
|
||||
// Created by koncord on 25.08.17.
|
||||
//
|
||||
|
||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
|
||||
#include "Script/LuaState.hpp"
|
||||
#include "Networking.hpp"
|
||||
|
||||
#include "Actors.hpp"
|
||||
#include "Cell.hpp"
|
||||
#include "CellController.hpp"
|
||||
#include "Player.hpp"
|
||||
#include "Players.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
void Actor::Init(LuaState &lua)
|
||||
{
|
||||
lua.getState()->new_usertype<Actor>("Actor",
|
||||
"getPosition", &NetActor::getPosition,
|
||||
"setPosition", &NetActor::setPosition,
|
||||
"getRotation", &NetActor::getRotation,
|
||||
"setRotation", &NetActor::setRotation,
|
||||
|
||||
"getHealth", &NetActor::getHealth,
|
||||
"setHealth", &NetActor::setHealth,
|
||||
"getMagicka", &NetActor::getMagicka,
|
||||
"setMagicka", &NetActor::setMagicka,
|
||||
"getFatigue", &NetActor::getFatigue,
|
||||
"setFatigue", &NetActor::setFatigue,
|
||||
|
||||
"getCell", &NetActor::getCell,
|
||||
"getInventory", &NetActor::getInventory,
|
||||
|
||||
"followPlayer", &Actor::followPlayer,
|
||||
"followActor", &Actor::followActor,
|
||||
|
||||
"refId", sol::property(&Actor::getRefId, &Actor::setRefId),
|
||||
"refNumIndex", sol::property(&Actor::getRefNumIndex, &Actor::setRefNumIndex),
|
||||
"mpNum", sol::property(&Actor::getMpNum, &Actor::setMpNum)
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
Actor::Actor() : NetActor()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Actor::followPlayer(int pid)
|
||||
{
|
||||
actor->aiAction = mwmp::BaseActor::AIAction::Follow;
|
||||
|
||||
actor->hasAiTarget = true;
|
||||
actor->aiTarget.isPlayer = true;
|
||||
|
||||
auto player = Players::getPlayerByPID(pid).get();
|
||||
actor->aiTarget.guid = player->guid;
|
||||
|
||||
aiChanged = true;
|
||||
}
|
||||
|
||||
void Actor::followActor(unsigned int refNumIndex, unsigned int mpNum)
|
||||
{
|
||||
actor->aiAction = mwmp::BaseActor::AIAction::Follow;
|
||||
|
||||
actor->hasAiTarget = true;
|
||||
actor->aiTarget.isPlayer = false;
|
||||
|
||||
actor->aiTarget.refNumIndex = refNumIndex;
|
||||
actor->aiTarget.mpNum = mpNum;
|
||||
|
||||
aiChanged = true;
|
||||
}
|
||||
|
||||
std::string Actor::getRefId() const
|
||||
{
|
||||
return actor->refId;
|
||||
}
|
||||
|
||||
void Actor::setRefId(const std::string &refId)
|
||||
{
|
||||
actor->refId = refId;
|
||||
}
|
||||
|
||||
unsigned Actor::getRefNumIndex() const
|
||||
{
|
||||
return actor->refNumIndex;
|
||||
}
|
||||
|
||||
void Actor::setRefNumIndex(unsigned refNumIndex)
|
||||
{
|
||||
actor->refNumIndex = refNumIndex;
|
||||
}
|
||||
|
||||
unsigned Actor::getMpNum() const
|
||||
{
|
||||
return actor->mpNum;
|
||||
}
|
||||
|
||||
void Actor::setMpNum(unsigned mpNum)
|
||||
{
|
||||
actor->mpNum = mpNum;
|
||||
}
|
||||
|
||||
bool Actor::doesHavePosition() const
|
||||
{
|
||||
return actor->hasPositionData;
|
||||
}
|
||||
|
||||
bool Actor::doesHaveStatsDynamic() const
|
||||
{
|
||||
return actor->hasStatsDynamicData;
|
||||
}
|
||||
|
||||
void ActorController::Init(LuaState &lua)
|
||||
{
|
||||
sol::table actorsTable = lua.getState()->create_named_table("Actors");
|
||||
|
||||
actorsTable.set_function("createActor", [&lua](){
|
||||
return lua.getActorCtrl().createActor();
|
||||
});
|
||||
|
||||
actorsTable.set_function("sendActors", [&lua](shared_ptr<Player> player, vector<shared_ptr<Actor>> actors,
|
||||
const std::string &cellDescription, bool sendToAll) {
|
||||
lua.getActorCtrl().sendActors(player, actors, Utils::getCellFromDescription(cellDescription), sendToAll);
|
||||
});
|
||||
|
||||
actorsTable.set_function("sendList", [&lua](shared_ptr<Player> player, vector<shared_ptr<Actor>> actors,
|
||||
const std::string &cellDescription, bool sendToAll) {
|
||||
lua.getActorCtrl().sendList(player, actors, Utils::getCellFromDescription(cellDescription), sendToAll);
|
||||
});
|
||||
|
||||
actorsTable.set_function("requestList", [&lua](shared_ptr<Player> player, const std::string &cellDescription){
|
||||
lua.getActorCtrl().requestList(player, Utils::getCellFromDescription(cellDescription));
|
||||
});
|
||||
|
||||
actorsTable.set_function("getActors", [&lua](shared_ptr<Player> player, const std::string &cellDescription){
|
||||
lua.getActorCtrl().getActors(player, Utils::getCellFromDescription(cellDescription));
|
||||
});
|
||||
}
|
||||
|
||||
ActorController::ActorController()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ActorController::~ActorController()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::shared_ptr<Actor> ActorController::createActor()
|
||||
{
|
||||
Actor *actor = new Actor();
|
||||
actor->actor.reset(new mwmp::BaseActor);
|
||||
|
||||
return shared_ptr<Actor>(actor);
|
||||
}
|
||||
|
||||
void ActorController::sendActors(std::shared_ptr<Player> player, std::vector<std::shared_ptr<Actor>> actors,
|
||||
const ESM::Cell &cell, bool sendToAll)
|
||||
{
|
||||
actorList.cell = cell;
|
||||
actorList.guid = player->guid;
|
||||
|
||||
bool positionChanged = false;
|
||||
bool statsChanged = false;
|
||||
/*bool attributesChanged = false;
|
||||
bool skillsChanged = false;
|
||||
bool baseInfoChanged = false;*/
|
||||
bool equipmentChanged = false;
|
||||
bool aiChanged = false;
|
||||
bool changedCell = false;
|
||||
|
||||
actorList.baseActors.clear();
|
||||
for (auto &actor : actors)
|
||||
{
|
||||
actorList.baseActors.push_back(actor->actor);
|
||||
|
||||
if (actor->positionChanged)
|
||||
positionChanged = true;
|
||||
if (actor->statsChanged)
|
||||
statsChanged = true;
|
||||
if (actor->aiChanged)
|
||||
aiChanged = true;
|
||||
/*if (actor->attributesChanged)
|
||||
attributesChanged = true;
|
||||
if (actor->skillsChanged)
|
||||
skillsChanged = true;
|
||||
if (actor->baseInfoChanged)
|
||||
baseInfoChanged = true;*/
|
||||
if (actor->inventory.isEquipmentChanged())
|
||||
{
|
||||
equipmentChanged = true;
|
||||
actor->inventory.resetEquipmentFlag();
|
||||
}
|
||||
if (actor->cellAPI.isChangedCell())
|
||||
{
|
||||
changedCell = true;
|
||||
actor->cellAPI.resetChangedCell();
|
||||
}
|
||||
actor->resetUpdateFlags();
|
||||
}
|
||||
|
||||
auto actorCtrl = mwmp::Networking::get().getActorPacketController();
|
||||
Cell *serverCell = nullptr;
|
||||
|
||||
if (sendToAll)
|
||||
serverCell = CellController::get().getCell(actorList.cell);
|
||||
|
||||
if (positionChanged)
|
||||
{
|
||||
auto packet = actorCtrl->GetPacket(ID_ACTOR_POSITION);
|
||||
|
||||
packet->setActorList(&actorList);
|
||||
packet->Send(actorList.guid);
|
||||
|
||||
if (sendToAll)
|
||||
serverCell->sendToLoaded(packet, &actorList);
|
||||
}
|
||||
if (statsChanged)
|
||||
{
|
||||
auto packet = actorCtrl->GetPacket(ID_ACTOR_STATS_DYNAMIC);
|
||||
|
||||
packet->setActorList(&actorList);
|
||||
packet->Send(actorList.guid);
|
||||
|
||||
if (sendToAll)
|
||||
serverCell->sendToLoaded(packet, &actorList);
|
||||
|
||||
}
|
||||
/*if (attributesChanged)
|
||||
{
|
||||
auto packet = actorCtrl->GetPacket(ID_ACTOR_POSITION);
|
||||
|
||||
}
|
||||
if (skillsChanged)
|
||||
{
|
||||
auto packet = actorCtrl->GetPacket(ID_ACTOR_POSITION);
|
||||
|
||||
}
|
||||
if (baseInfoChanged)
|
||||
{
|
||||
auto packet = actorCtrl->GetPacket(ID_ACTOR_POSITION);
|
||||
|
||||
}*/
|
||||
|
||||
if (equipmentChanged)
|
||||
{
|
||||
auto packet = actorCtrl->GetPacket(ID_ACTOR_EQUIPMENT);
|
||||
packet->setActorList(&actorList);
|
||||
packet->Send(actorList.guid);
|
||||
|
||||
if (sendToAll)
|
||||
serverCell->sendToLoaded(packet, &actorList);
|
||||
}
|
||||
if (aiChanged)
|
||||
{
|
||||
auto packet = actorCtrl->GetPacket(ID_ACTOR_AI);
|
||||
packet->setActorList(&actorList);
|
||||
packet->Send(actorList.guid);
|
||||
|
||||
if (sendToAll)
|
||||
serverCell->sendToLoaded(packet, &actorList);
|
||||
}
|
||||
if (changedCell)
|
||||
{
|
||||
auto packet = actorCtrl->GetPacket(ID_ACTOR_CELL_CHANGE);
|
||||
packet->setActorList(&actorList);
|
||||
packet->Send(actorList.guid);
|
||||
|
||||
if (sendToAll)
|
||||
serverCell->sendToLoaded(packet, &actorList);
|
||||
}
|
||||
}
|
||||
|
||||
void ActorController::sendList(std::shared_ptr<Player> player, std::vector<std::shared_ptr<Actor>> actors,
|
||||
const ESM::Cell &cell, bool sendToAll)
|
||||
{
|
||||
actorList.cell = player->cell;
|
||||
actorList.guid = player->guid;
|
||||
actorList.action = mwmp::BaseActorList::Action::Set;
|
||||
|
||||
for (auto &actor : actors)
|
||||
{
|
||||
actorList.baseActors.push_back(actor->actor);
|
||||
}
|
||||
|
||||
auto packet = mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_LIST);
|
||||
packet->setActorList(&actorList);
|
||||
packet->Send(actorList.guid);
|
||||
if (sendToAll)
|
||||
CellController::get().getCell(actorList.cell)->sendToLoaded(packet, &actorList);
|
||||
}
|
||||
|
||||
void ActorController::requestList(std::shared_ptr<Player> player, const ESM::Cell &cell)
|
||||
{
|
||||
actorList.cell = player->cell;
|
||||
actorList.guid = player->guid;
|
||||
actorList.action = mwmp::BaseActorList::Action::Request;
|
||||
|
||||
auto packet = mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_LIST);
|
||||
packet->setActorList(&actorList);
|
||||
packet->Send(actorList.guid);
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<Actor>> ActorController::getActors(std::shared_ptr<Player> player, const ESM::Cell &cell)
|
||||
{
|
||||
Cell *serverCell = CellController::get().getCell(player->cell);
|
||||
|
||||
std::vector<std::shared_ptr<Actor>> actorList;
|
||||
|
||||
for (const auto &actor : serverCell->getActorList()->baseActors)
|
||||
{
|
||||
auto a = new Actor;
|
||||
a->actor = actor;
|
||||
actorList.emplace_back(a);
|
||||
}
|
||||
|
||||
return actorList;
|
||||
}
|
59
apps/openmw-mp/Actors.hpp
Normal file
59
apps/openmw-mp/Actors.hpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
//
|
||||
// Created by koncord on 25.08.17.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <components/openmw-mp/Base/BaseActor.hpp>
|
||||
#include "NetActor.hpp"
|
||||
|
||||
class LuaState;
|
||||
class Player;
|
||||
|
||||
class Actor: public NetActor
|
||||
{
|
||||
friend class ActorController;
|
||||
public:
|
||||
static void Init(LuaState &lua);
|
||||
public:
|
||||
Actor();
|
||||
std::string getRefId() const;
|
||||
void setRefId(const std::string &refId);
|
||||
|
||||
unsigned getRefNumIndex() const;
|
||||
void setRefNumIndex(unsigned refNumIndex);
|
||||
unsigned getMpNum() const;
|
||||
void setMpNum(unsigned mpNum);
|
||||
|
||||
void followPlayer(int pid);
|
||||
void followActor(unsigned int refNumIndex, unsigned int mpNum);
|
||||
|
||||
bool doesHavePosition() const; // ????
|
||||
bool doesHaveStatsDynamic() const; // ????
|
||||
|
||||
std::shared_ptr<mwmp::BaseActor> actor;
|
||||
};
|
||||
|
||||
class ActorController
|
||||
{
|
||||
public:
|
||||
static void Init(LuaState &lua);
|
||||
public:
|
||||
|
||||
ActorController();
|
||||
~ActorController();
|
||||
|
||||
std::shared_ptr<Actor> createActor();
|
||||
void sendActors(std::shared_ptr<Player> player, std::vector<std::shared_ptr<Actor>> actors, const ESM::Cell &cell, bool sendToAll = false);
|
||||
void sendList(std::shared_ptr<Player> player, std::vector<std::shared_ptr<Actor>> actors, const ESM::Cell &cell, bool sendToAll = false);
|
||||
|
||||
void requestList(std::shared_ptr<Player> player, const ESM::Cell &cell);
|
||||
|
||||
std::vector<std::shared_ptr<Actor>> getActors(std::shared_ptr<Player> player, const ESM::Cell &cell);
|
||||
|
||||
private:
|
||||
mwmp::BaseActorList actorList;
|
||||
};
|
||||
|
||||
|
34
apps/openmw-mp/BaseMgr.cpp
Normal file
34
apps/openmw-mp/BaseMgr.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// Created by koncord on 02.01.18.
|
||||
//
|
||||
|
||||
#include "BaseMgr.hpp"
|
||||
#include "Player.hpp"
|
||||
#include "Worldstate.hpp"
|
||||
|
||||
BaseMgr::BaseMgr(Player *player) : player(player), changed(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
BaseMgr::BaseMgr(Worldstate *worldstate) : worldstate(worldstate), changed(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void BaseMgr::update()
|
||||
{
|
||||
if (!changed)
|
||||
return;
|
||||
changed = false;
|
||||
|
||||
processUpdate();
|
||||
}
|
||||
|
||||
void BaseMgr::setChanged()
|
||||
{
|
||||
changed = true;
|
||||
|
||||
if (player != nullptr)
|
||||
player->addToUpdateQueue();
|
||||
}
|
24
apps/openmw-mp/BaseMgr.hpp
Normal file
24
apps/openmw-mp/BaseMgr.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// Created by koncord on 02.01.18.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
class Player;
|
||||
class Worldstate;
|
||||
|
||||
class BaseMgr
|
||||
{
|
||||
public:
|
||||
explicit BaseMgr(Player *player);
|
||||
explicit BaseMgr(Worldstate *worldstate);
|
||||
void update();
|
||||
protected:
|
||||
bool isChanged() const { return changed; };
|
||||
void setChanged();
|
||||
virtual void processUpdate() = 0;
|
||||
Player *player;
|
||||
Worldstate *worldstate;
|
||||
private:
|
||||
bool changed;
|
||||
};
|
59
apps/openmw-mp/Books.cpp
Normal file
59
apps/openmw-mp/Books.cpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
//
|
||||
// Created by koncord on 15.08.17.
|
||||
//
|
||||
|
||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
|
||||
#include "Script/LuaState.hpp"
|
||||
#include "Networking.hpp"
|
||||
|
||||
#include "Books.hpp"
|
||||
#include "Player.hpp"
|
||||
|
||||
void Books::Init(LuaState &lua)
|
||||
{
|
||||
lua.getState()->new_usertype<Books>("Books",
|
||||
"addBook", &Books::addBook,
|
||||
"getBookId", &Books::getBookId,
|
||||
"getChanges", &Books::getChanges,
|
||||
"reset", &Books::reset
|
||||
);
|
||||
}
|
||||
|
||||
Books::Books(Player *player) : BaseMgr(player)
|
||||
{
|
||||
}
|
||||
|
||||
void Books::addBook(const std::string &bookId)
|
||||
{
|
||||
if (!isChanged())
|
||||
reset();
|
||||
player->bookChanges.books.push_back({bookId});
|
||||
setChanged();
|
||||
}
|
||||
|
||||
std::string Books::getBookId(unsigned i) const
|
||||
{
|
||||
if (i >= player->bookChanges.books.size())
|
||||
return "invalid";
|
||||
|
||||
return player->bookChanges.books.at(i).bookId;
|
||||
}
|
||||
|
||||
unsigned Books::getChanges() const
|
||||
{
|
||||
return player->bookChanges.books.size();
|
||||
}
|
||||
|
||||
void Books::reset()
|
||||
{
|
||||
player->bookChanges.books.clear();
|
||||
}
|
||||
|
||||
void Books::processUpdate()
|
||||
{
|
||||
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_BOOK);
|
||||
|
||||
packet->setPlayer(player);
|
||||
packet->Send(/*toOthers*/ false);
|
||||
}
|
26
apps/openmw-mp/Books.hpp
Normal file
26
apps/openmw-mp/Books.hpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// Created by koncord on 15.08.17.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "BaseMgr.hpp"
|
||||
|
||||
class LuaState;
|
||||
class Player;
|
||||
|
||||
class Books final: public BaseMgr
|
||||
{
|
||||
public:
|
||||
static void Init(LuaState &lua);
|
||||
public:
|
||||
explicit Books(Player *player);
|
||||
void addBook(const std::string &bookId);
|
||||
std::string getBookId(unsigned i) const;
|
||||
unsigned getChanges() const;
|
||||
void reset();
|
||||
|
||||
private:
|
||||
void processUpdate() final;
|
||||
};
|
|
@ -1,13 +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(BUILD_WITH_PAWN "Enable Pawn language" OFF)
|
||||
option(ENABLE_BREAKPAD "Enable Google Breakpad for Crash reporting" OFF)
|
||||
|
||||
if(ENABLE_BREAKPAD)
|
||||
|
@ -22,91 +14,48 @@ if(ENABLE_BREAKPAD)
|
|||
include_directories(${CMAKE_SOURCE_DIR}/extern/breakpad/src ${Breakpad_Headers})
|
||||
endif(ENABLE_BREAKPAD)
|
||||
|
||||
if(BUILD_WITH_PAWN)
|
||||
|
||||
add_subdirectory(amx)
|
||||
|
||||
#set(Pawn_ROOT ${CMAKE_SOURCE_DIR}/external/pawn/)
|
||||
set(Pawn_INCLUDES ${Pawn_ROOT}/include)
|
||||
set(Pawn_LIBRARY ${Pawn_ROOT}/lib/libamx.a)
|
||||
set(PawnScript_Sources
|
||||
Script/LangPawn/LangPAWN.cpp
|
||||
Script/LangPawn/PawnFunc.cpp)
|
||||
set(PawnScript_Headers ${Pawn_INCLUDES}
|
||||
Script/LangPawn/LangPAWN.hpp
|
||||
)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DENABLE_PAWN -DPAWN_CELL_SIZE=64")
|
||||
#include_directories(${Pawn_INCLUDES})
|
||||
include_directories("./amx/linux")
|
||||
endif(BUILD_WITH_PAWN)
|
||||
|
||||
option(BUILD_WITH_LUA "Enable Terra/Lua language" ON)
|
||||
option(FORCE_LUA "Use Lua instead Terra" OFF)
|
||||
if(BUILD_WITH_LUA)
|
||||
#set(Terra_ROOT ${CMAKE_SOURCE_DIR}/external/terra/)
|
||||
if(WIN32 OR FORCE_LUA)
|
||||
find_package(Lua51 REQUIRED)
|
||||
MESSAGE(STATUS "Found LUA_LIBRARY: ${LUA_LIBRARY}")
|
||||
MESSAGE(STATUS "Found LUA_INCLUDE_DIR: ${LUA_INCLUDE_DIR}")
|
||||
else()
|
||||
find_package(Terra REQUIRED)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DENABLE_TERRA")
|
||||
endif()
|
||||
set(LuaScript_Sources
|
||||
Script/LangLua/LangLua.cpp
|
||||
Script/LangLua/LuaFunc.cpp)
|
||||
set(LuaScript_Headers ${Terra_INCLUDES} ${LUA_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/extern/LuaBridge ${CMAKE_SOURCE_DIR}/extern/LuaBridge/detail
|
||||
Script/LangLua/LangLua.hpp)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DENABLE_LUA")
|
||||
include_directories(${Terra_INCLUDES} ${LUA_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/extern/LuaBridge)
|
||||
endif(BUILD_WITH_LUA)
|
||||
|
||||
set(NativeScript_Sources
|
||||
Script/LangNative/LangNative.cpp
|
||||
)
|
||||
set(NativeScript_Headers
|
||||
Script/LangNative/LangNative.hpp
|
||||
)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DENABLE_LUA")
|
||||
|
||||
# local files
|
||||
set(SERVER
|
||||
main.cpp
|
||||
Player.cpp
|
||||
Player.cpp Players.cpp
|
||||
Networking.cpp
|
||||
MasterClient.cpp
|
||||
Cell.cpp
|
||||
CellController.cpp
|
||||
Utils.cpp
|
||||
Script/Script.cpp Script/ScriptFunction.cpp
|
||||
Script/ScriptFunctions.cpp
|
||||
|
||||
Script/Functions/Actors.cpp Script/Functions/Objects.cpp Script/Functions/Miscellaneous.cpp
|
||||
Script/Functions/Worldstate.cpp
|
||||
|
||||
Script/Functions/Books.cpp Script/Functions/Cells.cpp Script/Functions/CharClass.cpp
|
||||
Script/Functions/Chat.cpp Script/Functions/Dialogue.cpp Script/Functions/Factions.cpp
|
||||
Script/Functions/GUI.cpp Script/Functions/Items.cpp Script/Functions/Mechanics.cpp
|
||||
Script/Functions/Positions.cpp Script/Functions/Quests.cpp Script/Functions/Settings.cpp
|
||||
Script/Functions/Shapeshift.cpp Script/Functions/Spells.cpp Script/Functions/Stats.cpp
|
||||
Script/Functions/Timer.cpp
|
||||
|
||||
Script/API/TimerAPI.cpp Script/API/PublicFnAPI.cpp
|
||||
${PawnScript_Sources}
|
||||
${LuaScript_Sources}
|
||||
${NativeScript_Sources}
|
||||
|
||||
CharClass.cpp
|
||||
Inventory.cpp
|
||||
Settings.cpp
|
||||
Timer.cpp
|
||||
Books.cpp
|
||||
GUI.cpp
|
||||
Dialogue.cpp
|
||||
Factions.cpp
|
||||
Cells.cpp
|
||||
Quests.cpp
|
||||
Spells.cpp
|
||||
Actors.cpp
|
||||
NetActor.cpp
|
||||
CellState.cpp
|
||||
Object.cpp
|
||||
stacktrace.cpp
|
||||
Window.cpp
|
||||
Weather.cpp
|
||||
Worldstate.cpp
|
||||
BaseMgr.cpp
|
||||
Script/CommandController.cpp Script/EventController.cpp Script/LuaState.cpp Script/luaUtils.cpp
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND SERVER stackwalker/StackWalker.cpp)
|
||||
endif()
|
||||
|
||||
set(SERVER_HEADER
|
||||
Script/Types.hpp Script/Script.hpp Script/SystemInterface.hpp
|
||||
Script/ScriptFunction.hpp Script/Platform.hpp Script/Language.hpp
|
||||
Script/ScriptFunctions.hpp Script/API/TimerAPI.hpp Script/API/PublicFnAPI.hpp
|
||||
${PawnScript_Headers}
|
||||
${LuaScript_Headers}
|
||||
${NativeScript_Headers}
|
||||
${CallFF_INCLUDES}
|
||||
|
||||
)
|
||||
source_group(tes3mp-server FILES ${SERVER} ${SERVER_HEADER})
|
||||
|
||||
|
@ -123,6 +72,9 @@ source_group(tes3mp-server\\processors\\actor FILES ${PROCESSORS_ACTOR})
|
|||
|
||||
set(PROCESSORS_PLAYER
|
||||
processors/player/ProcessorChatMsg.hpp processors/player/ProcessorGUIMessageBox.hpp
|
||||
|
||||
processors/player/ProcessorGUIWindow.hpp
|
||||
|
||||
processors/player/ProcessorGameWeather.hpp processors/player/ProcessorPlayerAnimFlags.hpp
|
||||
processors/player/ProcessorPlayerAnimPlay.hpp processors/player/ProcessorPlayerAttack.hpp
|
||||
processors/player/ProcessorPlayerAttribute.hpp processors/player/ProcessorPlayerBook.hpp
|
||||
|
@ -183,7 +135,16 @@ add_executable(tes3mp-server
|
|||
${PROCESSORS_ACTOR} ${PROCESSORS_PLAYER} ${PROCESSORS_OBJECT} ${PROCESSORS_WORLDSTATE} ${PROCESSORS}
|
||||
${APPLE_BUNDLE_RESOURCES}
|
||||
)
|
||||
add_definitions(-std=gnu++14 -Wno-ignored-qualifiers)
|
||||
|
||||
# For Lua debugging
|
||||
target_compile_definitions(tes3mp-server PRIVATE $<$<CONFIG:Debug>:SOL_SAFE_FUNCTIONS> $<$<CONFIG:RelWithDebInfo>:SOL_SAFE_FUNCTIONS>)
|
||||
target_compile_definitions(tes3mp-server PRIVATE $<$<CONFIG:Debug>:SOL_SAFE_USERTYPE> $<$<CONFIG:RelWithDebInfo>:SOL_SAFE_USERTYPE>)
|
||||
target_compile_definitions(tes3mp-server PRIVATE $<$<CONFIG:Debug>:SERVER_DEBUG> $<$<CONFIG:RelWithDebInfo>:SERVER_DEBUG>)
|
||||
|
||||
set_property(TARGET tes3mp-server PROPERTY CXX_STANDARD 14)
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
target_compile_options(tes3mp-server PRIVATE -Wno-ignored-qualifiers -ftemplate-depth=2048)
|
||||
endif()
|
||||
|
||||
target_link_libraries(tes3mp-server
|
||||
#${Boost_SYSTEM_LIBRARY}
|
||||
|
@ -192,11 +153,8 @@ target_link_libraries(tes3mp-server
|
|||
#${Boost_PROGRAM_OPTIONS_LIBRARY}
|
||||
${RakNet_LIBRARY}
|
||||
components
|
||||
${Terra_LIBRARY}
|
||||
${LUA_LIBRARIES}
|
||||
${Pawn_LIBRARY}
|
||||
${LuaJit_LIBRARIES}
|
||||
${Breakpad_Library}
|
||||
${CallFF_LIBRARY}
|
||||
)
|
||||
|
||||
if (UNIX)
|
||||
|
|
|
@ -2,19 +2,20 @@
|
|||
// Created by koncord on 18.02.17.
|
||||
//
|
||||
|
||||
#include "Cell.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include "Script/EventController.hpp"
|
||||
#include "Networking.hpp"
|
||||
|
||||
#include "Cell.hpp"
|
||||
#include "Player.hpp"
|
||||
#include "Script/Script.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
Cell::Cell(ESM::Cell cell) : cell(cell)
|
||||
Cell::Cell(const ESM::Cell &cell) : cell(cell)
|
||||
{
|
||||
cellActorList.count = 0;
|
||||
|
||||
}
|
||||
|
||||
Cell::Iterator Cell::begin() const
|
||||
|
@ -48,14 +49,15 @@ void Cell::addPlayer(Player *player)
|
|||
|
||||
LOG_APPEND(Log::LOG_INFO, "- Adding %s to Cell %s", player->npc.mName.c_str(), getDescription().c_str());
|
||||
|
||||
Script::Call<Script::CallbackIdentity("OnCellLoad")>(player->getId(), getDescription().c_str());
|
||||
|
||||
mwmp::Networking::get().getState().getEventCtrl().Call<CoreEvent::ON_CELL_LOAD>(player, getDescription());
|
||||
|
||||
players.push_back(player);
|
||||
}
|
||||
|
||||
void Cell::removePlayer(Player *player)
|
||||
{
|
||||
for (Iterator it = begin(); it != end(); it++)
|
||||
for (auto it = begin(); it != end(); it++)
|
||||
{
|
||||
if (*it == player)
|
||||
{
|
||||
|
@ -69,7 +71,8 @@ void Cell::removePlayer(Player *player)
|
|||
|
||||
LOG_APPEND(Log::LOG_INFO, "- Removing %s from Cell %s", player->npc.mName.c_str(), getDescription().c_str());
|
||||
|
||||
Script::Call<Script::CallbackIdentity("OnCellUnload")>(player->getId(), getDescription().c_str());
|
||||
|
||||
mwmp::Networking::get().getState().getEventCtrl().Call<CoreEvent::ON_CELL_UNLOAD>(player, getDescription());
|
||||
|
||||
players.erase(it);
|
||||
return;
|
||||
|
@ -79,77 +82,67 @@ void Cell::removePlayer(Player *player)
|
|||
|
||||
void Cell::readActorList(unsigned char packetID, const mwmp::BaseActorList *newActorList)
|
||||
{
|
||||
for (unsigned int i = 0; i < newActorList->count; i++)
|
||||
for (auto &newActor : newActorList->baseActors)
|
||||
{
|
||||
mwmp::BaseActor newActor = newActorList->baseActors.at(i);
|
||||
mwmp::BaseActor *cellActor;
|
||||
|
||||
if (containsActor(newActor.refNumIndex, newActor.mpNum))
|
||||
if (containsActor(newActor->refNumIndex, newActor->mpNum))
|
||||
{
|
||||
cellActor = getActor(newActor.refNumIndex, newActor.mpNum);
|
||||
mwmp::BaseActor *cellActor = getActor(newActor->refNumIndex, newActor->mpNum);
|
||||
|
||||
switch (packetID)
|
||||
{
|
||||
case ID_ACTOR_POSITION:
|
||||
|
||||
cellActor->hasPositionData = true;
|
||||
cellActor->position = newActor.position;
|
||||
cellActor->position = newActor->position;
|
||||
break;
|
||||
|
||||
case ID_ACTOR_STATS_DYNAMIC:
|
||||
|
||||
cellActor->hasStatsDynamicData = true;
|
||||
cellActor->creatureStats.mDynamic[0] = newActor.creatureStats.mDynamic[0];
|
||||
cellActor->creatureStats.mDynamic[1] = newActor.creatureStats.mDynamic[1];
|
||||
cellActor->creatureStats.mDynamic[2] = newActor.creatureStats.mDynamic[2];
|
||||
cellActor->creatureStats.mDynamic[0] = newActor->creatureStats.mDynamic[0];
|
||||
cellActor->creatureStats.mDynamic[1] = newActor->creatureStats.mDynamic[1];
|
||||
cellActor->creatureStats.mDynamic[2] = newActor->creatureStats.mDynamic[2];
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
cellActorList.baseActors.push_back(newActor);
|
||||
}
|
||||
|
||||
cellActorList.count = cellActorList.baseActors.size();
|
||||
}
|
||||
|
||||
bool Cell::containsActor(int refNumIndex, int mpNum)
|
||||
bool Cell::containsActor(unsigned refNumIndex, unsigned mpNum)
|
||||
{
|
||||
for (unsigned int i = 0; i < cellActorList.baseActors.size(); i++)
|
||||
for (const auto &actor : cellActorList.baseActors)
|
||||
{
|
||||
mwmp::BaseActor actor = cellActorList.baseActors.at(i);
|
||||
|
||||
if (actor.refNumIndex == refNumIndex && actor.mpNum == mpNum)
|
||||
if (actor->refNumIndex == refNumIndex && actor->mpNum == mpNum)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
mwmp::BaseActor *Cell::getActor(int refNumIndex, int mpNum)
|
||||
mwmp::BaseActor *Cell::getActor(unsigned refNumIndex, unsigned mpNum)
|
||||
{
|
||||
for (unsigned int i = 0; i < cellActorList.baseActors.size(); i++)
|
||||
for (const auto &actor : cellActorList.baseActors)
|
||||
{
|
||||
mwmp::BaseActor *actor = &cellActorList.baseActors.at(i);
|
||||
|
||||
if (actor->refNumIndex == refNumIndex && actor->mpNum == mpNum)
|
||||
return actor;
|
||||
return actor.get();
|
||||
}
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Cell::removeActors(const mwmp::BaseActorList *newActorList)
|
||||
void Cell::removeActors(const mwmp::BaseActorList &newActorList)
|
||||
{
|
||||
for (std::vector<mwmp::BaseActor>::iterator it = cellActorList.baseActors.begin(); it != cellActorList.baseActors.end();)
|
||||
for (auto it = cellActorList.baseActors.begin(); it != cellActorList.baseActors.end();)
|
||||
{
|
||||
int refNumIndex = (*it).refNumIndex;
|
||||
int mpNum = (*it).mpNum;
|
||||
unsigned refNumIndex = (*it)->refNumIndex;
|
||||
unsigned mpNum = (*it)->mpNum;
|
||||
|
||||
bool foundActor = false;
|
||||
|
||||
for (unsigned int i = 0; i < newActorList->count; i++)
|
||||
for (const auto &newActor : newActorList.baseActors)
|
||||
{
|
||||
mwmp::BaseActor newActor = newActorList->baseActors.at(i);
|
||||
|
||||
if (newActor.refNumIndex == refNumIndex && newActor.mpNum == mpNum)
|
||||
if (newActor->refNumIndex == refNumIndex && newActor->mpNum == mpNum)
|
||||
{
|
||||
it = cellActorList.baseActors.erase(it);
|
||||
foundActor = true;
|
||||
|
@ -160,8 +153,6 @@ void Cell::removeActors(const mwmp::BaseActorList *newActorList)
|
|||
if (!foundActor)
|
||||
it++;
|
||||
}
|
||||
|
||||
cellActorList.count = cellActorList.baseActors.size();
|
||||
}
|
||||
|
||||
RakNet::RakNetGUID *Cell::getAuthority()
|
||||
|
@ -191,7 +182,7 @@ void Cell::sendToLoaded(mwmp::ActorPacket *actorPacket, mwmp::BaseActorList *bas
|
|||
|
||||
std::list <Player*> plList;
|
||||
|
||||
for (auto pl : players)
|
||||
for (auto &pl : players)
|
||||
{
|
||||
if (pl != nullptr && !pl->npc.mName.empty())
|
||||
plList.push_back(pl);
|
||||
|
@ -200,7 +191,7 @@ void Cell::sendToLoaded(mwmp::ActorPacket *actorPacket, mwmp::BaseActorList *bas
|
|||
plList.sort();
|
||||
plList.unique();
|
||||
|
||||
for (auto pl : plList)
|
||||
for (auto &pl : plList)
|
||||
{
|
||||
if (pl->guid == baseActorList->guid) continue;
|
||||
|
||||
|
@ -218,7 +209,7 @@ void Cell::sendToLoaded(mwmp::ObjectPacket *objectPacket, mwmp::BaseObjectList *
|
|||
|
||||
std::list <Player*> plList;
|
||||
|
||||
for (auto pl : players)
|
||||
for (auto &pl : players)
|
||||
{
|
||||
if (pl != nullptr && !pl->npc.mName.empty())
|
||||
plList.push_back(pl);
|
||||
|
@ -227,7 +218,7 @@ void Cell::sendToLoaded(mwmp::ObjectPacket *objectPacket, mwmp::BaseObjectList *
|
|||
plList.sort();
|
||||
plList.unique();
|
||||
|
||||
for (auto pl : plList)
|
||||
for (auto &pl : plList)
|
||||
{
|
||||
if (pl->guid == baseObjectList->guid) continue;
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ class Cell
|
|||
{
|
||||
friend class CellController;
|
||||
public:
|
||||
Cell(ESM::Cell cell);
|
||||
Cell(const ESM::Cell &cell);
|
||||
typedef std::deque<Player*> TPlayers;
|
||||
typedef TPlayers::const_iterator Iterator;
|
||||
|
||||
|
@ -31,9 +31,9 @@ public:
|
|||
void removePlayer(Player *player);
|
||||
|
||||
void readActorList(unsigned char packetID, const mwmp::BaseActorList *newActorList);
|
||||
bool containsActor(int refNumIndex, int mpNum);
|
||||
mwmp::BaseActor *getActor(int refNumIndex, int mpNum);
|
||||
void removeActors(const mwmp::BaseActorList *newActorList);
|
||||
bool containsActor(unsigned refNumIndex, unsigned mpNum);
|
||||
mwmp::BaseActor *getActor(unsigned refNumIndex, unsigned mpNum);
|
||||
void removeActors(const mwmp::BaseActorList &newActorList);
|
||||
|
||||
RakNet::RakNetGUID *getAuthority();
|
||||
void setAuthority(const RakNet::RakNetGUID& guid);
|
||||
|
|
|
@ -1,50 +1,32 @@
|
|||
#include "CellController.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <apps/openmw-mp/Script/EventController.hpp>
|
||||
#include "Networking.hpp"
|
||||
|
||||
#include "CellController.hpp"
|
||||
#include "Cell.hpp"
|
||||
#include "Player.hpp"
|
||||
#include "Script/Script.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
CellController::CellController()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CellController::~CellController()
|
||||
{
|
||||
for (auto cell : cells)
|
||||
delete cell;
|
||||
}
|
||||
|
||||
CellController *CellController::sThis = nullptr;
|
||||
|
||||
void CellController::create()
|
||||
CellController &CellController::get()
|
||||
{
|
||||
assert(!sThis);
|
||||
sThis = new CellController;
|
||||
static CellController cellCtrl;
|
||||
return cellCtrl;
|
||||
}
|
||||
|
||||
void CellController::destroy()
|
||||
Cell *CellController::getCell(const ESM::Cell &esmCell)
|
||||
{
|
||||
assert(sThis);
|
||||
delete sThis;
|
||||
sThis = nullptr;
|
||||
}
|
||||
|
||||
CellController *CellController::get()
|
||||
{
|
||||
assert(sThis);
|
||||
return sThis;
|
||||
}
|
||||
|
||||
Cell *CellController::getCell(ESM::Cell *esmCell)
|
||||
{
|
||||
if (esmCell->isExterior())
|
||||
return getCellByXY(esmCell->mData.mX, esmCell->mData.mY);
|
||||
if (esmCell.isExterior())
|
||||
return getCellByXY(esmCell.mData.mX, esmCell.mData.mY);
|
||||
else
|
||||
return getCellByName(esmCell->mName);
|
||||
return getCellByName(esmCell.mName);
|
||||
}
|
||||
|
||||
|
||||
|
@ -64,7 +46,7 @@ Cell *CellController::getCellByXY(int x, int y)
|
|||
return *it;
|
||||
}
|
||||
|
||||
Cell *CellController::getCellByName(std::string cellName)
|
||||
Cell *CellController::getCellByName(const std::string &cellName)
|
||||
{
|
||||
auto it = find_if(cells.begin(), cells.end(), [cellName](const Cell *c)
|
||||
{
|
||||
|
@ -80,7 +62,7 @@ Cell *CellController::getCellByName(std::string cellName)
|
|||
return *it;
|
||||
}
|
||||
|
||||
Cell *CellController::addCell(ESM::Cell cellData)
|
||||
Cell *CellController::addCell(const ESM::Cell &cellData)
|
||||
{
|
||||
LOG_APPEND(Log::LOG_INFO, "- Loaded cells: %d", cells.size());
|
||||
auto it = find_if(cells.begin(), cells.end(), [cellData](const Cell *c) {
|
||||
|
@ -123,7 +105,8 @@ void CellController::removeCell(Cell *cell)
|
|||
{
|
||||
if (*it != nullptr && *it == cell)
|
||||
{
|
||||
Script::Call<Script::CallbackIdentity("OnCellDeletion")>(cell->getDescription().c_str());
|
||||
mwmp::Networking::get().getState().getEventCtrl().Call<CoreEvent::ON_CELL_DELETION>(cell->getDescription());
|
||||
|
||||
LOG_APPEND(Log::LOG_INFO, "- Removing %s from CellController", cell->getDescription().c_str());
|
||||
|
||||
delete *it;
|
||||
|
@ -149,7 +132,7 @@ 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)
|
||||
for (auto it = player->getCells()->begin(); !player->getCells()->empty(); ++it)
|
||||
removePlayer(*it, player);
|
||||
}
|
||||
|
||||
|
@ -157,7 +140,7 @@ void CellController::update(Player *player)
|
|||
{
|
||||
for (auto cell : player->cellStateChanges.cellStates)
|
||||
{
|
||||
if (cell.type == mwmp::CellState::LOAD)
|
||||
if (cell.type == mwmp::CellState::Type::Load)
|
||||
{
|
||||
Cell *c = addCell(cell.cell);
|
||||
c->addPlayer(player);
|
||||
|
|
|
@ -15,32 +15,32 @@ class Cell;
|
|||
class CellController
|
||||
{
|
||||
private:
|
||||
CellController();
|
||||
CellController() = default;
|
||||
~CellController();
|
||||
|
||||
CellController(CellController&); // not used
|
||||
public:
|
||||
static void create();
|
||||
static void destroy();
|
||||
static CellController *get();
|
||||
CellController(const CellController&) = delete;
|
||||
CellController(CellController&&) = delete;
|
||||
CellController& operator=(const CellController&) = delete;
|
||||
CellController& operator=(CellController&&) = delete;
|
||||
|
||||
static CellController &get();
|
||||
public:
|
||||
typedef std::deque<Cell*> TContainer;
|
||||
typedef TContainer::iterator TIter;
|
||||
|
||||
Cell * addCell(ESM::Cell cell);
|
||||
Cell *addCell(const ESM::Cell &cellData);
|
||||
void removeCell(Cell *);
|
||||
|
||||
void removePlayer(Cell *cell, Player *player);
|
||||
void deletePlayer(Player *player);
|
||||
|
||||
Cell *getCell(ESM::Cell *esmCell);
|
||||
Cell *getCell(const ESM::Cell &esmCell);
|
||||
Cell *getCellByXY(int x, int y);
|
||||
Cell *getCellByName(std::string cellName);
|
||||
Cell *getCellByName(const std::string &cellName);
|
||||
|
||||
void update(Player *player);
|
||||
|
||||
private:
|
||||
static CellController *sThis;
|
||||
TContainer cells;
|
||||
};
|
||||
|
||||
|
|
32
apps/openmw-mp/CellState.cpp
Normal file
32
apps/openmw-mp/CellState.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// Created by koncord on 25.08.17.
|
||||
//
|
||||
|
||||
#include "Script/LuaState.hpp"
|
||||
|
||||
#include "CellState.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
void CellState::Init(LuaState &lua)
|
||||
{
|
||||
lua.getState()->new_usertype<CellState>("CellState",
|
||||
"type", sol::property(&CellState::getStateType),
|
||||
"description", sol::property(&CellState::getDescription)
|
||||
);
|
||||
}
|
||||
|
||||
CellState::CellState(mwmp::CellState state) : state(std::move(state))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
mwmp::CellState::Type CellState::getStateType() const
|
||||
{
|
||||
return state.type;
|
||||
}
|
||||
|
||||
std::string CellState::getDescription() const
|
||||
{
|
||||
return state.cell.getDescription();
|
||||
}
|
24
apps/openmw-mp/CellState.hpp
Normal file
24
apps/openmw-mp/CellState.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// Created by koncord on 25.08.17.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <components/openmw-mp/Base/BasePlayer.hpp>
|
||||
|
||||
class LuaState;
|
||||
|
||||
class CellState
|
||||
{
|
||||
public:
|
||||
static void Init(LuaState &lua);
|
||||
|
||||
explicit CellState(mwmp::CellState state);
|
||||
public:
|
||||
mwmp::CellState::Type getStateType() const;
|
||||
std::string getDescription() const;
|
||||
|
||||
private:
|
||||
mwmp::CellState state;
|
||||
};
|
108
apps/openmw-mp/Cells.cpp
Normal file
108
apps/openmw-mp/Cells.cpp
Normal file
|
@ -0,0 +1,108 @@
|
|||
//
|
||||
// Created by koncord on 25.08.17.
|
||||
//
|
||||
|
||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
|
||||
#include "Script/LuaState.hpp"
|
||||
#include "Player.hpp"
|
||||
#include "Networking.hpp"
|
||||
|
||||
#include "Cells.hpp"
|
||||
#include "NetActor.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
void Cells::Init(LuaState &lua)
|
||||
{
|
||||
lua.getState()->new_usertype<Cells>("Cell",
|
||||
"description", sol::property(&Cells::getDescription, &Cells::setDescription),
|
||||
"getExterior", &Cells::getExterior,
|
||||
"setExterior", &Cells::setExterior,
|
||||
"getRegion", &Cells::getRegion,
|
||||
|
||||
"isExterior", &Cells::isExterior,
|
||||
"isChangingRegion", &Cells::isChangingRegion
|
||||
);
|
||||
}
|
||||
|
||||
Cells::Cells(NetActor *netActor) : netActor(netActor), changedCell(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Cells::~Cells()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Cells::update()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::string Cells::getDescription() const
|
||||
{
|
||||
return netActor->getNetCreature()->cell.getDescription();
|
||||
}
|
||||
|
||||
inline void Cells::setChanged()
|
||||
{
|
||||
if (!changedCell && netActor->isPlayer())
|
||||
netActor->toPlayer()->addToUpdateQueue();
|
||||
changedCell = true;
|
||||
}
|
||||
|
||||
void Cells::setDescription(const std::string &cellDescription)
|
||||
{
|
||||
/*LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Script is moving %s from %s to %s", netActor->getNetCreature()->npc.mName.c_str(),
|
||||
netActor->getNetCreature()->cell.getDescription().c_str(), cellDescription.c_str());*/
|
||||
|
||||
netActor->getNetCreature()->cell = Utils::getCellFromDescription(cellDescription);
|
||||
setChanged();
|
||||
}
|
||||
|
||||
std::tuple<int, int> Cells::getExterior() const
|
||||
{
|
||||
return make_tuple(netActor->getNetCreature()->cell.mData.mX, netActor->getNetCreature()->cell.mData.mY);
|
||||
}
|
||||
|
||||
void Cells::setExterior(int x, int y)
|
||||
{
|
||||
/*LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Script is moving %s from %s to %i,%i", netActor->getNetCreature()->npc.mName.c_str(),
|
||||
netActor->getNetCreature()->cell.getDescription().c_str(), x, y);*/
|
||||
|
||||
// If the player is currently in an interior, turn off the interior flag from the cell
|
||||
if (!netActor->getNetCreature()->cell.isExterior())
|
||||
netActor->getNetCreature()->cell.mData.mFlags &= ~ESM::Cell::Interior;
|
||||
|
||||
netActor->getNetCreature()->cell.mData.mX = x;
|
||||
netActor->getNetCreature()->cell.mData.mY = y;
|
||||
setChanged();
|
||||
}
|
||||
|
||||
bool Cells::isExterior() const
|
||||
{
|
||||
return netActor->getNetCreature()->cell.isExterior();
|
||||
}
|
||||
|
||||
bool Cells::isChangingRegion() const
|
||||
{
|
||||
return netActor->getNetCreature()->isChangingRegion;
|
||||
}
|
||||
|
||||
std::string Cells::getRegion() const
|
||||
{
|
||||
return netActor->getNetCreature()->cell.mRegion;
|
||||
}
|
||||
|
||||
bool Cells::isChangedCell() const
|
||||
{
|
||||
return changedCell;
|
||||
}
|
||||
|
||||
void Cells::resetChangedCell()
|
||||
{
|
||||
changedCell = false;
|
||||
}
|
||||
|
45
apps/openmw-mp/Cells.hpp
Normal file
45
apps/openmw-mp/Cells.hpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// Created by koncord on 25.08.17.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <components/openmw-mp/Base/BasePlayer.hpp>
|
||||
|
||||
class LuaState;
|
||||
class NetActor;
|
||||
|
||||
class Cells
|
||||
{
|
||||
public:
|
||||
static void Init(LuaState &lua);
|
||||
public:
|
||||
explicit Cells(NetActor *netActor);
|
||||
~Cells();
|
||||
|
||||
void update();
|
||||
|
||||
std::string getDescription() const;
|
||||
void setDescription(const std::string &cellDescription);
|
||||
|
||||
std::tuple<int, int> getExterior() const;
|
||||
void setExterior(int x, int y);
|
||||
|
||||
bool isExterior() const;
|
||||
bool isChangingRegion() const;
|
||||
|
||||
std::string getRegion() const;
|
||||
|
||||
bool isChangedCell() const;
|
||||
void resetChangedCell();
|
||||
|
||||
|
||||
private:
|
||||
NetActor *netActor;
|
||||
bool changedCell;
|
||||
void setChanged();
|
||||
};
|
||||
|
||||
|
147
apps/openmw-mp/CharClass.cpp
Normal file
147
apps/openmw-mp/CharClass.cpp
Normal file
|
@ -0,0 +1,147 @@
|
|||
//
|
||||
// Created by koncord on 12.08.17.
|
||||
//
|
||||
|
||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
|
||||
#include "Script/LuaState.hpp"
|
||||
#include "Networking.hpp"
|
||||
|
||||
#include "CharClass.hpp"
|
||||
#include "Player.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
void CharClass::Init(LuaState &lua)
|
||||
{
|
||||
lua.getState()->new_usertype<CharClass>("Class",
|
||||
//"__gc", sol::destructor(deleter),
|
||||
"default", sol::property(&CharClass::getDefault, &CharClass::setDefault),
|
||||
"isCustom", &CharClass::isCustom,
|
||||
|
||||
"name", sol::property(&CharClass::getName, &CharClass::setName),
|
||||
"description", sol::property(&CharClass::getDescription, &CharClass::setDescription),
|
||||
"specialization",
|
||||
sol::property(&CharClass::getSpecialization, &CharClass::setSpecialization),
|
||||
|
||||
"getMajorAttributes", &CharClass::getMajorAttributes,
|
||||
"setMajorAttributes", &CharClass::setMajorAttributes,
|
||||
|
||||
"getMinorSkills", &CharClass::getMinorSkills,
|
||||
"setMinorSkills", &CharClass::setMinorSkills,
|
||||
|
||||
"getMajorSkills", &CharClass::getMajorSkills,
|
||||
"setMajorSkills", &CharClass::setMajorSkills
|
||||
);
|
||||
}
|
||||
|
||||
CharClass::CharClass(Player *player) : BaseMgr(player)
|
||||
{
|
||||
}
|
||||
|
||||
string CharClass::getDefault() const
|
||||
{
|
||||
return player->charClass.mId;
|
||||
}
|
||||
|
||||
void CharClass::setDefault(const string &className)
|
||||
{
|
||||
player->charClass.mId = className;
|
||||
setChanged();
|
||||
printf("CharClass::setDefault()\n");
|
||||
}
|
||||
|
||||
bool CharClass::isCustom() const
|
||||
{
|
||||
return player->charClass.mId.empty();
|
||||
}
|
||||
|
||||
void CharClass::setName(const string &className)
|
||||
{
|
||||
player->charClass.mName = className;
|
||||
setChanged();
|
||||
}
|
||||
|
||||
string CharClass::getName() const
|
||||
{
|
||||
return player->charClass.mName;
|
||||
}
|
||||
|
||||
std::string CharClass::getDescription() const
|
||||
{
|
||||
return player->charClass.mDescription;
|
||||
}
|
||||
|
||||
void CharClass::setDescription(const string &desc)
|
||||
{
|
||||
player->charClass.mDescription = desc;
|
||||
setChanged();
|
||||
}
|
||||
|
||||
std::tuple<int, int> CharClass::getMajorAttributes() const
|
||||
{
|
||||
const auto &data = player->charClass.mData;
|
||||
return make_tuple(data.mAttribute[0], data.mAttribute[1]);
|
||||
}
|
||||
|
||||
void CharClass::setMajorAttributes(int first, int second)
|
||||
{
|
||||
auto &data = player->charClass.mData;
|
||||
data.mAttribute[0] = first;
|
||||
data.mAttribute[1] = second;
|
||||
setChanged();
|
||||
}
|
||||
|
||||
int CharClass::getSpecialization() const
|
||||
{
|
||||
return player->charClass.mData.mSpecialization;
|
||||
}
|
||||
|
||||
void CharClass::setSpecialization(int spec)
|
||||
{
|
||||
auto &data = player->charClass.mData;
|
||||
data.mSpecialization = spec;
|
||||
setChanged();
|
||||
}
|
||||
|
||||
std::tuple<int, int, int, int, int> CharClass::getMinorSkills() const
|
||||
{
|
||||
const auto &data = player->charClass.mData;
|
||||
return make_tuple( data.mSkills[0][0], data.mSkills[1][0], data.mSkills[2][0], data.mSkills[3][0], data.mSkills[4][0]);
|
||||
}
|
||||
|
||||
void CharClass::setMinorSkills(int first, int second, int third, int fourth, int fifth)
|
||||
{
|
||||
auto &data = player->charClass.mData;
|
||||
data.mSkills[0][0] = first;
|
||||
data.mSkills[1][0] = second;
|
||||
data.mSkills[2][0] = third;
|
||||
data.mSkills[3][0] = fourth;
|
||||
data.mSkills[4][0] = fifth;
|
||||
setChanged();
|
||||
}
|
||||
|
||||
std::tuple<int, int, int, int, int> CharClass::getMajorSkills() const
|
||||
{
|
||||
const auto &data = player->charClass.mData;
|
||||
return make_tuple( data.mSkills[0][1], data.mSkills[1][1], data.mSkills[2][1], data.mSkills[3][1], data.mSkills[4][1]);
|
||||
}
|
||||
|
||||
void CharClass::setMajorSkills(int first, int second, int third, int fourth, int fifth)
|
||||
{
|
||||
auto &data = player->charClass.mData;
|
||||
data.mSkills[0][1] = first;
|
||||
data.mSkills[1][1] = second;
|
||||
data.mSkills[2][1] = third;
|
||||
data.mSkills[3][1] = fourth;
|
||||
data.mSkills[4][1] = fifth;
|
||||
setChanged();
|
||||
}
|
||||
|
||||
void CharClass::processUpdate()
|
||||
{
|
||||
printf("CharClass::update()\n");
|
||||
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_CHARCLASS);
|
||||
packet->setPlayer(player);
|
||||
packet->Send(false);
|
||||
}
|
48
apps/openmw-mp/CharClass.hpp
Normal file
48
apps/openmw-mp/CharClass.hpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// Created by koncord on 12.08.17.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include "BaseMgr.hpp"
|
||||
|
||||
class LuaState;
|
||||
class Player;
|
||||
|
||||
class CharClass final: public BaseMgr
|
||||
{
|
||||
public:
|
||||
static void Init(LuaState &lua);
|
||||
|
||||
public:
|
||||
explicit CharClass(Player *player);
|
||||
|
||||
std::string getDefault() const;
|
||||
void setDefault(const std::string &className);
|
||||
bool isCustom() const;
|
||||
|
||||
std::string getName() const;
|
||||
void setName(const std::string &className);
|
||||
|
||||
std::string getDescription() const;
|
||||
void setDescription(const std::string &desc);
|
||||
|
||||
std::tuple<int, int> getMajorAttributes() const;
|
||||
void setMajorAttributes(int first, int second);
|
||||
|
||||
int getSpecialization() const;
|
||||
void setSpecialization(int spec);
|
||||
|
||||
std::tuple<int, int, int, int, int> getMinorSkills() const;
|
||||
void setMinorSkills(int fisrt, int second, int third, int fourth, int fifth);
|
||||
|
||||
std::tuple<int, int, int, int, int> getMajorSkills() const;
|
||||
void setMajorSkills(int fisrt, int second, int third, int fourth, int fifth);
|
||||
|
||||
private:
|
||||
void processUpdate() final;
|
||||
};
|
||||
|
||||
|
82
apps/openmw-mp/Dialogue.cpp
Normal file
82
apps/openmw-mp/Dialogue.cpp
Normal file
|
@ -0,0 +1,82 @@
|
|||
//
|
||||
// Created by koncord on 15.08.17.
|
||||
//
|
||||
|
||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
|
||||
#include "Script/LuaState.hpp"
|
||||
#include "Networking.hpp"
|
||||
|
||||
#include "Dialogue.hpp"
|
||||
#include "Player.hpp"
|
||||
|
||||
void Dialogue::Init(LuaState &lua)
|
||||
{
|
||||
lua.getState()->new_usertype<Dialogue>("Dialogue",
|
||||
"addTopic", &Dialogue::addTopic,
|
||||
"getTopicId", &Dialogue::getTopicId,
|
||||
"size", &Dialogue::size,
|
||||
"reset", &Dialogue::reset);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Dialogue::Dialogue(Player *player) : BaseMgr(player)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Dialogue::reset()
|
||||
{
|
||||
player->topicChanges.topics.clear();
|
||||
}
|
||||
|
||||
void Dialogue::processUpdate()
|
||||
{
|
||||
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_TOPIC);
|
||||
|
||||
packet->setPlayer(player);
|
||||
packet->Send(/*toOthers*/ false);
|
||||
}
|
||||
|
||||
void Dialogue::addTopic(const std::string &topicId)
|
||||
{
|
||||
if (!isChanged())
|
||||
reset();
|
||||
setChanged();
|
||||
player->topicChanges.topics.push_back({topicId});
|
||||
}
|
||||
|
||||
std::string Dialogue::getTopicId(unsigned int i) const
|
||||
{
|
||||
return player->topicChanges.topics.at(i).topicId;
|
||||
}
|
||||
|
||||
size_t Dialogue::size() const
|
||||
{
|
||||
return player->topicChanges.topics.size();
|
||||
}
|
||||
|
||||
void Dialogue::playAnimation(const std::string &groupname, int mode, int count, bool persist)
|
||||
{
|
||||
player->animation.groupname = groupname;
|
||||
player->animation.mode = mode;
|
||||
player->animation.count = count;
|
||||
player->animation.persist = persist;
|
||||
|
||||
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_ANIM_PLAY);
|
||||
packet->setPlayer(player);
|
||||
packet->Send(false);
|
||||
player->sendToLoaded(*packet);
|
||||
}
|
||||
|
||||
void Dialogue::playSpeech(const std::string &sound)
|
||||
{
|
||||
player->sound = sound;
|
||||
|
||||
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_SPEECH);
|
||||
packet->setPlayer(player);
|
||||
packet->Send(false);
|
||||
player->sendToLoaded(*packet);
|
||||
}
|
32
apps/openmw-mp/Dialogue.hpp
Normal file
32
apps/openmw-mp/Dialogue.hpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// Created by koncord on 15.08.17.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "BaseMgr.hpp"
|
||||
|
||||
class LuaState;
|
||||
class Player;
|
||||
|
||||
class Dialogue final: public BaseMgr
|
||||
{
|
||||
public:
|
||||
static void Init(LuaState &lua);
|
||||
public:
|
||||
explicit Dialogue(Player *player);
|
||||
|
||||
void addTopic(const std::string &topicId);
|
||||
std::string getTopicId(unsigned int i) const;
|
||||
size_t size() const;
|
||||
|
||||
void playAnimation(const std::string &groupname, int mode, int count, bool persist);
|
||||
void playSpeech(const std::string &sound);
|
||||
|
||||
void reset();
|
||||
private:
|
||||
void processUpdate() final;
|
||||
};
|
||||
|
||||
|
127
apps/openmw-mp/Factions.cpp
Normal file
127
apps/openmw-mp/Factions.cpp
Normal file
|
@ -0,0 +1,127 @@
|
|||
//
|
||||
// Created by koncord on 17.08.17.
|
||||
//
|
||||
|
||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
|
||||
#include "Script/LuaState.hpp"
|
||||
#include "Networking.hpp"
|
||||
|
||||
#include "Factions.hpp"
|
||||
#include "Player.hpp"
|
||||
|
||||
void Factions::Init(LuaState &lua)
|
||||
{
|
||||
lua.getState()->new_usertype<Factions>("Factions",
|
||||
"addFaction", &Factions::addFaction,
|
||||
"getFaction", &Factions::getFaction,
|
||||
"setFaction", &Factions::setFaction,
|
||||
"clear", &Factions::clear,
|
||||
"size", &Factions::size
|
||||
);
|
||||
}
|
||||
|
||||
Factions::Factions(Player *player): BaseMgr(player)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Factions::~Factions()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Factions::processUpdate()
|
||||
{
|
||||
auto packet =mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_FACTION);
|
||||
packet->setPlayer(player);
|
||||
packet->Send(/*toOthers*/ false);
|
||||
clear();
|
||||
}
|
||||
|
||||
void Factions::addFaction(const Faction &faction)
|
||||
{
|
||||
player->factionChanges.factions.emplace_back(faction.faction);
|
||||
setChanged();
|
||||
}
|
||||
|
||||
|
||||
Faction Factions::getFaction(int id) const
|
||||
{
|
||||
return Faction(player->factionChanges.factions.at(id));
|
||||
}
|
||||
|
||||
void Factions::setFaction(int id, const Faction &faction)
|
||||
{
|
||||
player->factionChanges.factions.at(id) = faction.faction;
|
||||
setChanged();
|
||||
}
|
||||
|
||||
void Factions::clear()
|
||||
{
|
||||
player->factionChanges.factions.clear();
|
||||
setChanged();
|
||||
}
|
||||
|
||||
size_t Factions::size() const
|
||||
{
|
||||
return player->factionChanges.factions.size();
|
||||
}
|
||||
|
||||
void Faction::Init(LuaState &lua)
|
||||
{
|
||||
lua.getState()->new_usertype<Faction>("Faction",
|
||||
"factionId", sol::property(&Faction::getFactionId, &Faction::setFactionId),
|
||||
"rank", sol::property(&Faction::getFactionRank, &Faction::setFactionRank),
|
||||
"isExpelled", sol::property(&Faction::getFactionExpulsionState, &Faction::setFactionExpulsionState),
|
||||
"reputation", sol::property(&Faction::getFactionReputation, &Faction::setFactionReputation)
|
||||
);
|
||||
}
|
||||
|
||||
Faction::Faction(mwmp::Faction &faction): faction(faction)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::string Faction::getFactionId() const
|
||||
{
|
||||
return faction.factionId;
|
||||
}
|
||||
|
||||
void Faction::setFactionId(const std::string &factionId)
|
||||
{
|
||||
faction.factionId = factionId;
|
||||
}
|
||||
|
||||
int Faction::getFactionRank() const
|
||||
{
|
||||
return faction.rank;
|
||||
}
|
||||
|
||||
void Faction::setFactionRank(unsigned int rank)
|
||||
{
|
||||
faction.rankChanged();
|
||||
faction.rank = rank;
|
||||
}
|
||||
|
||||
bool Faction::getFactionExpulsionState() const
|
||||
{
|
||||
return faction.isExpelled;
|
||||
}
|
||||
|
||||
void Faction::setFactionExpulsionState(bool expulsionState)
|
||||
{
|
||||
faction.expulsionChanged();
|
||||
faction.isExpelled = expulsionState;
|
||||
}
|
||||
|
||||
int Faction::getFactionReputation() const
|
||||
{
|
||||
return faction.reputation;
|
||||
}
|
||||
|
||||
void Faction::setFactionReputation(int reputation)
|
||||
{
|
||||
faction.reputationChanged();
|
||||
faction.reputation = reputation;
|
||||
}
|
53
apps/openmw-mp/Factions.hpp
Normal file
53
apps/openmw-mp/Factions.hpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// Created by koncord on 17.08.17.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <components/openmw-mp/Base/BasePlayer.hpp>
|
||||
#include "BaseMgr.hpp"
|
||||
|
||||
class LuaState;
|
||||
class Player;
|
||||
|
||||
class Faction
|
||||
{
|
||||
friend class Factions;
|
||||
public:
|
||||
static void Init(LuaState &lua);
|
||||
public:
|
||||
explicit Faction(mwmp::Faction &faction);
|
||||
|
||||
std::string getFactionId() const;
|
||||
void setFactionId(const std::string &factionId);
|
||||
|
||||
int getFactionRank() const;
|
||||
void setFactionRank(unsigned int rank);
|
||||
|
||||
bool getFactionExpulsionState() const;
|
||||
void setFactionExpulsionState(bool expulsionState);
|
||||
|
||||
int getFactionReputation() const;
|
||||
void setFactionReputation(int reputation);
|
||||
|
||||
mwmp::Faction faction;
|
||||
};
|
||||
|
||||
class Factions final: public BaseMgr
|
||||
{
|
||||
public:
|
||||
static void Init(LuaState &lua);
|
||||
public:
|
||||
explicit Factions(Player *player);
|
||||
~Factions();
|
||||
|
||||
void addFaction(const Faction &faction);
|
||||
Faction getFaction(int id) const;
|
||||
void setFaction(int id, const Faction &faction);
|
||||
size_t size() const;
|
||||
void clear();
|
||||
|
||||
private:
|
||||
void processUpdate() final;
|
||||
};
|
279
apps/openmw-mp/GUI.cpp
Normal file
279
apps/openmw-mp/GUI.cpp
Normal file
|
@ -0,0 +1,279 @@
|
|||
//
|
||||
// Created by koncord on 15.08.17.
|
||||
//
|
||||
|
||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
#include "GUI.hpp"
|
||||
#include "Player.hpp"
|
||||
#include "Networking.hpp"
|
||||
#include <RakPeerInterface.h>
|
||||
|
||||
void GUI::Init(LuaState &lua)
|
||||
{
|
||||
lua.getState()->new_usertype<GUI>("GUI",
|
||||
"messageBox", &GUI::messageBox,
|
||||
"customMessageBox", &GUI::customMessageBox,
|
||||
"inputDialog", &GUI::inputDialog,
|
||||
"passwordDialog", &GUI::passwordDialog,
|
||||
"listBox", &GUI::listBox,
|
||||
"setMapVisibility", &GUI::setMapVisibility,
|
||||
"setMapVisibilityAll", &GUI::setMapVisibilityAll,
|
||||
"createWindow", &GUI::createWindow,
|
||||
"deleteWindow", &GUI::deleteWindow
|
||||
);
|
||||
Window::Init(lua);
|
||||
}
|
||||
|
||||
GUI::GUI(Player *player): BaseMgr(player)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void GUI::processUpdate()
|
||||
{
|
||||
player->guiMessageBox = guiQueue.front().first;
|
||||
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_GUI_MESSAGEBOX);
|
||||
packet->setPlayer(player);
|
||||
packet->Send(false);
|
||||
}
|
||||
|
||||
uint64_t GUI::generateGuiId()
|
||||
{
|
||||
return RakNet::RakPeerInterface::Get64BitUniqueRandomNumber();
|
||||
}
|
||||
|
||||
void GUI::messageBox(sol::function fn, const char *label, sol::this_environment te)
|
||||
{
|
||||
mwmp::BasePlayer::GUIMessageBox mbox;
|
||||
|
||||
mbox.id = generateGuiId();
|
||||
mbox.label = label;
|
||||
mbox.type = Player::GUIMessageBox::Type::MessageBox;
|
||||
|
||||
guiQueue.emplace(std::move(mbox), std::move(fn));
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
void GUI::customMessageBox(sol::function fn, const char *label, const char *buttons, sol::this_environment te)
|
||||
{
|
||||
mwmp::BasePlayer::GUIMessageBox mbox;
|
||||
mbox.id = generateGuiId();
|
||||
mbox.label = label;
|
||||
mbox.buttons = buttons;
|
||||
mbox.type = Player::GUIMessageBox::Type::CustomMessageBox;
|
||||
|
||||
guiQueue.emplace(std::move(mbox), std::move(fn));
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
void GUI::inputDialog(sol::function fn, const char *label, const char *note, sol::this_environment te)
|
||||
{
|
||||
mwmp::BasePlayer::GUIMessageBox mbox;
|
||||
|
||||
mbox.id = generateGuiId();
|
||||
mbox.label = label;
|
||||
mbox.note = note;
|
||||
mbox.type = Player::GUIMessageBox::Type::InputDialog;
|
||||
|
||||
guiQueue.emplace(std::move(mbox), std::move(fn));
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
void GUI::passwordDialog(sol::function fn, const char *label, const char *note, sol::this_environment te)
|
||||
{
|
||||
mwmp::BasePlayer::GUIMessageBox mbox;
|
||||
|
||||
mbox.id = generateGuiId();
|
||||
mbox.label = label;
|
||||
mbox.note = note;
|
||||
mbox.type = Player::GUIMessageBox::Type::PasswordDialog;
|
||||
|
||||
guiQueue.emplace(std::move(mbox), std::move(fn));
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
void GUI::listBox(sol::function fn, const char *label, const char *items, sol::this_environment te)
|
||||
{
|
||||
mwmp::BasePlayer::GUIMessageBox mbox;
|
||||
|
||||
mbox.id = generateGuiId();
|
||||
|
||||
mbox.label = label;
|
||||
mbox.data = items;
|
||||
mbox.type = Player::GUIMessageBox::Type::ListBox;
|
||||
|
||||
guiQueue.emplace(std::move(mbox), std::move(fn));
|
||||
|
||||
setChanged();
|
||||
}
|
||||
|
||||
void GUI::onGUIAction()
|
||||
{
|
||||
auto mbox = std::move(guiQueue.front().first);
|
||||
auto callback = std::move(guiQueue.front().second);
|
||||
guiQueue.pop();
|
||||
|
||||
if (!guiQueue.empty())
|
||||
setChanged();
|
||||
|
||||
if (player->guiMessageBox.id != mbox.id)
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Wrong MessageBox id from %s(%d).", player->npc.mName, player->getId());
|
||||
return;
|
||||
}
|
||||
|
||||
if (callback.valid())
|
||||
callback.call(player, player->guiMessageBox.data);
|
||||
}
|
||||
|
||||
void GUI::setMapVisibility(unsigned short targetPID, unsigned short affectedPID, unsigned short state)
|
||||
{
|
||||
LOG_MESSAGE(Log::LOG_WARN, "stub");
|
||||
}
|
||||
|
||||
void GUI::setMapVisibilityAll(unsigned short targetPID, unsigned short state)
|
||||
{
|
||||
LOG_MESSAGE(Log::LOG_WARN, "stub");
|
||||
}
|
||||
|
||||
std::shared_ptr<Window> GUI::createWindow(short x, short y, sol::function fn, sol::this_environment te)
|
||||
{
|
||||
int id = 0;
|
||||
|
||||
for (auto &window : windows)
|
||||
{
|
||||
if (window.second == nullptr)
|
||||
{
|
||||
id = window.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (id == 0)
|
||||
id = lastWindowId++;
|
||||
|
||||
auto window = std::make_shared<Window>(player, id);
|
||||
window->setSize(x, y);
|
||||
window->setCallback(fn);
|
||||
|
||||
windows[id] = window;
|
||||
return window;
|
||||
}
|
||||
|
||||
void GUI::deleteWindow(std::shared_ptr<Window> window)
|
||||
{
|
||||
auto it = windows.find(window->getID());
|
||||
if (it != windows.end())
|
||||
{
|
||||
it->second = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void GUI::onGUIWindowAction()
|
||||
{
|
||||
auto it = windows.find(player->guiWindow.id);
|
||||
if (it != windows.end() && it->second != nullptr)
|
||||
{
|
||||
it->second->call(player->guiWindow);
|
||||
}
|
||||
}
|
||||
|
||||
void QuickKeys::Init(LuaState &lua)
|
||||
{
|
||||
lua.getState()->new_usertype<QuickKeys>("QuickKeys",
|
||||
"addQuickKey", &QuickKeys::addQuickKey,
|
||||
"getQuickKey", &QuickKeys::getQuickKey,
|
||||
"setQuickKey", &QuickKeys::setQuickKey,
|
||||
"clear", &QuickKeys::clear,
|
||||
"size", &QuickKeys::size
|
||||
);
|
||||
}
|
||||
|
||||
QuickKeys::QuickKeys(Player *player) : BaseMgr(player)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void QuickKeys::processUpdate()
|
||||
{
|
||||
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_QUICKKEYS);
|
||||
packet->setPlayer(player);
|
||||
packet->Send(false);
|
||||
clear();
|
||||
}
|
||||
|
||||
void QuickKeys::addQuickKey(const QuickKey &quickKey)
|
||||
{
|
||||
player->quickKeyChanges.quickKeys.push_back(quickKey.quickKey);
|
||||
setChanged();
|
||||
}
|
||||
|
||||
QuickKey QuickKeys::getQuickKey(int id) const
|
||||
{
|
||||
return QuickKey(player->quickKeyChanges.quickKeys.at(id));
|
||||
}
|
||||
|
||||
void QuickKeys::setQuickKey(int id, const QuickKey &quickKey)
|
||||
{
|
||||
player->quickKeyChanges.quickKeys.at(id) = quickKey.quickKey;
|
||||
setChanged();
|
||||
}
|
||||
|
||||
void QuickKeys::clear()
|
||||
{
|
||||
player->quickKeyChanges.quickKeys.clear();
|
||||
setChanged();
|
||||
}
|
||||
|
||||
size_t QuickKeys::size() const
|
||||
{
|
||||
return player->quickKeyChanges.quickKeys.size();
|
||||
}
|
||||
|
||||
void QuickKey::Init(LuaState &lua)
|
||||
{
|
||||
lua.getState()->new_usertype<QuickKey>("QuickKey",
|
||||
"slot", sol::property(&QuickKey::getSlot, &QuickKey::setSlot),
|
||||
"type", sol::property(&QuickKey::getType, &QuickKey::setType),
|
||||
"itemId", sol::property(&QuickKey::getItemId, &QuickKey::setItemId)
|
||||
);
|
||||
}
|
||||
|
||||
QuickKey::QuickKey(mwmp::QuickKey &quickKey): quickKey(quickKey)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int QuickKey::getSlot() const
|
||||
{
|
||||
return quickKey.slot;
|
||||
}
|
||||
|
||||
void QuickKey::setSlot(unsigned short slot)
|
||||
{
|
||||
quickKey.slot = slot;
|
||||
}
|
||||
|
||||
mwmp::QuickKey::Type QuickKey::getType() const
|
||||
{
|
||||
return quickKey.type;
|
||||
}
|
||||
|
||||
void QuickKey::setType(mwmp::QuickKey::Type type)
|
||||
{
|
||||
quickKey.type = type;
|
||||
}
|
||||
|
||||
std::string QuickKey::getItemId() const
|
||||
{
|
||||
return quickKey.itemId;
|
||||
}
|
||||
|
||||
void QuickKey::setItemId(const std::string &itemId)
|
||||
{
|
||||
quickKey.itemId = itemId;
|
||||
}
|
82
apps/openmw-mp/GUI.hpp
Normal file
82
apps/openmw-mp/GUI.hpp
Normal file
|
@ -0,0 +1,82 @@
|
|||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
#include "Window.hpp"
|
||||
#include "BaseMgr.hpp"
|
||||
|
||||
class LuaState;
|
||||
class Player;
|
||||
|
||||
class GUI final: public BaseMgr
|
||||
{
|
||||
public:
|
||||
static void Init(LuaState &lua);
|
||||
public:
|
||||
explicit GUI(Player *player);
|
||||
|
||||
void messageBox(sol::function fn, const char *label, sol::this_environment te);
|
||||
|
||||
void customMessageBox(sol::function fn, const char *label, const char *buttons, sol::this_environment te);
|
||||
void inputDialog(sol::function fn, const char *label, const char *note, sol::this_environment te);
|
||||
void passwordDialog(sol::function fn, const char *label, const char *note, sol::this_environment te);
|
||||
|
||||
void listBox(sol::function fn, const char *label, const char *items, sol::this_environment te);
|
||||
|
||||
void onGUIAction();
|
||||
|
||||
//state 0 - disallow, 1 - allow
|
||||
void setMapVisibility(unsigned short targetPID, unsigned short affectedPID, unsigned short state);
|
||||
void setMapVisibilityAll(unsigned short targetPID, unsigned short state);
|
||||
|
||||
std::shared_ptr<Window> createWindow(short x, short y, sol::function fn, sol::this_environment te);
|
||||
void deleteWindow(std::shared_ptr<Window> window);
|
||||
void onGUIWindowAction();
|
||||
|
||||
void addQuickKey(unsigned short slot, int type, const std::string &itemId);
|
||||
std::string getTopicId(unsigned int i) const;
|
||||
unsigned int getChanges() const;
|
||||
|
||||
private:
|
||||
uint64_t generateGuiId();
|
||||
void processUpdate() final;
|
||||
std::unordered_map<int, std::shared_ptr<Window>> windows;
|
||||
std::queue<std::pair<mwmp::BasePlayer::GUIMessageBox, sol::function>> guiQueue;
|
||||
int lastWindowId;
|
||||
};
|
||||
|
||||
class QuickKey
|
||||
{
|
||||
friend class QuickKeys;
|
||||
public:
|
||||
static void Init(LuaState &lua);
|
||||
public:
|
||||
explicit QuickKey(mwmp::QuickKey &quickKey);
|
||||
|
||||
int getSlot() const;
|
||||
void setSlot(unsigned short slot);
|
||||
|
||||
mwmp::QuickKey::Type getType() const;
|
||||
void setType(mwmp::QuickKey::Type slot);
|
||||
|
||||
std::string getItemId() const;
|
||||
void setItemId(const std::string &itemId);
|
||||
|
||||
mwmp::QuickKey quickKey;
|
||||
};
|
||||
|
||||
class QuickKeys final: public BaseMgr
|
||||
{
|
||||
public:
|
||||
static void Init(LuaState &lua);
|
||||
public:
|
||||
explicit QuickKeys(Player *player);
|
||||
|
||||
void addQuickKey(const QuickKey &quickKey);
|
||||
QuickKey getQuickKey(int id) const;
|
||||
void setQuickKey(int id, const QuickKey &quickKey);
|
||||
size_t size() const;
|
||||
void clear();
|
||||
|
||||
private:
|
||||
void processUpdate() final;
|
||||
};
|
188
apps/openmw-mp/Inventory.cpp
Normal file
188
apps/openmw-mp/Inventory.cpp
Normal file
|
@ -0,0 +1,188 @@
|
|||
//
|
||||
// Created by koncord on 12.08.17.
|
||||
//
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
|
||||
#include <apps/openmw/mwworld/inventorystore.hpp>
|
||||
|
||||
#include "Script/LuaState.hpp"
|
||||
#include "Networking.hpp"
|
||||
|
||||
#include "Inventory.hpp"
|
||||
#include "NetActor.hpp"
|
||||
#include <Player.hpp>
|
||||
|
||||
using namespace std;
|
||||
|
||||
void Inventory::Init(LuaState &lua)
|
||||
{
|
||||
lua.getState()->new_usertype<Inventory>("Inventory",
|
||||
"getInventoryChangesSize", &Inventory::getChangesSize,
|
||||
"addItem", &Inventory::addItem,
|
||||
"removeItem", &Inventory::removeItem,
|
||||
"getInventoryItem", &Inventory::getInventoryItem,
|
||||
|
||||
"equipItem", &Inventory::equipItem,
|
||||
"unequipItem", &Inventory::unequipItem,
|
||||
"hasItemEquipped", &Inventory::hasItemEquipped,
|
||||
"getEquipmentItem", &Inventory::getEquipmentItem
|
||||
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
Inventory::Inventory(NetActor *actor) : netActor(actor), equipmentChanged(false), inventoryChanged(false)
|
||||
{
|
||||
printf("Inventory::Inventory()\n");
|
||||
}
|
||||
|
||||
Inventory::~Inventory()
|
||||
{
|
||||
printf("Inventory::~Inventory()\n");
|
||||
}
|
||||
|
||||
void Inventory::update()
|
||||
{
|
||||
printf("Inventory::update()");
|
||||
/*if (isEquipmentChanged())
|
||||
{
|
||||
if (netActor->isPlayer())
|
||||
{
|
||||
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_EQUIPMENT);
|
||||
packet->setPlayer(dynamic_cast<Player *>(netActor));
|
||||
packet->Send(false);
|
||||
packet->Send(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto packet = mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_EQUIPMENT);
|
||||
packet->setActorList(&actorList);
|
||||
packet->Send(actorList.guid);
|
||||
|
||||
if (sendToAll)
|
||||
serverCell->sendToLoaded(packet, &actorList);
|
||||
}
|
||||
resetEquipmentFlag();
|
||||
}*/
|
||||
/*if (equipmentChanged)
|
||||
{
|
||||
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_EQUIPMENT);
|
||||
packet->setPlayer(netActor->getNetCreature());
|
||||
packet->Send(false);
|
||||
packet->Send(true);
|
||||
}
|
||||
|
||||
if (inventoryChanged != 0)
|
||||
{
|
||||
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_EQUIPMENT);
|
||||
packet->setPlayer(netActor->getNetCreature());
|
||||
packet->Send(false);
|
||||
}
|
||||
|
||||
equipmentChanged = false;
|
||||
inventoryChanged = 0;*/
|
||||
}
|
||||
|
||||
int Inventory::getChangesSize() const
|
||||
{
|
||||
return netActor->getNetCreature()->inventoryChanges.items.size();
|
||||
}
|
||||
|
||||
void Inventory::equipItem(unsigned short slot, const std::string& refId, unsigned int count, int charge, float enchantmentCharge)
|
||||
{
|
||||
netActor->getNetCreature()->equipmentItems[slot].refId = refId;
|
||||
netActor->getNetCreature()->equipmentItems[slot].count = count;
|
||||
netActor->getNetCreature()->equipmentItems[slot].charge = charge;
|
||||
netActor->getNetCreature()->equipmentItems[slot].enchantmentCharge = enchantmentCharge;
|
||||
|
||||
if (!Utils::vectorContains(&netActor->getNetCreature()->equipmentIndexChanges, slot))
|
||||
netActor->getNetCreature()->equipmentIndexChanges.push_back(slot);
|
||||
|
||||
if (!equipmentChanged && netActor->isPlayer())
|
||||
netActor->toPlayer()->addToUpdateQueue();
|
||||
|
||||
equipmentChanged = true;
|
||||
}
|
||||
|
||||
void Inventory::unequipItem( unsigned short slot)
|
||||
{
|
||||
equipItem(slot, "", 0, -1, -1);
|
||||
}
|
||||
|
||||
|
||||
void Inventory::addItem(const std::string &refId, unsigned int count, int charge, float enchantmentCharge)
|
||||
{
|
||||
if (!inventoryChanged)
|
||||
resetInventoryFlag();
|
||||
|
||||
mwmp::Item item;
|
||||
item.refId = refId;
|
||||
item.count = count;
|
||||
item.charge = charge;
|
||||
item.enchantmentCharge = enchantmentCharge;
|
||||
|
||||
netActor->getNetCreature()->inventoryChanges.items.emplace_back(item, mwmp::InventoryChanges::Action::Add);
|
||||
if (netActor->isPlayer())
|
||||
netActor->toPlayer()->addToUpdateQueue();
|
||||
inventoryChanged = true;
|
||||
}
|
||||
|
||||
void Inventory::removeItem(const std::string &refId, unsigned short count)
|
||||
{
|
||||
if (!inventoryChanged)
|
||||
resetInventoryFlag();
|
||||
mwmp::Item item;
|
||||
item.refId = refId;
|
||||
item.count = count;
|
||||
|
||||
netActor->getNetCreature()->inventoryChanges.items.emplace_back(item, mwmp::InventoryChanges::Action::Remove);
|
||||
if (netActor->isPlayer())
|
||||
netActor->toPlayer()->addToUpdateQueue();
|
||||
inventoryChanged = true;
|
||||
}
|
||||
|
||||
bool Inventory::hasItemEquipped(const std::string &refId) const
|
||||
{
|
||||
for (const auto &equipmentItem : netActor->getNetCreature()->equipmentItems)
|
||||
if (Misc::StringUtils::ciEqual(equipmentItem.refId, refId))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::tuple<std::string, int, int, double> Inventory::getEquipmentItem(unsigned short slot) const
|
||||
{
|
||||
const auto &item = netActor->getNetCreature()->equipmentItems[slot];
|
||||
return make_tuple(item.refId, item.count, item.charge, item.enchantmentCharge);
|
||||
}
|
||||
|
||||
std::tuple<std::string, int, int, double> Inventory::getInventoryItem(unsigned int slot) const
|
||||
{
|
||||
const auto &item = netActor->getNetCreature()->inventoryChanges.items.at(slot).first;
|
||||
return make_tuple(item.refId, item.count, item.charge, item.enchantmentCharge);
|
||||
}
|
||||
|
||||
void Inventory::resetEquipmentFlag()
|
||||
{
|
||||
equipmentChanged = false;
|
||||
|
||||
netActor->getNetCreature()->equipmentIndexChanges.clear();
|
||||
}
|
||||
|
||||
bool Inventory::isEquipmentChanged()
|
||||
{
|
||||
return equipmentChanged;
|
||||
}
|
||||
|
||||
bool Inventory::isInventoryChanged()
|
||||
{
|
||||
return inventoryChanged;
|
||||
}
|
||||
|
||||
void Inventory::resetInventoryFlag()
|
||||
{
|
||||
inventoryChanged = false;
|
||||
|
||||
netActor->getNetCreature()->inventoryChanges.items.clear();
|
||||
}
|
59
apps/openmw-mp/Inventory.hpp
Normal file
59
apps/openmw-mp/Inventory.hpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
//
|
||||
// Created by koncord on 12.08.17.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
class LuaState;
|
||||
class NetActor;
|
||||
|
||||
class Inventory
|
||||
{
|
||||
public:
|
||||
static void Init(LuaState &lua);
|
||||
bool isEquipmentChanged();
|
||||
void resetEquipmentFlag();
|
||||
bool isInventoryChanged();
|
||||
void resetInventoryFlag();
|
||||
public:
|
||||
explicit Inventory(NetActor *netActor);
|
||||
~Inventory();
|
||||
void update();
|
||||
|
||||
//inventory
|
||||
int getChangesSize() const;
|
||||
void addItem(const std::string& refId, unsigned int count, int charge, float enchantmentCharge);
|
||||
void removeItem(const std::string& refId, unsigned short count);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param slot
|
||||
* @return refid, count, charge, enchantmentCharge
|
||||
*/
|
||||
std::tuple<std::string,int, int, double> getInventoryItem(unsigned int slot) const;
|
||||
|
||||
|
||||
// equipment
|
||||
void equipItem(unsigned short slot, const std::string& refId, unsigned int count, int charge, float enchantmentCharge);
|
||||
void unequipItem(unsigned short slot);
|
||||
|
||||
bool hasItemEquipped(const std::string& refId) const;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param slot
|
||||
* @return refid, count, charge, enchantmentCharge
|
||||
*/
|
||||
std::tuple<std::string,int, int, double> getEquipmentItem(unsigned short slot) const;
|
||||
|
||||
private:
|
||||
// not controlled pointer
|
||||
NetActor *netActor;
|
||||
bool equipmentChanged;
|
||||
bool inventoryChanged;
|
||||
};
|
||||
|
||||
|
|
@ -2,18 +2,23 @@
|
|||
// Created by koncord on 14.08.16.
|
||||
//
|
||||
|
||||
#include <RakSleep.h>
|
||||
#include <Getche.h>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
|
||||
#include <Getche.h>
|
||||
#include <RakPeerInterface.h>
|
||||
#include "MasterClient.hpp"
|
||||
#include <RakSleep.h>
|
||||
|
||||
#include <components/openmw-mp/Log.hpp>
|
||||
#include <components/openmw-mp/Version.hpp>
|
||||
#include <components/openmw-mp/Master/PacketMasterAnnounce.hpp>
|
||||
|
||||
#include "MasterClient.hpp"
|
||||
#include "Networking.hpp"
|
||||
|
||||
#include "Players.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace mwmp;
|
||||
using namespace RakNet;
|
||||
|
@ -32,7 +37,7 @@ MasterClient::MasterClient(RakNet::RakPeerInterface *peer, std::string queryAddr
|
|||
void MasterClient::SetPlayers(unsigned pl)
|
||||
{
|
||||
mutexData.lock();
|
||||
if (queryData.GetPlayers() != pl)
|
||||
if ((unsigned) queryData.GetPlayers() != pl)
|
||||
{
|
||||
queryData.SetPlayers(pl);
|
||||
updated = true;
|
||||
|
@ -43,7 +48,7 @@ void MasterClient::SetPlayers(unsigned pl)
|
|||
void MasterClient::SetMaxPlayers(unsigned pl)
|
||||
{
|
||||
mutexData.lock();
|
||||
if (queryData.GetMaxPlayers() != pl)
|
||||
if ((unsigned) queryData.GetMaxPlayers() != pl)
|
||||
{
|
||||
queryData.SetMaxPlayers(pl);
|
||||
updated = true;
|
||||
|
@ -63,10 +68,10 @@ void MasterClient::SetHostname(std::string hostname)
|
|||
mutexData.unlock();
|
||||
}
|
||||
|
||||
void MasterClient::SetModname(std::string modname)
|
||||
void MasterClient::SetGameModeName(std::string modeName)
|
||||
{
|
||||
mutexData.lock();
|
||||
string substr = modname.substr(0, 200);
|
||||
string substr = modeName.substr(0, 200);
|
||||
if (queryData.GetGameMode() != substr)
|
||||
{
|
||||
queryData.SetGameMode(substr.c_str());
|
||||
|
@ -108,7 +113,7 @@ void MasterClient::SetRuleValue(std::string key, double value)
|
|||
void MasterClient::PushPlugin(Plugin plugin)
|
||||
{
|
||||
mutexData.lock();
|
||||
queryData.plugins.push_back(plugin);
|
||||
queryData.plugins.push_back(move(plugin));
|
||||
updated = true;
|
||||
mutexData.unlock();
|
||||
}
|
||||
|
@ -128,6 +133,10 @@ bool MasterClient::Process(RakNet::Packet *packet)
|
|||
case ID_CONNECTION_REQUEST_ACCEPTED:
|
||||
case ID_DISCONNECTION_NOTIFICATION:
|
||||
break;
|
||||
case ID_CONNECTION_BANNED:
|
||||
Stop();
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_FATAL, "Your server was banned on the master server. Contact the master server administrator for details.");
|
||||
break;
|
||||
case ID_MASTER_QUERY:
|
||||
break;
|
||||
case ID_MASTER_ANNOUNCE:
|
||||
|
@ -148,7 +157,7 @@ bool MasterClient::Process(RakNet::Packet *packet)
|
|||
}
|
||||
break;
|
||||
default:
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Received wrong packet from master server with id: %d", packet->data[0]);
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Received wrong packet from master server with id: %d", (int) packet->data[0]);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -195,22 +204,22 @@ void MasterClient::Thread()
|
|||
queryData.SetPassword((int) Networking::get().isPassworded());
|
||||
queryData.SetVersion(TES3MP_VERSION);
|
||||
|
||||
auto *players = Players::getPlayers();
|
||||
//auto *players = Players::getPlayers();
|
||||
while (sRun)
|
||||
{
|
||||
SetPlayers((int) players->size());
|
||||
SetPlayers((unsigned) Players::size());
|
||||
|
||||
auto pIt = players->begin();
|
||||
if (queryData.players.size() != players->size())
|
||||
auto pIt = Players::begin();
|
||||
if (queryData.players.size() != Players::size())
|
||||
{
|
||||
queryData.players.clear();
|
||||
updated = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; pIt != players->end(); i++, pIt++)
|
||||
for (int i = 0; pIt != Players::end(); i++, pIt++)
|
||||
{
|
||||
if (queryData.players[i] != pIt->second->npc.mName)
|
||||
if (queryData.players[i] != (*pIt)->npc.mName)
|
||||
{
|
||||
queryData.players.clear();
|
||||
updated = true;
|
||||
|
@ -222,13 +231,12 @@ void MasterClient::Thread()
|
|||
if (updated)
|
||||
{
|
||||
updated = false;
|
||||
if (pIt != players->end())
|
||||
if (pIt != Players::end())
|
||||
{
|
||||
for (auto player : *players)
|
||||
{
|
||||
if (!player.second->npc.mName.empty())
|
||||
queryData.players.push_back(player.second->npc.mName);
|
||||
}
|
||||
Players::for_each([this](auto player) {
|
||||
if (!player->npc.mName.empty())
|
||||
queryData.players.push_back(player->npc.mName);
|
||||
});
|
||||
}
|
||||
Send(PacketMasterAnnounce::FUNCTION_ANNOUNCE);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <components/openmw-mp/Master/MasterData.hpp>
|
||||
#include <RakString.h>
|
||||
#include <components/openmw-mp/Master/PacketMasterAnnounce.hpp>
|
||||
#include <atomic>
|
||||
|
||||
class MasterClient
|
||||
{
|
||||
|
@ -23,7 +24,7 @@ public:
|
|||
void SetPlayers(unsigned pl);
|
||||
void SetMaxPlayers(unsigned pl);
|
||||
void SetHostname(std::string hostname);
|
||||
void SetModname(std::string hostname);
|
||||
void SetGameModeName(std::string modeName);
|
||||
void SetRuleString(std::string key, std::string value);
|
||||
void SetRuleValue(std::string key, double value);
|
||||
void PushPlugin(Plugin plugin);
|
||||
|
@ -46,7 +47,7 @@ private:
|
|||
std::thread thrQuery;
|
||||
mwmp::PacketMasterAnnounce pma;
|
||||
RakNet::BitStream writeStream;
|
||||
bool updated;
|
||||
std::atomic<bool> updated;
|
||||
};
|
||||
|
||||
|
||||
|
|
143
apps/openmw-mp/NetActor.cpp
Normal file
143
apps/openmw-mp/NetActor.cpp
Normal file
|
@ -0,0 +1,143 @@
|
|||
//
|
||||
// Created by koncord on 25.08.17.
|
||||
//
|
||||
|
||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
#include <components/openmw-mp/Base/BaseNetCreature.hpp>
|
||||
|
||||
#include "Script/LuaState.hpp"
|
||||
#include "Networking.hpp"
|
||||
|
||||
#include "NetActor.hpp"
|
||||
#include "Player.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
NetActor::NetActor() : inventory(this), cellAPI(this), isActorPlayer(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void NetActor::resetUpdateFlags()
|
||||
{
|
||||
baseInfoChanged = false;
|
||||
shapeshiftChanged = false;
|
||||
levelChanged = false;
|
||||
statsChanged = false;
|
||||
positionChanged = false;
|
||||
skillsChanged = false;
|
||||
attributesChanged = false;
|
||||
}
|
||||
|
||||
std::tuple<float, float, float> NetActor::getPosition() const
|
||||
{
|
||||
return make_tuple(netCreature->position.pos[0], netCreature->position.pos[1], netCreature->position.pos[2]);
|
||||
}
|
||||
|
||||
void NetActor::setPosition(float x, float y, float z)
|
||||
{
|
||||
netCreature->position.pos[0] = x;
|
||||
netCreature->position.pos[1] = y;
|
||||
netCreature->position.pos[2] = z;
|
||||
|
||||
if (!positionChanged && isPlayer())
|
||||
toPlayer()->addToUpdateQueue();
|
||||
positionChanged = true;
|
||||
}
|
||||
|
||||
std::tuple<float, float> NetActor::getRotation() const
|
||||
{
|
||||
return make_tuple(netCreature->position.rot[0], netCreature->position.rot[2]);
|
||||
}
|
||||
|
||||
void NetActor::setRotation(float x, float z)
|
||||
{
|
||||
netCreature->position.rot[0] = x;
|
||||
netCreature->position.rot[2] = z;
|
||||
|
||||
if (!positionChanged && isPlayer())
|
||||
toPlayer()->addToUpdateQueue();
|
||||
positionChanged = true;
|
||||
}
|
||||
|
||||
void NetActor::setMomentum(float x, float y, float z)
|
||||
{
|
||||
netCreature->momentum.pos[0] = x;
|
||||
netCreature->momentum.pos[1] = y;
|
||||
netCreature->momentum.pos[2] = z;
|
||||
|
||||
if (!momentumChanged && isPlayer())
|
||||
toPlayer()->addToUpdateQueue();
|
||||
momentumChanged = true;
|
||||
}
|
||||
|
||||
std::tuple<float, float> NetActor::getHealth() const
|
||||
{
|
||||
return make_tuple(netCreature->creatureStats.mDynamic[0].mBase, netCreature->creatureStats.mDynamic[0].mCurrent);
|
||||
}
|
||||
|
||||
void NetActor::setHealth(float base, float current)
|
||||
{
|
||||
netCreature->creatureStats.mDynamic[0].mBase = base;
|
||||
netCreature->creatureStats.mDynamic[0].mCurrent = current;
|
||||
|
||||
if (!Utils::vectorContains(&netCreature->statsDynamicIndexChanges, 0))
|
||||
netCreature->statsDynamicIndexChanges.push_back(0);
|
||||
|
||||
if (!statsChanged && isPlayer())
|
||||
toPlayer()->addToUpdateQueue();
|
||||
statsChanged = true;
|
||||
}
|
||||
|
||||
std::tuple<float, float> NetActor::getMagicka() const
|
||||
{
|
||||
return make_tuple(netCreature->creatureStats.mDynamic[1].mBase, netCreature->creatureStats.mDynamic[1].mCurrent);
|
||||
}
|
||||
|
||||
void NetActor::setMagicka(float base, float current)
|
||||
{
|
||||
netCreature->creatureStats.mDynamic[1].mBase = base;
|
||||
netCreature->creatureStats.mDynamic[1].mCurrent = current;
|
||||
|
||||
if (!Utils::vectorContains(&netCreature->statsDynamicIndexChanges, 1))
|
||||
netCreature->statsDynamicIndexChanges.push_back(1);
|
||||
|
||||
if (!statsChanged && isPlayer())
|
||||
toPlayer()->addToUpdateQueue();
|
||||
statsChanged = true;
|
||||
}
|
||||
|
||||
std::tuple<float, float> NetActor::getFatigue() const
|
||||
{
|
||||
return make_tuple(netCreature->creatureStats.mDynamic[2].mBase, netCreature->creatureStats.mDynamic[2].mCurrent);
|
||||
}
|
||||
|
||||
void NetActor::setFatigue(float base, float current)
|
||||
{
|
||||
netCreature->creatureStats.mDynamic[2].mBase = base;
|
||||
netCreature->creatureStats.mDynamic[2].mCurrent = current;
|
||||
|
||||
if (!Utils::vectorContains(&netCreature->statsDynamicIndexChanges, 2))
|
||||
netCreature->statsDynamicIndexChanges.push_back(2);
|
||||
|
||||
if (!statsChanged && isPlayer())
|
||||
toPlayer()->addToUpdateQueue();
|
||||
statsChanged = true;
|
||||
}
|
||||
|
||||
Inventory &NetActor::getInventory()
|
||||
{
|
||||
return inventory;
|
||||
}
|
||||
|
||||
Cells &NetActor::getCell()
|
||||
{
|
||||
return cellAPI;
|
||||
}
|
||||
|
||||
Player *NetActor::toPlayer()
|
||||
{
|
||||
if (isPlayer())
|
||||
return dynamic_cast<Player*>(this);
|
||||
return nullptr;
|
||||
}
|
83
apps/openmw-mp/NetActor.hpp
Normal file
83
apps/openmw-mp/NetActor.hpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
//
|
||||
// Created by koncord on 25.08.17.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <RakNetTypes.h>
|
||||
#include <tuple>
|
||||
#include "Inventory.hpp"
|
||||
#include "Cells.hpp"
|
||||
|
||||
namespace mwmp
|
||||
{
|
||||
class BasePlayer;
|
||||
class BaseNetCreature;
|
||||
class BaseActor;
|
||||
}
|
||||
|
||||
class NetActor
|
||||
{
|
||||
public:
|
||||
NetActor();
|
||||
virtual ~NetActor() = default;
|
||||
|
||||
void resetUpdateFlags();
|
||||
|
||||
/**
|
||||
*
|
||||
* @return x, y, z
|
||||
*/
|
||||
|
||||
std::tuple<float, float, float> getPosition() const;
|
||||
void setPosition(float x, float y, float z);
|
||||
|
||||
/**
|
||||
*
|
||||
* @return x, y
|
||||
*/
|
||||
std::tuple<float, float> getRotation() const;
|
||||
void setRotation(float x, float z);
|
||||
|
||||
void setMomentum(float x, float y, float z);
|
||||
|
||||
/**
|
||||
*
|
||||
* @return base, current
|
||||
*/
|
||||
std::tuple<float, float> getHealth() const;
|
||||
void setHealth(float base, float current);
|
||||
|
||||
/**
|
||||
*
|
||||
* @return base, current
|
||||
*/
|
||||
std::tuple<float, float> getMagicka() const;
|
||||
void setMagicka(float base, float current);
|
||||
|
||||
/**
|
||||
*
|
||||
* @return base, current
|
||||
*/
|
||||
std::tuple<float, float> getFatigue() const;
|
||||
void setFatigue(float base, float current);
|
||||
|
||||
Inventory &getInventory();
|
||||
Cells &getCell();
|
||||
|
||||
mwmp::BaseNetCreature *getNetCreature() { return netCreature; }
|
||||
|
||||
bool isPlayer() const { return isActorPlayer; }
|
||||
Player *toPlayer();
|
||||
protected:
|
||||
bool baseInfoChanged, shapeshiftChanged, levelChanged, statsChanged, positionChanged, momentumChanged, attributesChanged, skillsChanged, aiChanged;
|
||||
|
||||
mwmp::BasePlayer *basePlayer;
|
||||
mwmp::BaseNetCreature *netCreature;
|
||||
|
||||
Inventory inventory;
|
||||
Cells cellAPI;
|
||||
bool isActorPlayer;
|
||||
};
|
||||
|
||||
|
|
@ -2,10 +2,12 @@
|
|||
// Created by koncord on 12.01.16.
|
||||
//
|
||||
|
||||
#include "Player.hpp"
|
||||
#include "processors/ProcessorInitializer.hpp"
|
||||
#include <RakPeer.h>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include <Kbhit.h>
|
||||
#include <RakPeer.h>
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
|
@ -13,25 +15,24 @@
|
|||
#include <components/openmw-mp/Version.hpp>
|
||||
#include <components/openmw-mp/Packets/PacketPreInit.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <Script/Script.hpp>
|
||||
#include <Script/API/TimerAPI.hpp>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "Networking.hpp"
|
||||
#include "MasterClient.hpp"
|
||||
#include "Cell.hpp"
|
||||
#include "CellController.hpp"
|
||||
#include <Script/EventController.hpp>
|
||||
#include "processors/ProcessorInitializer.hpp"
|
||||
#include "processors/PlayerProcessor.hpp"
|
||||
#include "processors/ActorProcessor.hpp"
|
||||
#include "processors/ObjectProcessor.hpp"
|
||||
#include "processors/WorldstateProcessor.hpp"
|
||||
|
||||
#include "Networking.hpp"
|
||||
#include "MasterClient.hpp"
|
||||
|
||||
#include "Cell.hpp"
|
||||
#include "CellController.hpp"
|
||||
#include "Players.hpp"
|
||||
|
||||
using namespace mwmp;
|
||||
using namespace std;
|
||||
|
||||
Networking *Networking::sThis = 0;
|
||||
Networking *Networking::sThis = nullptr;
|
||||
|
||||
static int currentMpNum = 0;
|
||||
static bool pluginEnforcementState = true;
|
||||
|
@ -40,42 +41,30 @@ Networking::Networking(RakNet::RakPeerInterface *peer) : mclient(nullptr)
|
|||
{
|
||||
sThis = this;
|
||||
this->peer = peer;
|
||||
players = Players::getPlayers();
|
||||
|
||||
CellController::create();
|
||||
|
||||
playerPacketController = new PlayerPacketController(peer);
|
||||
actorPacketController = new ActorPacketController(peer);
|
||||
objectPacketController = new ObjectPacketController(peer);
|
||||
worldstatePacketController = new WorldstatePacketController(peer);
|
||||
playerPacketController = make_unique<PlayerPacketController>(peer);
|
||||
actorPacketController = make_unique<ActorPacketController>(peer);
|
||||
objectPacketController = make_unique<ObjectPacketController>(peer);
|
||||
worldstatePacketController = make_unique<WorldstatePacketController>(peer);
|
||||
|
||||
// Set send stream
|
||||
playerPacketController->SetStream(0, &bsOut);
|
||||
actorPacketController->SetStream(0, &bsOut);
|
||||
objectPacketController->SetStream(0, &bsOut);
|
||||
worldstatePacketController->SetStream(0, &bsOut);
|
||||
playerPacketController->SetStream(nullptr, &bsOut);
|
||||
actorPacketController->SetStream(nullptr, &bsOut);
|
||||
objectPacketController->SetStream(nullptr, &bsOut);
|
||||
worldstatePacketController->SetStream(nullptr, &bsOut);
|
||||
|
||||
running = true;
|
||||
exitCode = 0;
|
||||
|
||||
Script::Call<Script::CallbackIdentity("OnServerInit")>();
|
||||
|
||||
serverPassword = TES3MP_DEFAULT_PASSW;
|
||||
|
||||
ProcessorInitializer();
|
||||
createChannel(); // create Default channel
|
||||
}
|
||||
|
||||
Networking::~Networking()
|
||||
{
|
||||
Script::Call<Script::CallbackIdentity("OnServerExit")>(false);
|
||||
|
||||
CellController::destroy();
|
||||
|
||||
sThis = 0;
|
||||
delete playerPacketController;
|
||||
delete actorPacketController;
|
||||
delete objectPacketController;
|
||||
delete worldstatePacketController;
|
||||
luaState.getEventCtrl().Call<CoreEvent::ON_EXIT>(false);
|
||||
}
|
||||
|
||||
void Networking::setServerPassword(std::string passw) noexcept
|
||||
|
@ -90,13 +79,13 @@ bool Networking::isPassworded() const
|
|||
|
||||
void Networking::processPlayerPacket(RakNet::Packet *packet)
|
||||
{
|
||||
Player *player = Players::getPlayer(packet->guid);
|
||||
auto player = Players::getPlayerByGUID(packet->guid);
|
||||
|
||||
PlayerPacket *myPacket = playerPacketController->GetPacket(packet->data[0]);
|
||||
|
||||
if (packet->data[0] == ID_HANDSHAKE)
|
||||
{
|
||||
myPacket->setPlayer(player);
|
||||
myPacket->setPlayer(player.get());
|
||||
myPacket->Read();
|
||||
|
||||
if (player->isHandshaked())
|
||||
|
@ -136,16 +125,16 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
|
|||
if (packet->data[0] == ID_LOADED)
|
||||
{
|
||||
player->setLoadState(Player::LOADED);
|
||||
player->joinChannel(0, "Default");
|
||||
|
||||
static constexpr unsigned int ident = Script::CallbackIdentity("OnPlayerConnect");
|
||||
Script::CallBackReturn<ident> result = true;
|
||||
Script::Call<ident>(result, Players::getPlayer(packet->guid)->getId());
|
||||
luaState.getEventCtrl().Call<CoreEvent::ON_PLAYER_CONNECT>(player.get());
|
||||
|
||||
if (!result)
|
||||
if (peer->GetConnectionState(player->guid) != RakNet::ConnectionState::IS_CONNECTED)
|
||||
{
|
||||
playerPacketController->GetPacket(ID_USER_DISCONNECTED)->setPlayer(Players::getPlayer(packet->guid));
|
||||
LOG_MESSAGE(Log::LOG_TRACE, "Player \"%s\" Disconnected by ON_PLAYER_CONNECT event", player->getName().c_str());
|
||||
playerPacketController->GetPacket(ID_USER_DISCONNECTED)->setPlayer(player.get());
|
||||
playerPacketController->GetPacket(ID_USER_DISCONNECTED)->Send(false);
|
||||
Players::deletePlayer(packet->guid);
|
||||
Players::deletePlayerByGUID(packet->guid);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -153,7 +142,7 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
|
|||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Received ID_PLAYER_BASEINFO about %s", player->npc.mName.c_str());
|
||||
|
||||
myPacket->setPlayer(player);
|
||||
myPacket->setPlayer(player.get());
|
||||
myPacket->Read();
|
||||
myPacket->Send(true);
|
||||
}
|
||||
|
@ -169,37 +158,37 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
|
|||
|
||||
|
||||
if (!PlayerProcessor::Process(*packet))
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled PlayerPacket with identifier %i has arrived", packet->data[0]);
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled PlayerPacket with identifier %i has arrived", (int) packet->data[0]);
|
||||
|
||||
}
|
||||
|
||||
void Networking::processActorPacket(RakNet::Packet *packet)
|
||||
{
|
||||
Player *player = Players::getPlayer(packet->guid);
|
||||
auto player = Players::getPlayerByGUID(packet->guid);
|
||||
|
||||
if (!player->isHandshaked() || player->getLoadState() != Player::POSTLOADED)
|
||||
return;
|
||||
|
||||
if (!ActorProcessor::Process(*packet, baseActorList))
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled ActorPacket with identifier %i has arrived", packet->data[0]);
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled ActorPacket with identifier %i has arrived", (int) packet->data[0]);
|
||||
|
||||
}
|
||||
|
||||
void Networking::processObjectPacket(RakNet::Packet *packet)
|
||||
{
|
||||
Player *player = Players::getPlayer(packet->guid);
|
||||
auto player = Players::getPlayerByGUID(packet->guid);
|
||||
|
||||
if (!player->isHandshaked() || player->getLoadState() != Player::POSTLOADED)
|
||||
return;
|
||||
|
||||
if (!ObjectProcessor::Process(*packet, baseObjectList))
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled ObjectPacket with identifier %i has arrived", packet->data[0]);
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled ObjectPacket with identifier %i has arrived", (int) packet->data[0]);
|
||||
|
||||
}
|
||||
|
||||
void Networking::processWorldstatePacket(RakNet::Packet *packet)
|
||||
{
|
||||
Player *player = Players::getPlayer(packet->guid);
|
||||
auto player = Players::getPlayerByGUID(packet->guid);
|
||||
|
||||
if (!player->isHandshaked() || player->getLoadState() != Player::POSTLOADED)
|
||||
return;
|
||||
|
@ -209,15 +198,15 @@ void Networking::processWorldstatePacket(RakNet::Packet *packet)
|
|||
|
||||
}
|
||||
|
||||
void Networking::update(RakNet::Packet *packet)
|
||||
bool Networking::update(RakNet::Packet *packet)
|
||||
{
|
||||
Player *player = Players::getPlayer(packet->guid);
|
||||
auto player = Players::getPlayerByGUID(packet->guid);
|
||||
|
||||
RakNet::BitStream bsIn(&packet->data[1], packet->length, false);
|
||||
|
||||
bsIn.IgnoreBytes((unsigned int) RakNet::RakNetGUID::size()); // Ignore GUID from received packet
|
||||
|
||||
if (player == 0)
|
||||
if (player == nullptr)
|
||||
{
|
||||
if (packet->data[0] == ID_GAME_PREINIT)
|
||||
{
|
||||
|
@ -247,7 +236,6 @@ void Networking::update(RakNet::Packet *packet)
|
|||
// the server
|
||||
if (it == hashList.end())
|
||||
break;
|
||||
|
||||
}
|
||||
else // name is incorrect
|
||||
break;
|
||||
|
@ -271,38 +259,42 @@ void Networking::update(RakNet::Packet *packet)
|
|||
packetPreInit.setChecksums(&tmp);
|
||||
packetPreInit.Send(packet->systemAddress);
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
playerPacketController->SetStream(&bsIn, 0);
|
||||
playerPacketController->SetStream(&bsIn, nullptr);
|
||||
|
||||
playerPacketController->GetPacket(ID_HANDSHAKE)->RequestData(packet->guid);
|
||||
Players::newPlayer(packet->guid);
|
||||
player = Players::getPlayer(packet->guid);
|
||||
return;
|
||||
player = Players::addPlayer(packet->guid);
|
||||
return false;
|
||||
}
|
||||
else if (playerPacketController->ContainsPacket(packet->data[0]))
|
||||
|
||||
if (playerPacketController->ContainsPacket(packet->data[0]))
|
||||
{
|
||||
playerPacketController->SetStream(&bsIn, 0);
|
||||
playerPacketController->SetStream(&bsIn, nullptr);
|
||||
processPlayerPacket(packet);
|
||||
}
|
||||
else if (actorPacketController->ContainsPacket(packet->data[0]))
|
||||
{
|
||||
actorPacketController->SetStream(&bsIn, 0);
|
||||
actorPacketController->SetStream(&bsIn, nullptr);
|
||||
processActorPacket(packet);
|
||||
}
|
||||
else if (objectPacketController->ContainsPacket(packet->data[0]))
|
||||
{
|
||||
objectPacketController->SetStream(&bsIn, 0);
|
||||
objectPacketController->SetStream(&bsIn, nullptr);
|
||||
processObjectPacket(packet);
|
||||
}
|
||||
else if (worldstatePacketController->ContainsPacket(packet->data[0]))
|
||||
{
|
||||
worldstatePacketController->SetStream(&bsIn, 0);
|
||||
worldstatePacketController->SetStream(&bsIn, nullptr);
|
||||
processWorldstatePacket(packet);
|
||||
}
|
||||
else
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled RakNet packet with identifier %i has arrived", packet->data[0]);
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled RakNet packet with identifier %i has arrived", (int) packet->data[0]);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Networking::newPlayer(RakNet::RakNetGUID guid)
|
||||
|
@ -315,27 +307,24 @@ void Networking::newPlayer(RakNet::RakNetGUID guid)
|
|||
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Sending info about other players to %lu", guid.g);
|
||||
|
||||
for (TPlayers::iterator pl = players->begin(); pl != players->end(); pl++) //sending other players to new player
|
||||
Players::for_each([this, &guid](auto pl) //sending other players to new player
|
||||
{
|
||||
// If we are iterating over the new player, don't send the packets below
|
||||
if (pl->first == guid) continue;
|
||||
if (pl->guid == guid) return;
|
||||
|
||||
// If an invalid key makes it into the Players map, ignore it
|
||||
else if (pl->first == RakNet::UNASSIGNED_CRABNET_GUID) continue;
|
||||
|
||||
// if player not fully connected
|
||||
else if (pl->second == nullptr) continue;
|
||||
else if (pl->guid == RakNet::UNASSIGNED_CRABNET_GUID) return;
|
||||
|
||||
// If we are iterating over a player who has inputted their name, proceed
|
||||
else if (pl->second->getLoadState() == Player::POSTLOADED)
|
||||
else if (pl->getLoadState() == Player::POSTLOADED)
|
||||
{
|
||||
playerPacketController->GetPacket(ID_PLAYER_BASEINFO)->setPlayer(pl->second);
|
||||
playerPacketController->GetPacket(ID_PLAYER_STATS_DYNAMIC)->setPlayer(pl->second);
|
||||
playerPacketController->GetPacket(ID_PLAYER_ATTRIBUTE)->setPlayer(pl->second);
|
||||
playerPacketController->GetPacket(ID_PLAYER_SKILL)->setPlayer(pl->second);
|
||||
playerPacketController->GetPacket(ID_PLAYER_POSITION)->setPlayer(pl->second);
|
||||
playerPacketController->GetPacket(ID_PLAYER_CELL_CHANGE)->setPlayer(pl->second);
|
||||
playerPacketController->GetPacket(ID_PLAYER_EQUIPMENT)->setPlayer(pl->second);
|
||||
playerPacketController->GetPacket(ID_PLAYER_BASEINFO)->setPlayer(pl);
|
||||
playerPacketController->GetPacket(ID_PLAYER_STATS_DYNAMIC)->setPlayer(pl);
|
||||
playerPacketController->GetPacket(ID_PLAYER_ATTRIBUTE)->setPlayer(pl);
|
||||
playerPacketController->GetPacket(ID_PLAYER_SKILL)->setPlayer(pl);
|
||||
playerPacketController->GetPacket(ID_PLAYER_POSITION)->setPlayer(pl);
|
||||
playerPacketController->GetPacket(ID_PLAYER_CELL_CHANGE)->setPlayer(pl);
|
||||
playerPacketController->GetPacket(ID_PLAYER_EQUIPMENT)->setPlayer(pl);
|
||||
|
||||
playerPacketController->GetPacket(ID_PLAYER_BASEINFO)->Send(guid);
|
||||
playerPacketController->GetPacket(ID_PLAYER_STATS_DYNAMIC)->Send(guid);
|
||||
|
@ -345,7 +334,7 @@ void Networking::newPlayer(RakNet::RakNetGUID guid)
|
|||
playerPacketController->GetPacket(ID_PLAYER_CELL_CHANGE)->Send(guid);
|
||||
playerPacketController->GetPacket(ID_PLAYER_EQUIPMENT)->Send(guid);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
LOG_APPEND(Log::LOG_WARN, "- Done");
|
||||
|
||||
|
@ -353,34 +342,35 @@ void Networking::newPlayer(RakNet::RakNetGUID guid)
|
|||
|
||||
void Networking::disconnectPlayer(RakNet::RakNetGUID guid)
|
||||
{
|
||||
Player *player = Players::getPlayer(guid);
|
||||
if (!player)
|
||||
auto player = Players::getPlayerByGUID(guid);
|
||||
if (player == nullptr)
|
||||
return;
|
||||
Script::Call<Script::CallbackIdentity("OnPlayerDisconnect")>(player->getId());
|
||||
|
||||
playerPacketController->GetPacket(ID_USER_DISCONNECTED)->setPlayer(player);
|
||||
luaState.getEventCtrl().Call<CoreEvent::ON_PLAYER_DISCONNECT>(player.get());
|
||||
|
||||
playerPacketController->GetPacket(ID_USER_DISCONNECTED)->setPlayer(player.get());
|
||||
playerPacketController->GetPacket(ID_USER_DISCONNECTED)->Send(true);
|
||||
Players::deletePlayer(guid);
|
||||
Players::deletePlayerByGUID(guid);
|
||||
}
|
||||
|
||||
PlayerPacketController *Networking::getPlayerPacketController() const
|
||||
{
|
||||
return playerPacketController;
|
||||
return playerPacketController.get();
|
||||
}
|
||||
|
||||
ActorPacketController *Networking::getActorPacketController() const
|
||||
{
|
||||
return actorPacketController;
|
||||
return actorPacketController.get();
|
||||
}
|
||||
|
||||
ObjectPacketController *Networking::getObjectPacketController() const
|
||||
{
|
||||
return objectPacketController;
|
||||
return objectPacketController.get();
|
||||
}
|
||||
|
||||
WorldstatePacketController *Networking::getWorldstatePacketController() const
|
||||
{
|
||||
return worldstatePacketController;
|
||||
return worldstatePacketController.get();
|
||||
}
|
||||
|
||||
BaseActorList *Networking::getLastActorList()
|
||||
|
@ -398,6 +388,11 @@ BaseWorldstate *Networking::getLastWorldstate()
|
|||
return &baseWorldstate;
|
||||
}
|
||||
|
||||
Worldstate *Networking::getServerWorldstate()
|
||||
{
|
||||
return &serverWorldstate;
|
||||
}
|
||||
|
||||
int Networking::getCurrentMpNum()
|
||||
{
|
||||
return currentMpNum;
|
||||
|
@ -411,7 +406,7 @@ void Networking::setCurrentMpNum(int value)
|
|||
int Networking::incrementMpNum()
|
||||
{
|
||||
currentMpNum++;
|
||||
Script::Call<Script::CallbackIdentity("OnMpNumIncrement")>(currentMpNum);
|
||||
luaState.getEventCtrl().Call<CoreEvent::ON_MP_REFNUM>(currentMpNum);
|
||||
return currentMpNum;
|
||||
}
|
||||
|
||||
|
@ -425,12 +420,11 @@ void Networking::setPluginEnforcementState(bool state)
|
|||
pluginEnforcementState = state;
|
||||
}
|
||||
|
||||
const Networking &Networking::get()
|
||||
Networking &Networking::get()
|
||||
{
|
||||
return *sThis;
|
||||
}
|
||||
|
||||
|
||||
Networking *Networking::getPtr()
|
||||
{
|
||||
return sThis;
|
||||
|
@ -448,20 +442,18 @@ PacketPreInit::PluginContainer Networking::getPluginListSample()
|
|||
while (true)
|
||||
{
|
||||
unsigned field = 0;
|
||||
auto name = "";
|
||||
Script::Call<Script::CallbackIdentity("OnRequestPluginList")>(name, id, field++);
|
||||
if (strlen(name) == 0)
|
||||
auto name = luaState.getEventCtrl().Call<CoreEvent::ON_REQUEST_PLUGIN_LIST, false, string>(id, field++);
|
||||
if (name.empty())
|
||||
break;
|
||||
PacketPreInit::HashList hashList;
|
||||
while (true)
|
||||
{
|
||||
auto hash = "";
|
||||
Script::Call<Script::CallbackIdentity("OnRequestPluginList")>(hash, id, field++);
|
||||
if (strlen(hash) == 0)
|
||||
auto hash = luaState.getEventCtrl().Call<CoreEvent::ON_REQUEST_PLUGIN_LIST, false, string>(id, field++);
|
||||
if (hash.empty())
|
||||
break;
|
||||
hashList.push_back((unsigned)stoul(hash));
|
||||
}
|
||||
pls.push_back({name, hashList});
|
||||
pls.emplace_back(name, hashList);
|
||||
id++;
|
||||
}
|
||||
return pls;
|
||||
|
@ -473,12 +465,46 @@ void Networking::stopServer(int code)
|
|||
exitCode = code;
|
||||
}
|
||||
|
||||
using hrclock = chrono::high_resolution_clock;
|
||||
|
||||
template<typename _Rep, typename _Period>
|
||||
hrclock::time_point limitTPS(const chrono::duration<_Rep, _Period> &limit)
|
||||
{
|
||||
|
||||
static hrclock::time_point now = hrclock::now();
|
||||
static auto last = now;
|
||||
now = hrclock::now();
|
||||
hrclock::duration delta = now - last;
|
||||
auto tmp = last;
|
||||
last = now;
|
||||
if (delta < limit)
|
||||
this_thread::sleep_for(limit - delta);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
int Networking::mainLoop()
|
||||
{
|
||||
RakNet::Packet *packet;
|
||||
|
||||
auto &timerCtrl = luaState.getTimerCtrl();
|
||||
|
||||
auto OneSecTimer = []() {
|
||||
static hrclock::time_point time;
|
||||
time = hrclock::now();
|
||||
|
||||
static auto last2 = time;
|
||||
hrclock::duration delta = time - last2;
|
||||
if (delta >= 1s)
|
||||
{
|
||||
last2 = time;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
while (running)
|
||||
{
|
||||
bool updated = false;
|
||||
if (kbhit() && getch() == '\n')
|
||||
break;
|
||||
for (packet=peer->Receive(); packet; peer->DeallocatePacket(packet), packet=peer->Receive())
|
||||
|
@ -521,15 +547,27 @@ int Networking::mainLoop()
|
|||
case ID_UNCONNECTED_PING:
|
||||
break;
|
||||
default:
|
||||
update(packet);
|
||||
updated = update(packet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
TimerAPI::Tick();
|
||||
this_thread::sleep_for(chrono::milliseconds(1));
|
||||
timerCtrl.tick();
|
||||
|
||||
if (updated)
|
||||
{
|
||||
Players::processUpdated();
|
||||
serverWorldstate.update();
|
||||
}
|
||||
|
||||
const int limit = 60;
|
||||
|
||||
auto t = limitTPS(1.0s / limit); // 60 ticks per 1 second
|
||||
|
||||
if (OneSecTimer())
|
||||
printf("TPS %.1Lf / %d \n", 1.0s / (hrclock::now() - t), limit);
|
||||
}
|
||||
|
||||
TimerAPI::Terminate();
|
||||
timerCtrl.terminate();
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
|
@ -565,17 +603,17 @@ int Networking::getAvgPing(RakNet::AddressOrGUID addr) const
|
|||
|
||||
MasterClient *Networking::getMasterClient()
|
||||
{
|
||||
return mclient;
|
||||
return mclient.get();
|
||||
}
|
||||
|
||||
void Networking::InitQuery(std::string queryAddr, unsigned short queryPort)
|
||||
void Networking::InitQuery(const std::string &queryAddr, unsigned short queryPort)
|
||||
{
|
||||
mclient = new MasterClient(peer, queryAddr, queryPort);
|
||||
mclient = make_unique<MasterClient>(peer, queryAddr, queryPort);
|
||||
}
|
||||
|
||||
void Networking::postInit()
|
||||
{
|
||||
Script::Call<Script::CallbackIdentity("OnServerPostInit")>();
|
||||
luaState.getEventCtrl().Call<CoreEvent::ON_POST_INIT>();
|
||||
samples = getPluginListSample();
|
||||
if (mclient)
|
||||
{
|
||||
|
@ -588,3 +626,41 @@ void Networking::postInit()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<ChatChannel> Networking::getChannel(unsigned id)
|
||||
{
|
||||
auto it = chatChannels.find(id);
|
||||
if (it != chatChannels.end())
|
||||
return it->second;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
unsigned Networking::createChannel()
|
||||
{
|
||||
static unsigned lastChatId = 0;
|
||||
unsigned id = 0;
|
||||
for (auto &channel : chatChannels)
|
||||
{
|
||||
if (channel.second == nullptr)
|
||||
id = channel.first;
|
||||
}
|
||||
|
||||
if (id == 0)
|
||||
id = lastChatId++;
|
||||
|
||||
|
||||
chatChannels[id] = make_shared<ChatChannel>();
|
||||
return id;
|
||||
}
|
||||
|
||||
bool Networking::closeChannel(unsigned id)
|
||||
{
|
||||
auto it = chatChannels.find(id);
|
||||
if (it != chatChannels.end())
|
||||
{
|
||||
it->second = nullptr;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -6,11 +6,20 @@
|
|||
#include <components/openmw-mp/Controllers/ObjectPacketController.hpp>
|
||||
#include <components/openmw-mp/Controllers/WorldstatePacketController.hpp>
|
||||
#include <components/openmw-mp/Packets/PacketPreInit.hpp>
|
||||
#include "Player.hpp"
|
||||
#include <apps/openmw-mp/Script/LuaState.hpp>
|
||||
#include <apps/openmw-mp/Worldstate.hpp>
|
||||
|
||||
class MasterClient;
|
||||
namespace mwmp
|
||||
{
|
||||
struct ChatChannel
|
||||
{
|
||||
ChatChannel()
|
||||
{
|
||||
}
|
||||
std::vector<std::weak_ptr<Player>> members;
|
||||
};
|
||||
|
||||
class Networking
|
||||
{
|
||||
public:
|
||||
|
@ -29,7 +38,7 @@ namespace mwmp
|
|||
void processActorPacket(RakNet::Packet *packet);
|
||||
void processObjectPacket(RakNet::Packet *packet);
|
||||
void processWorldstatePacket(RakNet::Packet *packet);
|
||||
void update(RakNet::Packet *packet);
|
||||
bool update(RakNet::Packet *packet);
|
||||
|
||||
unsigned short numberOfConnections() const;
|
||||
unsigned int maxConnections() const;
|
||||
|
@ -44,9 +53,12 @@ namespace mwmp
|
|||
ObjectPacketController *getObjectPacketController() const;
|
||||
WorldstatePacketController *getWorldstatePacketController() const;
|
||||
|
||||
LuaState &getState() {return luaState;}
|
||||
|
||||
BaseActorList *getLastActorList();
|
||||
BaseObjectList *getLastObjectList();
|
||||
BaseWorldstate *getLastWorldstate();
|
||||
Worldstate *getServerWorldstate();
|
||||
|
||||
int getCurrentMpNum();
|
||||
void setCurrentMpNum(int value);
|
||||
|
@ -56,36 +68,45 @@ namespace mwmp
|
|||
void setPluginEnforcementState(bool state);
|
||||
|
||||
MasterClient *getMasterClient();
|
||||
void InitQuery(std::string queryAddr, unsigned short queryPort);
|
||||
void InitQuery(const std::string &queryAddr, unsigned short queryPort);
|
||||
void setServerPassword(std::string passw) noexcept;
|
||||
bool isPassworded() const;
|
||||
|
||||
static const Networking &get();
|
||||
static Networking &get();
|
||||
static Networking *getPtr();
|
||||
|
||||
void postInit();
|
||||
|
||||
std::shared_ptr<ChatChannel> getChannel(unsigned id);
|
||||
unsigned createChannel();
|
||||
bool closeChannel(unsigned id);
|
||||
|
||||
private:
|
||||
LuaState luaState;
|
||||
PacketPreInit::PluginContainer getPluginListSample();
|
||||
std::string serverPassword;
|
||||
static Networking *sThis;
|
||||
|
||||
RakNet::RakPeerInterface *peer;
|
||||
RakNet::BitStream bsOut;
|
||||
TPlayers *players;
|
||||
MasterClient *mclient;
|
||||
std::unique_ptr<MasterClient> mclient;
|
||||
|
||||
BaseActorList baseActorList;
|
||||
BaseObjectList baseObjectList;
|
||||
BaseWorldstate baseWorldstate;
|
||||
|
||||
PlayerPacketController *playerPacketController;
|
||||
ActorPacketController *actorPacketController;
|
||||
ObjectPacketController *objectPacketController;
|
||||
WorldstatePacketController *worldstatePacketController;
|
||||
Worldstate serverWorldstate;
|
||||
|
||||
std::unique_ptr<PlayerPacketController> playerPacketController;
|
||||
std::unique_ptr<ActorPacketController> actorPacketController;
|
||||
std::unique_ptr<ObjectPacketController> objectPacketController;
|
||||
std::unique_ptr<WorldstatePacketController> worldstatePacketController;
|
||||
|
||||
bool running;
|
||||
int exitCode;
|
||||
PacketPreInit::PluginContainer samples;
|
||||
|
||||
std::unordered_map<unsigned, std::shared_ptr<ChatChannel>> chatChannels;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
595
apps/openmw-mp/Object.cpp
Normal file
595
apps/openmw-mp/Object.cpp
Normal file
|
@ -0,0 +1,595 @@
|
|||
//
|
||||
// Created by koncord on 26.08.17.
|
||||
//
|
||||
|
||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
|
||||
#include "Networking.hpp"
|
||||
|
||||
#include "Object.hpp"
|
||||
#include "Player.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
void Object::Init(LuaState &lua)
|
||||
{
|
||||
lua.getState()->new_usertype<Object>("Object",
|
||||
"refId", sol::property(&BaseObject::getRefId, &BaseObject::setRefId),
|
||||
"refNum", sol::property(&BaseObject::getRefNum, &BaseObject::setRefNum),
|
||||
"mpNum", sol::property(&BaseObject::getMpNum, &BaseObject::setMpNum),
|
||||
"guid", sol::property(&BaseObject::getGuid, &BaseObject::setGuid),
|
||||
"getPosition", &Object::getPosition,
|
||||
"setPosition", &Object::setPosition,
|
||||
"getRotation", &Object::getRotation,
|
||||
"setRotation", &Object::setRotation,
|
||||
"count", sol::property(&Object::getCount, &Object::setCount),
|
||||
"goldValue", sol::property(&Object::getGoldValue, &Object::setGoldValue),
|
||||
"scale", sol::property(&Object::getScale, &Object::setScale),
|
||||
"state", sol::property(&Object::getState, &Object::setState),
|
||||
"lockLevel", sol::property(&Object::getLockLevel, &Object::setLockLevel),
|
||||
"doorState", sol::property(&Object::getDoorState, &Object::setDoorState),
|
||||
"hasContainer", &Object::hasContainer,
|
||||
"setTeleportState", &Object::setTeleportState,
|
||||
"setDoorDestination", &Object::setDoorDestination,
|
||||
"setDisarmState", &Object::setDisarmState,
|
||||
"setMasterState", &Object::setMasterState
|
||||
);
|
||||
}
|
||||
|
||||
Object::Object()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Object::~Object()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Object::update()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
tuple<float, float, float> Object::getPosition() const
|
||||
{
|
||||
return make_tuple(object.position.pos[0], object.position.pos[1], object.position.pos[2]);
|
||||
}
|
||||
|
||||
void Object::setPosition(float x, float y, float z)
|
||||
{
|
||||
object.position.pos[0] = x;
|
||||
object.position.pos[1] = y;
|
||||
object.position.pos[2] = z;
|
||||
object.droppedByPlayer = false;
|
||||
changedObjectPlace = true;
|
||||
}
|
||||
|
||||
tuple<float, float, float> Object::getRotation() const
|
||||
{
|
||||
return make_tuple(object.position.rot[0], object.position.rot[1], object.position.rot[2]);
|
||||
}
|
||||
|
||||
void Object::setRotation(float x, float y, float z)
|
||||
{
|
||||
object.position.rot[0] = x;
|
||||
object.position.rot[1] = y;
|
||||
object.position.rot[2] = z;
|
||||
changedObjectPlace = true;
|
||||
}
|
||||
|
||||
BaseObject::BaseObject(): changedBase(false), copied(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
string BaseObject::getRefId() const
|
||||
{
|
||||
return object.refId;
|
||||
}
|
||||
|
||||
void BaseObject::setRefId(const string &refId)
|
||||
{
|
||||
changedBase = true;
|
||||
object.refId = refId;
|
||||
}
|
||||
|
||||
unsigned BaseObject::getRefNum() const
|
||||
{
|
||||
return object.refNumIndex;
|
||||
}
|
||||
|
||||
void BaseObject::setRefNum(unsigned refNum)
|
||||
{
|
||||
changedBase = true;
|
||||
object.refNumIndex = refNum;
|
||||
}
|
||||
|
||||
unsigned BaseObject::getMpNum() const
|
||||
{
|
||||
return object.mpNum;
|
||||
}
|
||||
|
||||
void BaseObject::setMpNum(unsigned mpNum)
|
||||
{
|
||||
changedBase = true;
|
||||
object.mpNum = mpNum;
|
||||
}
|
||||
|
||||
RakNet::RakNetGUID BaseObject::getGuid() const
|
||||
{
|
||||
return object.guid;
|
||||
}
|
||||
|
||||
void BaseObject::setGuid(const RakNet::RakNetGUID &guid)
|
||||
{
|
||||
changedBase = true;
|
||||
object.guid = guid;
|
||||
object.isPlayer = true;
|
||||
}
|
||||
|
||||
int Object::getCount() const
|
||||
{
|
||||
return object.count;
|
||||
}
|
||||
|
||||
void Object::setCount(int count)
|
||||
{
|
||||
changedObjectPlace = true;
|
||||
object.count = count;
|
||||
}
|
||||
|
||||
int Object::getCharge() const
|
||||
{
|
||||
return object.charge;
|
||||
}
|
||||
|
||||
void Object::setCharge(int charge)
|
||||
{
|
||||
changedObjectPlace = true;
|
||||
object.charge = charge;
|
||||
|
||||
}
|
||||
|
||||
float Object::getEnchantmentCharge() const
|
||||
{
|
||||
return object.enchantmentCharge;
|
||||
}
|
||||
|
||||
void Object::setEnchantmentCharge(float enchantmentCharge)
|
||||
{
|
||||
changedObjectPlace = true;
|
||||
object.enchantmentCharge = enchantmentCharge;
|
||||
|
||||
}
|
||||
|
||||
int Object::getGoldValue() const
|
||||
{
|
||||
return object.goldValue;
|
||||
}
|
||||
|
||||
void Object::setGoldValue(int gold)
|
||||
{
|
||||
changedObjectPlace = true;
|
||||
object.goldValue = gold;
|
||||
}
|
||||
|
||||
float Object::getScale() const
|
||||
{
|
||||
return object.scale;
|
||||
}
|
||||
|
||||
void Object::setScale(float scale)
|
||||
{
|
||||
changedObjectScale = true;
|
||||
object.scale = scale;
|
||||
}
|
||||
|
||||
bool Object::getState() const
|
||||
{
|
||||
return object.objectState;
|
||||
}
|
||||
|
||||
void Object::setState(bool state)
|
||||
{
|
||||
changedObjectState = true;
|
||||
object.objectState = state;
|
||||
}
|
||||
|
||||
int Object::getDoorState() const
|
||||
{
|
||||
return object.doorState;
|
||||
}
|
||||
|
||||
void Object::setDoorState(int state)
|
||||
{
|
||||
changedDoorState = true;
|
||||
object.doorState = state;
|
||||
}
|
||||
|
||||
int Object::getLockLevel() const
|
||||
{
|
||||
return object.lockLevel;
|
||||
}
|
||||
|
||||
void Object::setLockLevel(int locklevel)
|
||||
{
|
||||
changedObjectLock = true;
|
||||
object.lockLevel = locklevel;
|
||||
}
|
||||
|
||||
bool Object::hasContainer() const
|
||||
{
|
||||
return object.hasContainer;
|
||||
}
|
||||
|
||||
void Object::setTeleportState(bool state)
|
||||
{
|
||||
changedDoorDestination = true;
|
||||
object.teleportState = state;
|
||||
}
|
||||
|
||||
void Object::setDoorDestination(const std::string &cellDescription, float posX, float posY, float posZ, float rotX, float rotY, float rotZ)
|
||||
{
|
||||
changedDoorDestination = true;
|
||||
object.destinationCell = Utils::getCellFromDescription(cellDescription);
|
||||
object.destinationPosition.pos[0] = posX;
|
||||
object.destinationPosition.pos[1] = posY;
|
||||
object.destinationPosition.pos[2] = posZ;
|
||||
object.destinationPosition.rot[0] = rotX;
|
||||
object.destinationPosition.rot[1] = rotY;
|
||||
object.destinationPosition.rot[2] = rotZ;
|
||||
}
|
||||
|
||||
void Object::setDisarmState(bool state)
|
||||
{
|
||||
changedObjectTrap = true;
|
||||
object.isDisarmed = state;
|
||||
}
|
||||
|
||||
void Object::setMasterState(bool state)
|
||||
{
|
||||
changedObjectSpawn = true;
|
||||
object.hasMaster = state;
|
||||
}
|
||||
|
||||
|
||||
// TODO: Make this actually reflect the capabilities offered by containers in 0.6.3
|
||||
void Container::Init(LuaState &lua)
|
||||
{
|
||||
lua.getState()->new_usertype<Container>("Container",
|
||||
"refId", sol::property(&BaseObject::getRefId, &BaseObject::setRefId),
|
||||
"refNum", sol::property(&BaseObject::getRefNum, &BaseObject::setRefNum),
|
||||
"mpNum", sol::property(&BaseObject::getMpNum, &BaseObject::setMpNum),
|
||||
"getItem", &Container::getItem,
|
||||
"addItem", &Container::addItem,
|
||||
"setItem", &Container::setItem,
|
||||
"getActionCount", &Container::getActionCount
|
||||
);
|
||||
}
|
||||
|
||||
Container::Container()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
tuple<string, int, int, double> Container::getItem(int i) const
|
||||
{
|
||||
auto &item = object.containerItems.at(i);
|
||||
return make_tuple(item.refId, item.count, item.charge, item.enchantmentCharge);
|
||||
}
|
||||
|
||||
void Container::setItem(int i, const string &refId, int count, int charge, float enchantmentCharge)
|
||||
{
|
||||
auto &item = object.containerItems.at(i);
|
||||
item.refId = refId;
|
||||
item.count = count;
|
||||
item.charge = charge;
|
||||
item.enchantmentCharge = enchantmentCharge;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
void Container::addItem(const string &refId, int count, int charge, float enchantmentCharge)
|
||||
{
|
||||
mwmp::ContainerItem item;
|
||||
item.refId = refId;
|
||||
item.count = count;
|
||||
item.charge = charge;
|
||||
item.enchantmentCharge = enchantmentCharge;
|
||||
object.containerItems.push_back(item);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
int Container::getActionCount(int i) const
|
||||
{
|
||||
return object.containerItems.at(i).actionCount;
|
||||
}
|
||||
|
||||
size_t Container::size() const
|
||||
{
|
||||
return object.containerItems.size();
|
||||
}
|
||||
|
||||
void ObjectController::Init(LuaState &lua)
|
||||
{
|
||||
sol::table objectCtrl = lua.getState()->create_table("ObjectCtrl");
|
||||
|
||||
objectCtrl.set_function("sendObjects", [&lua](shared_ptr<Player> player, shared_ptr<vector<shared_ptr<Object>>> objects,
|
||||
const std::string &cellDescription) {
|
||||
return lua.getObjectCtrl().sendObjects(player, objects, Utils::getCellFromDescription(cellDescription));
|
||||
});
|
||||
|
||||
objectCtrl.set_function("sendContainers", [&lua](shared_ptr<Player> player, shared_ptr<vector<shared_ptr<Container>>> objects,
|
||||
const std::string &cellDescription) {
|
||||
return lua.getObjectCtrl().sendContainers(player, objects, Utils::getCellFromDescription(cellDescription));
|
||||
});
|
||||
|
||||
objectCtrl.set_function("sendConsoleCommand", [&lua](shared_ptr<Player> player, shared_ptr<vector<shared_ptr<Object>>> objects,
|
||||
const std::string &cellDescription, const std::string &command,
|
||||
bool broadcast) {
|
||||
return lua.getObjectCtrl().sendConsoleCommand(player, objects, Utils::getCellFromDescription(cellDescription),
|
||||
command, broadcast);
|
||||
});
|
||||
objectCtrl.set_function("requestContainers", [&lua](shared_ptr<Player> player) {
|
||||
lua.getObjectCtrl().requestContainers(player);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
shared_ptr<vector<shared_ptr<Object>>> ObjectController::copyObjects(mwmp::BaseObjectList &objectList)
|
||||
{
|
||||
auto objects = make_shared<vector<shared_ptr<Object>>>();
|
||||
|
||||
for (auto &obj : objectList.baseObjects)
|
||||
{
|
||||
auto object = new Object;
|
||||
object->copied = true;
|
||||
object->object = obj;
|
||||
objects->emplace_back(object);
|
||||
}
|
||||
return objects;
|
||||
}
|
||||
|
||||
shared_ptr<vector<shared_ptr<Container>>> ObjectController::copyContainers(mwmp::BaseObjectList &objectList)
|
||||
{
|
||||
auto containers = make_shared<vector<shared_ptr<Container>>>();
|
||||
|
||||
for (auto &obj : objectList.baseObjects)
|
||||
{
|
||||
auto container = new Container;
|
||||
container->copied = true;
|
||||
container->object = obj;
|
||||
containers->emplace_back(container);
|
||||
}
|
||||
return containers;
|
||||
}
|
||||
|
||||
void ObjectController::sendObjects(shared_ptr<Player> player, shared_ptr<vector<shared_ptr<Object>>> objects, const ESM::Cell &cell, bool broadcast)
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
DOOR_STATE = 0,
|
||||
DOOR_DESTINATION,
|
||||
OBJECT_STATE,
|
||||
OBJECT_SCALE,
|
||||
OBJECT_TRAP,
|
||||
OBJECT_LOCK,
|
||||
OBJECT_DELETE,
|
||||
OBJECT_SPAWN,
|
||||
OBJECT_PLACE,
|
||||
LAST
|
||||
};
|
||||
mwmp::BaseObjectList objectLists[Type::LAST];
|
||||
bool changed[Type::LAST];
|
||||
|
||||
for (auto &objectList : objectLists)
|
||||
{
|
||||
objectList.action = mwmp::BaseObjectList::Action::Set;
|
||||
objectList.guid = player->guid;
|
||||
objectList.cell = cell;
|
||||
}
|
||||
|
||||
|
||||
for (auto &object : *objects)
|
||||
{
|
||||
//sendObject(player.get(), object.get());
|
||||
|
||||
bool validNewObjOrCopy = (!object->copied && object->changedBase) || object->copied;
|
||||
|
||||
if (object->changedDoorState && validNewObjOrCopy)
|
||||
{
|
||||
changed[Type::DOOR_STATE] = true;
|
||||
objectLists[Type::DOOR_STATE].baseObjects.push_back(object->object);
|
||||
}
|
||||
if (object->changedDoorDestination && validNewObjOrCopy)
|
||||
{
|
||||
changed[Type::DOOR_DESTINATION] = true;
|
||||
objectLists[Type::DOOR_DESTINATION].baseObjects.push_back(object->object);
|
||||
}
|
||||
if (object->changedObjectState && validNewObjOrCopy)
|
||||
{
|
||||
changed[Type::OBJECT_STATE] = true;
|
||||
objectLists[Type::OBJECT_STATE].baseObjects.push_back(object->object);
|
||||
}
|
||||
if (object->changedObjectScale && validNewObjOrCopy)
|
||||
{
|
||||
changed[Type::OBJECT_SCALE] = true;
|
||||
objectLists[Type::OBJECT_SCALE].baseObjects.push_back(object->object);
|
||||
}
|
||||
if (object->changedObjectTrap && validNewObjOrCopy)
|
||||
{
|
||||
changed[Type::OBJECT_TRAP] = true;
|
||||
objectLists[Type::OBJECT_TRAP].baseObjects.push_back(object->object);
|
||||
}
|
||||
if (object->changedObjectLock && validNewObjOrCopy)
|
||||
{
|
||||
changed[Type::OBJECT_LOCK] = true;
|
||||
objectLists[Type::OBJECT_LOCK].baseObjects.push_back(object->object);
|
||||
}
|
||||
if (object->changedObjectDelete && validNewObjOrCopy)
|
||||
{
|
||||
changed[Type::OBJECT_DELETE] = true;
|
||||
objectLists[Type::OBJECT_DELETE].baseObjects.push_back(object->object);
|
||||
}
|
||||
if (object->changedObjectSpawn && validNewObjOrCopy)
|
||||
{
|
||||
changed[Type::OBJECT_SPAWN] = true;
|
||||
objectLists[Type::OBJECT_SPAWN].baseObjects.push_back(object->object);
|
||||
}
|
||||
if (object->changedObjectPlace && validNewObjOrCopy)
|
||||
{
|
||||
changed[Type::OBJECT_PLACE] = true;
|
||||
objectLists[Type::OBJECT_PLACE].baseObjects.push_back(object->object);
|
||||
}
|
||||
}
|
||||
|
||||
auto worldCtrl = mwmp::Networking::get().getObjectPacketController();
|
||||
|
||||
if (changed[Type::DOOR_STATE])
|
||||
{
|
||||
auto packet = worldCtrl->GetPacket(ID_DOOR_STATE);
|
||||
auto &objectList = objectLists[Type::DOOR_STATE];
|
||||
packet->setObjectList(&objectList);
|
||||
packet->Send(false);
|
||||
|
||||
if (broadcast)
|
||||
packet->Send(true);
|
||||
}
|
||||
if (changed[Type::DOOR_DESTINATION])
|
||||
{
|
||||
auto packet = worldCtrl->GetPacket(ID_DOOR_DESTINATION);
|
||||
auto &objectList = objectLists[Type::DOOR_DESTINATION];
|
||||
packet->setObjectList(&objectList);
|
||||
packet->Send(false);
|
||||
|
||||
if (broadcast)
|
||||
packet->Send(true);
|
||||
}
|
||||
if (changed[Type::OBJECT_STATE])
|
||||
{
|
||||
auto packet = worldCtrl->GetPacket(ID_OBJECT_STATE);
|
||||
auto &objectList = objectLists[Type::OBJECT_STATE];
|
||||
packet->setObjectList(&objectList);
|
||||
packet->Send(false);
|
||||
|
||||
if (broadcast)
|
||||
packet->Send(true);
|
||||
}
|
||||
if (changed[Type::OBJECT_SCALE])
|
||||
{
|
||||
auto packet = worldCtrl->GetPacket(ID_OBJECT_SCALE);
|
||||
auto &objectList = objectLists[Type::OBJECT_SCALE];
|
||||
packet->setObjectList(&objectList);
|
||||
packet->Send(false);
|
||||
|
||||
if (broadcast)
|
||||
packet->Send(true);
|
||||
}
|
||||
if (changed[Type::OBJECT_TRAP])
|
||||
{
|
||||
auto packet = worldCtrl->GetPacket(ID_OBJECT_TRAP);
|
||||
auto &objectList = objectLists[Type::OBJECT_TRAP];
|
||||
packet->setObjectList(&objectList);
|
||||
packet->Send(false);
|
||||
|
||||
if (broadcast)
|
||||
packet->Send(true);
|
||||
}
|
||||
if (changed[Type::OBJECT_LOCK])
|
||||
{
|
||||
auto packet = worldCtrl->GetPacket(ID_OBJECT_LOCK);
|
||||
auto &objectList = objectLists[Type::OBJECT_LOCK];
|
||||
packet->setObjectList(&objectList);
|
||||
packet->Send(false);
|
||||
|
||||
if (broadcast)
|
||||
packet->Send(true);
|
||||
}
|
||||
if (changed[Type::OBJECT_DELETE])
|
||||
{
|
||||
auto packet = worldCtrl->GetPacket(ID_OBJECT_DELETE);
|
||||
auto &objectList = objectLists[Type::OBJECT_DELETE];
|
||||
packet->setObjectList(&objectList);
|
||||
packet->Send(false);
|
||||
|
||||
if (broadcast)
|
||||
packet->Send(true);
|
||||
}
|
||||
if (changed[Type::OBJECT_SCALE])
|
||||
{
|
||||
auto packet = worldCtrl->GetPacket(ID_OBJECT_SPAWN);
|
||||
auto &objectList = objectLists[Type::OBJECT_SCALE];
|
||||
packet->setObjectList(&objectList);
|
||||
packet->Send(false);
|
||||
|
||||
if (broadcast)
|
||||
packet->Send(true);
|
||||
}
|
||||
if (changed[Type::OBJECT_PLACE])
|
||||
{
|
||||
auto packet = worldCtrl->GetPacket(ID_OBJECT_PLACE);
|
||||
auto &objectList = objectLists[Type::OBJECT_PLACE];
|
||||
packet->setObjectList(&objectList);
|
||||
packet->Send(false);
|
||||
|
||||
if (broadcast)
|
||||
packet->Send(true);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectController::sendConsoleCommand(shared_ptr<Player> player, shared_ptr<vector<shared_ptr<Object>>> objects,
|
||||
const ESM::Cell &cell, const std::string &consoleCommand, bool broadcast)
|
||||
{
|
||||
|
||||
mwmp::BaseObjectList objectList;
|
||||
objectList.cell = cell;
|
||||
objectList.consoleCommand = consoleCommand;
|
||||
objectList.guid = player->guid;
|
||||
|
||||
for (auto &object : *objects)
|
||||
objectList.baseObjects.push_back(object->object);
|
||||
|
||||
auto packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_CONSOLE_COMMAND);
|
||||
packet->setObjectList(&objectList);
|
||||
packet->Send(false);
|
||||
|
||||
if (broadcast)
|
||||
packet->Send(true);
|
||||
}
|
||||
|
||||
void ObjectController::sendContainers(shared_ptr<Player> player, shared_ptr<vector<shared_ptr<Container>>> objects,
|
||||
const ESM::Cell &cell, bool broadcast)
|
||||
{
|
||||
|
||||
mwmp::BaseObjectList objectList;
|
||||
objectList.cell = cell;
|
||||
objectList.action = mwmp::BaseObjectList::Action::Set;
|
||||
objectList.guid = player->guid;
|
||||
|
||||
for (auto &object : *objects)
|
||||
{
|
||||
bool validNewObjOrCopy = (!object->copied && object->changedBase) || object->copied;
|
||||
if (object->changed && validNewObjOrCopy)
|
||||
objectList.baseObjects.push_back(object->object);
|
||||
}
|
||||
|
||||
auto packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_CONTAINER);
|
||||
packet->setObjectList(&objectList);
|
||||
packet->Send(false);
|
||||
|
||||
if (broadcast)
|
||||
packet->Send(true);
|
||||
}
|
||||
|
||||
void ObjectController::requestContainers(shared_ptr<Player> player)
|
||||
{
|
||||
mwmp::BaseObjectList objectList;
|
||||
objectList.action = mwmp::BaseObjectList::Action::Request;
|
||||
objectList.guid = player->guid;
|
||||
objectList.cell = player->cell;
|
||||
|
||||
auto packet = mwmp::Networking::get().getObjectPacketController()->GetPacket(ID_CONTAINER);
|
||||
packet->setObjectList(&objectList);
|
||||
packet->Send(objectList.guid);
|
||||
}
|
130
apps/openmw-mp/Object.hpp
Normal file
130
apps/openmw-mp/Object.hpp
Normal file
|
@ -0,0 +1,130 @@
|
|||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
#include <components/openmw-mp/Base/BaseObject.hpp>
|
||||
#include <memory>
|
||||
|
||||
class LuaState;
|
||||
class Player;
|
||||
|
||||
class BaseObject
|
||||
{
|
||||
public:
|
||||
BaseObject();
|
||||
std::string getRefId() const;
|
||||
void setRefId(const std::string &refId);
|
||||
|
||||
unsigned getRefNum() const;
|
||||
void setRefNum(unsigned refNum);
|
||||
|
||||
unsigned getMpNum() const;
|
||||
void setMpNum(unsigned mpNum);
|
||||
|
||||
RakNet::RakNetGUID getGuid() const;
|
||||
void setGuid(const RakNet::RakNetGUID &guid);
|
||||
|
||||
//void setEventCell(const std::string &cellDescription);
|
||||
|
||||
|
||||
mwmp::BaseObject object;
|
||||
bool changedBase;
|
||||
bool copied;
|
||||
};
|
||||
|
||||
class Object : public BaseObject
|
||||
{
|
||||
public:
|
||||
static void Init(LuaState &lua);
|
||||
public:
|
||||
Object();
|
||||
~Object();
|
||||
|
||||
void update();
|
||||
|
||||
/**
|
||||
*
|
||||
* @return x, y, z
|
||||
*/
|
||||
|
||||
std::tuple<float, float, float> getPosition() const;
|
||||
void setPosition(float x, float y, float z);
|
||||
|
||||
/**
|
||||
*
|
||||
* @return x, y, z
|
||||
*/
|
||||
std::tuple<float, float, float> getRotation() const;
|
||||
void setRotation(float x, float y, float z);
|
||||
|
||||
int getCount() const;
|
||||
void setCount(int count);
|
||||
|
||||
int getCharge() const;
|
||||
void setCharge(int charge);
|
||||
|
||||
float getEnchantmentCharge() const;
|
||||
void setEnchantmentCharge(float enchantmentCharge);
|
||||
|
||||
int getGoldValue() const;
|
||||
void setGoldValue(int gold);
|
||||
|
||||
float getScale() const;
|
||||
void setScale(float scale);
|
||||
|
||||
bool getState() const;
|
||||
void setState(bool state);
|
||||
|
||||
int getDoorState() const;
|
||||
void setDoorState(int state);
|
||||
|
||||
int getLockLevel() const;
|
||||
void setLockLevel(int locklevel);
|
||||
|
||||
bool hasContainer() const;
|
||||
|
||||
void setTeleportState(bool state);
|
||||
void setDoorDestination(const std::string &cellDescription, float posX, float posY, float posZ, float rotX, float rotY, float rotZ);
|
||||
|
||||
void setDisarmState(bool state);
|
||||
void setMasterState(bool state);
|
||||
|
||||
bool changedDoorState, changedDoorDestination, changedObjectState, changedObjectScale, changedObjectTrap, changedObjectLock,
|
||||
changedObjectDelete, changedObjectSpawn, changedObjectPlace;
|
||||
};
|
||||
|
||||
class Container : public BaseObject
|
||||
{
|
||||
public:
|
||||
static void Init(LuaState &lua);
|
||||
public:
|
||||
Container();
|
||||
|
||||
std::tuple<std::string, int, int, double> getItem(int i) const;
|
||||
void addItem(const std::string &refId, int count, int charge, float enchantmentCharge);
|
||||
|
||||
void setItem(int i, const std::string &refId, int count, int charge, float enchantmentCharge);
|
||||
int getActionCount(int i) const;
|
||||
|
||||
size_t size() const;
|
||||
bool changed;
|
||||
};
|
||||
|
||||
|
||||
class ObjectController
|
||||
{
|
||||
public:
|
||||
static void Init(LuaState &lua);
|
||||
public:
|
||||
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Object>>> copyObjects(mwmp::BaseObjectList &objectList);
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Container>>> copyContainers(mwmp::BaseObjectList &objectList);
|
||||
|
||||
void sendObjects(std::shared_ptr<Player> player, std::shared_ptr<std::vector<std::shared_ptr<Object>>> objects,
|
||||
const ESM::Cell &cell, bool broadcast = false);
|
||||
void sendConsoleCommand(std::shared_ptr<Player> player, std::shared_ptr<std::vector<std::shared_ptr<Object>>> objects,
|
||||
const ESM::Cell &cell, const std::string &consoleCommand, bool broadcast = false);
|
||||
void sendContainers(std::shared_ptr<Player> player, std::shared_ptr<std::vector<std::shared_ptr<Container>>> objects,
|
||||
const ESM::Cell &cell, bool broadcast = false);
|
||||
|
||||
void requestContainers(std::shared_ptr<Player> player);
|
||||
};
|
File diff suppressed because it is too large
Load diff
|
@ -8,6 +8,7 @@
|
|||
#include <map>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <RakNetTypes.h>
|
||||
|
||||
#include <components/esm/npcstats.hpp>
|
||||
|
@ -18,32 +19,31 @@
|
|||
#include <components/openmw-mp/Log.hpp>
|
||||
#include <components/openmw-mp/Base/BasePlayer.hpp>
|
||||
#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>
|
||||
#include <apps/openmw-mp/Script/LuaState.hpp>
|
||||
#include "Cell.hpp"
|
||||
#include "CellController.hpp"
|
||||
#include "CharClass.hpp"
|
||||
#include "Inventory.hpp"
|
||||
#include "Settings.hpp"
|
||||
#include "Books.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "Dialogue.hpp"
|
||||
#include "Factions.hpp"
|
||||
#include "Cells.hpp"
|
||||
#include "Quests.hpp"
|
||||
#include "Spells.hpp"
|
||||
#include "NetActor.hpp"
|
||||
#include "CellState.hpp"
|
||||
#include "Weather.hpp"
|
||||
|
||||
struct Player;
|
||||
typedef std::map<RakNet::RakNetGUID, Player*> TPlayers;
|
||||
typedef std::map<unsigned short, Player*> TSlots;
|
||||
|
||||
class Players
|
||||
{
|
||||
public:
|
||||
static void newPlayer(RakNet::RakNetGUID guid);
|
||||
static void deletePlayer(RakNet::RakNetGUID guid);
|
||||
static Player *getPlayer(RakNet::RakNetGUID guid);
|
||||
static Player *getPlayer(unsigned short id);
|
||||
static TPlayers *getPlayers();
|
||||
static unsigned short getLastPlayerId();
|
||||
|
||||
private:
|
||||
static TPlayers players;
|
||||
static TSlots slots;
|
||||
};
|
||||
|
||||
class Player : public mwmp::BasePlayer
|
||||
class Player : public mwmp::BasePlayer, public NetActor
|
||||
{
|
||||
friend class Cell;
|
||||
friend class Players;
|
||||
unsigned short id;
|
||||
uint64_t getGUID() const {return guid.g;}
|
||||
public:
|
||||
static void Init(LuaState &lua);
|
||||
public:
|
||||
|
||||
enum
|
||||
|
@ -52,7 +52,7 @@ public:
|
|||
LOADED,
|
||||
POSTLOADED
|
||||
};
|
||||
Player(RakNet::RakNetGUID guid);
|
||||
explicit Player(RakNet::RakNetGUID guid);
|
||||
|
||||
unsigned short getId();
|
||||
void setId(unsigned short id);
|
||||
|
@ -68,15 +68,163 @@ public:
|
|||
virtual ~Player();
|
||||
|
||||
CellController::TContainer *getCells();
|
||||
void sendToLoaded(mwmp::PlayerPacket *myPacket);
|
||||
void sendToLoaded(mwmp::PlayerPacket &myPacket);
|
||||
|
||||
void forEachLoaded(std::function<void(Player *pl, Player *other)> func);
|
||||
|
||||
void update();
|
||||
|
||||
public:
|
||||
void kick() const;
|
||||
void ban() const;
|
||||
|
||||
void cleanChannel(unsigned channelId);
|
||||
void message(unsigned channelId, const std::string &message, bool toAll = false);
|
||||
bool joinChannel(unsigned channelId, const std::string &name);
|
||||
void renameChannel(unsigned channelId, const std::string &name);
|
||||
void closeChannel(unsigned channelId);
|
||||
void leaveChannel(unsigned channelId);
|
||||
void setChannel(unsigned channelId);
|
||||
bool isChannelOpen(unsigned channelId);
|
||||
|
||||
int getAvgPing();
|
||||
|
||||
std::string getName();
|
||||
void setName(const std::string &name);
|
||||
void setCharGenStages(int currentStage, int endStage);
|
||||
int getGender() const;
|
||||
void setGender(int gender);
|
||||
std::string getRace() const;
|
||||
void setRace(const std::string &race);
|
||||
std::string getHead() const;
|
||||
void setHead(const std::string &head);
|
||||
std::string getHair() const;
|
||||
void setHair(const std::string &hair);
|
||||
std::string getBirthsign() const;
|
||||
void setBirthsign(const std::string &sign);
|
||||
void setResetStats(bool state);
|
||||
|
||||
int getBounty() const;
|
||||
void setBounty(int bounty);
|
||||
int getReputation() const;
|
||||
void setReputation(int reputation, bool toOthers);
|
||||
|
||||
int getLevel() const;
|
||||
void setLevel(int level);
|
||||
int getLevelProgress() const;
|
||||
void setLevelProgress(int progress);
|
||||
|
||||
/**
|
||||
* \brief Send a PlayerResurrect packet about a player.
|
||||
*
|
||||
* This sends the packet to all players connected to the server.
|
||||
*
|
||||
* \param type The type of resurrection (0 for REGULAR, 1 for IMPERIAL_SHRINE, 2 for TRIBUNAL_TEMPLE).
|
||||
*/
|
||||
void resurrect(unsigned int type);
|
||||
|
||||
/**
|
||||
* \brief Send a PlayerJail packet about a player.
|
||||
*
|
||||
* This is similar to the player being jailed by a guard, but provides extra parameters for
|
||||
* increased flexibility.
|
||||
*
|
||||
* It is only sent to the player being jailed, as the other players will be informed of the
|
||||
* jailing's actual consequences via other packets sent by the affected client.
|
||||
*
|
||||
* \param jailDays The number of days to spend jailed, where each day affects one skill point.
|
||||
* \param ignoreJailTeleportation Whether the player being teleported to the nearest jail
|
||||
* marker should be overridden.
|
||||
* \param ignoreJailSkillIncrease Whether the player's Sneak and Security skills should be
|
||||
* prevented from increasing as a result of the jailing,
|
||||
* overriding default behavior.
|
||||
* \param jailProgressText The text that should be displayed while jailed.
|
||||
* \param jailEndText The text that should be displayed once the jailing period is over.
|
||||
*/
|
||||
void jail(int jailDays, bool ignoreJailTeleportation, bool ignoreJailSkillIncreases,
|
||||
const std::string &jailProgressText, const std::string &jailEndText);
|
||||
|
||||
bool getWerewolfState() const;
|
||||
void setWerewolfState(bool state);
|
||||
|
||||
float getScale() const;
|
||||
void setScale(float newScale);
|
||||
|
||||
std::string getCreatureRefId() const;
|
||||
void setCreatureRefId(const std::string &model);
|
||||
bool getCreatureNameDisplayState() const;
|
||||
void setCreatureNameDisplayState(bool useName);
|
||||
|
||||
std::string getIP() const;
|
||||
|
||||
/**
|
||||
*
|
||||
* @return x, y, z
|
||||
*/
|
||||
std::tuple<float, float, float> getPreviousCellPos() const;
|
||||
|
||||
/**
|
||||
*
|
||||
* @return base, current
|
||||
*/
|
||||
std::tuple<int, int> getAttribute(unsigned short id) const;
|
||||
void setAttribute(unsigned short id, int base, bool clearModifier);
|
||||
|
||||
/**
|
||||
*
|
||||
* @return base, current, progress, increase
|
||||
*/
|
||||
std::tuple<int, int, float> getSkill(unsigned short id) const;
|
||||
void setSkill(unsigned short id, int base, bool clearModifier, float progress);
|
||||
|
||||
int getSkillIncrease(unsigned short attributeId) const;
|
||||
void setSkillIncrease(unsigned short attributeId, int increase);
|
||||
|
||||
void setMark(float x, float y, float z, float xRot, float zRot, const std::string &cellDescription);
|
||||
std::tuple<float, float, float, float, float, std::string> getMark();
|
||||
|
||||
std::string getSelectedSpell();
|
||||
void setSelectedSpell(const std::string &newSelectedSpellId);
|
||||
|
||||
size_t cellStateSize() const;
|
||||
CellState getCellState(int i);
|
||||
|
||||
CharClass &getCharClass(sol::this_state thisState);
|
||||
GameSettings &getSettings();
|
||||
Books &getBooks();
|
||||
GUI &getGUI();
|
||||
Dialogue &getDialogue();
|
||||
Factions &getFactions();
|
||||
Quests &getQuests();
|
||||
Spells &getSpells();
|
||||
QuickKeys &getQuickKeys();
|
||||
WeatherMgr &getWeatherMgr();
|
||||
|
||||
void setAuthority();
|
||||
|
||||
bool isMarkedForDeletion() const;
|
||||
|
||||
void addToUpdateQueue();
|
||||
|
||||
private:
|
||||
CellController::TContainer cells;
|
||||
int loadState;
|
||||
int handshakeCounter;
|
||||
|
||||
bool /*statsChanged, attributesChanged, skillsChanged, baseInfoChanged, positionChanged,*/ changedMarkLocation, changedSelectedSpell;
|
||||
CharClass cClass;
|
||||
GameSettings settings;
|
||||
Books books;
|
||||
GUI gui;
|
||||
Dialogue dialogue;
|
||||
Factions factions;
|
||||
Quests quests;
|
||||
Spells spells;
|
||||
QuickKeys quickKeys;
|
||||
WeatherMgr weatherMgr;
|
||||
sol::table storedData;
|
||||
sol::table customData;
|
||||
bool markedForDeletion;
|
||||
bool inUpdateQueue;
|
||||
};
|
||||
|
||||
#endif //OPENMW_PLAYER_HPP
|
||||
|
|
148
apps/openmw-mp/Players.cpp
Normal file
148
apps/openmw-mp/Players.cpp
Normal file
|
@ -0,0 +1,148 @@
|
|||
//
|
||||
// Created by koncord on 12.08.17.
|
||||
//
|
||||
|
||||
#include "Players.hpp"
|
||||
#include "Networking.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
Players::Store Players::store;
|
||||
std::queue<Player*> Players::updateQueue;
|
||||
|
||||
void Players::Init(LuaState &lua)
|
||||
{
|
||||
sol::table playersTable = lua.getState()->create_named_table("Players");
|
||||
playersTable.set_function("getByPID", [](int pid) { return Players::getPlayerByPID(pid).get(); });
|
||||
playersTable.set_function("getByGUID", [](RakNet::RakNetGUID guid) { return Players::getPlayerByGUID(guid).get(); });
|
||||
playersTable.set_function("for_each", [](sol::function func)
|
||||
{
|
||||
for (shared_ptr<Player> player : store)
|
||||
func(player);
|
||||
});
|
||||
|
||||
playersTable.set_function("size", &Players::size);
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<Player> Players::getPlayerByPID(int pid)
|
||||
{
|
||||
const auto &ls = store.get<ByID>();
|
||||
auto it = ls.find(pid);
|
||||
if (it != ls.end())
|
||||
return *it;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<Player> Players::getPlayerByGUID(RakNet::RakNetGUID guid)
|
||||
{
|
||||
const auto &ls = store.get<ByGUID>();
|
||||
auto it = ls.find(guid.g);
|
||||
if (it != ls.end())
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_TRACE, "%d references: %d", guid.g, it->use_count());
|
||||
return *it;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void deleter(Player *pl)
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Player %lu deleted", pl->guid.g);
|
||||
delete pl;
|
||||
}
|
||||
|
||||
std::shared_ptr<Player> Players::addPlayer(RakNet::RakNetGUID guid)
|
||||
{
|
||||
const int maxConnections = 65535;
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Creating new player with guid %lu", guid.g);
|
||||
|
||||
auto player = shared_ptr<Player>(new Player(guid), deleter);
|
||||
|
||||
unsigned short findPid = 0;
|
||||
const auto &ls = store.get<ByID>();
|
||||
for (; findPid < maxConnections; ++findPid) // find empty slot
|
||||
{
|
||||
auto it = ls.find(findPid);
|
||||
if (it == ls.end())
|
||||
break;
|
||||
}
|
||||
|
||||
if (findPid >= maxConnections)
|
||||
return nullptr;
|
||||
|
||||
LOG_APPEND(Log::LOG_INFO, "- Storing in slot %i", findPid);
|
||||
player->id = findPid;
|
||||
player->guid = guid;
|
||||
store.push_back(player);
|
||||
return player;
|
||||
}
|
||||
|
||||
void Players::deletePlayerByPID(int pid)
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Marking player (pid %i) for deletion", pid);
|
||||
auto &ls = store.get<ByID>();
|
||||
auto it = ls.find(pid);
|
||||
if (it != ls.end())
|
||||
{
|
||||
(*it)->markedForDeletion = true;
|
||||
mwmp::Networking::get().getState().getState()->collect_garbage();
|
||||
size_t useCount = it->use_count();
|
||||
ls.erase(it);
|
||||
LOG_APPEND(Log::LOG_TRACE, "- references: %d", useCount - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void Players::deletePlayerByGUID(RakNet::RakNetGUID guid)
|
||||
{
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Marking player (guid %lu) for deletion", guid.g);
|
||||
auto &ls = store.get<ByGUID>();
|
||||
auto it = ls.find(guid.g);
|
||||
if (it != ls.end())
|
||||
{
|
||||
(*it)->markedForDeletion = true;
|
||||
mwmp::Networking::get().getState().getState()->collect_garbage();
|
||||
size_t useCount = it->use_count();
|
||||
ls.erase(it);
|
||||
LOG_APPEND(Log::LOG_TRACE, "- references: %d", useCount - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void Players::for_each(std::function<void (Player *)> func)
|
||||
{
|
||||
for (auto &player : store)
|
||||
func(player.get());
|
||||
}
|
||||
|
||||
Players::Store::const_iterator Players::begin()
|
||||
{
|
||||
return store.cbegin();
|
||||
}
|
||||
|
||||
Players::Store::const_iterator Players::end()
|
||||
{
|
||||
return store.cend();
|
||||
}
|
||||
|
||||
size_t Players::size()
|
||||
{
|
||||
return store.size();
|
||||
}
|
||||
|
||||
void Players::processUpdated()
|
||||
{
|
||||
while (!updateQueue.empty())
|
||||
{
|
||||
Player *player = updateQueue.front();
|
||||
updateQueue.pop();
|
||||
player->update();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Players::addToQueue(Player *player)
|
||||
{
|
||||
updateQueue.push(player);
|
||||
}
|
59
apps/openmw-mp/Players.hpp
Normal file
59
apps/openmw-mp/Players.hpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
//
|
||||
// Created by koncord on 12.08.17.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/ordered_index.hpp>
|
||||
#include <boost/multi_index/identity.hpp>
|
||||
#include <boost/multi_index/member.hpp>
|
||||
#include <boost/multi_index/global_fun.hpp>
|
||||
#include <boost/multi_index/mem_fun.hpp>
|
||||
#include <boost/multi_index/random_access_index.hpp>
|
||||
#include <queue>
|
||||
#include "Player.hpp"
|
||||
|
||||
class LuaState;
|
||||
|
||||
|
||||
class Players
|
||||
{
|
||||
friend class Player;
|
||||
public:
|
||||
static void Init(LuaState &lua);
|
||||
public:
|
||||
Players() = delete; // static class
|
||||
protected:
|
||||
|
||||
struct ByID {};
|
||||
struct ByGUID {};
|
||||
|
||||
public:
|
||||
typedef boost::multi_index_container<std::shared_ptr<Player>,
|
||||
boost::multi_index::indexed_by<
|
||||
boost::multi_index::random_access<>,
|
||||
boost::multi_index::ordered_unique<boost::multi_index::tag<ByGUID>, BOOST_MULTI_INDEX_CONST_MEM_FUN(Player, uint64_t, getGUID)>,
|
||||
boost::multi_index::ordered_unique<boost::multi_index::tag<ByID>, boost::multi_index::member<Player, unsigned short, &Player::id> >
|
||||
> > Store;
|
||||
|
||||
|
||||
static std::shared_ptr<Player> getPlayerByPID(int pid);
|
||||
static std::shared_ptr<Player> getPlayerByGUID(RakNet::RakNetGUID guid);
|
||||
static std::shared_ptr<Player> addPlayer(RakNet::RakNetGUID guid);
|
||||
static void deletePlayerByPID(int pid);
|
||||
static void deletePlayerByGUID(RakNet::RakNetGUID guid);
|
||||
static Store::const_iterator begin();
|
||||
static Store::const_iterator end();
|
||||
static size_t size();
|
||||
|
||||
static void for_each(std::function<void(Player *)> func);
|
||||
|
||||
static void processUpdated();
|
||||
private:
|
||||
static void addToQueue(Player *player);
|
||||
|
||||
static Store store;
|
||||
static std::queue<Player*> updateQueue;
|
||||
};
|
||||
|
154
apps/openmw-mp/Quests.cpp
Normal file
154
apps/openmw-mp/Quests.cpp
Normal file
|
@ -0,0 +1,154 @@
|
|||
//
|
||||
// Created by koncord on 25.08.17.
|
||||
//
|
||||
|
||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
|
||||
#include "Script/LuaState.hpp"
|
||||
#include "Networking.hpp"
|
||||
|
||||
#include "Quests.hpp"
|
||||
#include "Player.hpp"
|
||||
|
||||
void JournalItem::Init(LuaState &lua)
|
||||
{
|
||||
lua.getState()->new_usertype<JournalItem>("JournalItem",
|
||||
"quest", sol::property(&JournalItem::getQuest, &JournalItem::setQuest),
|
||||
"index", sol::property(&JournalItem::getIndex, &JournalItem::setIndex),
|
||||
"actorRefId", sol::property(&JournalItem::getActorRefId, &JournalItem::setActorRefId),
|
||||
"type", sol::property(&JournalItem::getType, &JournalItem::setType)
|
||||
);
|
||||
}
|
||||
|
||||
JournalItem::JournalItem(mwmp::JournalItem item) : item(item)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
JournalItem::~JournalItem()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::string JournalItem::getQuest() const
|
||||
{
|
||||
return item.quest;
|
||||
}
|
||||
|
||||
void JournalItem::setQuest(const std::string &quest)
|
||||
{
|
||||
item.quest = quest;
|
||||
}
|
||||
|
||||
int JournalItem::getIndex() const
|
||||
{
|
||||
return item.index;
|
||||
}
|
||||
|
||||
void JournalItem::setIndex(int index)
|
||||
{
|
||||
item.index = index;
|
||||
}
|
||||
|
||||
mwmp::JournalItem::Type JournalItem::getType() const
|
||||
{
|
||||
return item.type;
|
||||
}
|
||||
|
||||
void JournalItem::setType(mwmp::JournalItem::Type type)
|
||||
{
|
||||
item.type = type;
|
||||
}
|
||||
|
||||
std::string JournalItem::getActorRefId() const
|
||||
{
|
||||
return item.actorRefId;
|
||||
}
|
||||
|
||||
void JournalItem::setActorRefId(const std::string &refid)
|
||||
{
|
||||
item.actorRefId = refid;
|
||||
}
|
||||
|
||||
void Quests::Init(LuaState &lua)
|
||||
{
|
||||
lua.getState()->new_usertype<Quests>("Quests",
|
||||
"getJournalChangesSize", &Quests::getJournalChangesSize,
|
||||
"getKillChangesSize", &Quests::getKillChangesSize,
|
||||
|
||||
"addJournalItem", &Quests::addJournalItem,
|
||||
"setJournalItem", &Quests::setJournalItem,
|
||||
"getJournalItem", &Quests::getJournalItem,
|
||||
|
||||
"addKill", &Quests::addKill,
|
||||
"getKill", &Quests::getKill
|
||||
);
|
||||
}
|
||||
|
||||
Quests::Quests(Player *player) : BaseMgr(player), changedKills(false), changedJournal(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Quests::processUpdate()
|
||||
{
|
||||
if (changedJournal)
|
||||
{
|
||||
changedJournal = false;
|
||||
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_JOURNAL);
|
||||
|
||||
packet->setPlayer(player);
|
||||
packet->Send(/*toOthers*/ false);
|
||||
}
|
||||
|
||||
if (changedKills)
|
||||
{
|
||||
changedKills = false;
|
||||
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_KILL_COUNT);
|
||||
|
||||
packet->setPlayer(player);
|
||||
packet->Send(/*toOthers*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
size_t Quests::getJournalChangesSize() const
|
||||
{
|
||||
return player->journalChanges.journalItems.size();
|
||||
}
|
||||
|
||||
size_t Quests::getKillChangesSize() const
|
||||
{
|
||||
return player->killChanges.kills.size();
|
||||
}
|
||||
|
||||
void Quests::addJournalItem(JournalItem item)
|
||||
{
|
||||
player->journalChanges.journalItems.push_back(item.item);
|
||||
changedJournal = true;
|
||||
setChanged();
|
||||
}
|
||||
|
||||
void Quests::setJournalItem(unsigned int id, JournalItem item)
|
||||
{
|
||||
player->journalChanges.journalItems.at(id) = item.item;
|
||||
changedJournal = true;
|
||||
setChanged();
|
||||
}
|
||||
|
||||
JournalItem Quests::getJournalItem(unsigned int id)
|
||||
{
|
||||
return JournalItem(player->journalChanges.journalItems.at(id));
|
||||
}
|
||||
|
||||
void Quests::addKill(const std::string &refId, int number)
|
||||
{
|
||||
player->killChanges.kills.push_back({refId, number});
|
||||
changedKills = true;
|
||||
setChanged();
|
||||
}
|
||||
|
||||
std::tuple<std::string, int> Quests::getKill(unsigned int i) const
|
||||
{
|
||||
auto & kill = player->killChanges.kills.at(i);
|
||||
return std::make_tuple(kill.refId, kill.number);
|
||||
}
|
61
apps/openmw-mp/Quests.hpp
Normal file
61
apps/openmw-mp/Quests.hpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// Created by koncord on 25.08.17.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <components/openmw-mp/Base/BasePlayer.hpp>
|
||||
#include "BaseMgr.hpp"
|
||||
|
||||
class LuaState;
|
||||
class Player;
|
||||
|
||||
class JournalItem
|
||||
{
|
||||
public:
|
||||
static void Init(LuaState &lua);
|
||||
public:
|
||||
explicit JournalItem(mwmp::JournalItem item);
|
||||
~JournalItem();
|
||||
|
||||
std::string getQuest() const;
|
||||
void setQuest(const std::string &quest);
|
||||
|
||||
int getIndex() const;
|
||||
void setIndex(int index);
|
||||
|
||||
mwmp::JournalItem::Type getType() const;
|
||||
void setType(mwmp::JournalItem::Type type);
|
||||
|
||||
std::string getActorRefId() const;
|
||||
void setActorRefId(const std::string &refid);
|
||||
|
||||
|
||||
mwmp::JournalItem item;
|
||||
};
|
||||
|
||||
class Quests final: public BaseMgr
|
||||
{
|
||||
public:
|
||||
static void Init(LuaState &lua);
|
||||
public:
|
||||
explicit Quests(Player *player);
|
||||
|
||||
|
||||
size_t getJournalChangesSize() const;
|
||||
size_t getKillChangesSize() const;
|
||||
|
||||
void addJournalItem(JournalItem item);
|
||||
void setJournalItem(unsigned int id, JournalItem item);
|
||||
JournalItem getJournalItem(unsigned int id);
|
||||
|
||||
void addKill(const std::string &refId, int number);
|
||||
std::tuple<std::string, int> getKill(unsigned int i) const;
|
||||
|
||||
private:
|
||||
void processUpdate() final;
|
||||
bool changedKills, changedJournal;
|
||||
};
|
||||
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
//
|
||||
// Created by koncord on 14.05.16.
|
||||
//
|
||||
|
||||
#include <Script/ScriptFunction.hpp>
|
||||
#include "PublicFnAPI.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
unordered_map<string, Public *> Public::publics;
|
||||
|
||||
Public::~Public()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Public::Public(ScriptFunc _public, const std::string &name, char ret_type, const std::string &def) : ScriptFunction(_public, ret_type, def)
|
||||
{
|
||||
publics.emplace(name, this);
|
||||
}
|
||||
|
||||
Public::Public(ScriptFuncLua _public, lua_State *lua, const std::string &name, char ret_type, const std::string &def) : ScriptFunction(
|
||||
_public, lua, ret_type, def)
|
||||
{
|
||||
publics.emplace(name, this);
|
||||
}
|
||||
|
||||
#if defined(ENABLE_PAWN)
|
||||
Public::Public(ScriptFuncPAWN _public, AMX* amx, const std::string& name, char ret_type, const std::string& def): ScriptFunction(_public, amx, ret_type, def)
|
||||
{
|
||||
publics.emplace(name, this);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
boost::any Public::Call(const std::string &name, const std::vector<boost::any> &args)
|
||||
{
|
||||
auto it = publics.find(name);
|
||||
if (it == publics.end())
|
||||
throw runtime_error("Public with name \"" + name + "\" does not exist");
|
||||
|
||||
return it->second->ScriptFunction::Call(args);
|
||||
}
|
||||
|
||||
|
||||
const std::string &Public::GetDefinition(const std::string &name)
|
||||
{
|
||||
auto it = publics.find(name);
|
||||
|
||||
if (it == publics.end())
|
||||
throw runtime_error("Public with name \"" + name + "\" does not exist");
|
||||
|
||||
return it->second->def;
|
||||
}
|
||||
|
||||
|
||||
bool Public::IsLua(const std::string &name)
|
||||
{
|
||||
#if !defined(ENABLE_LUA)
|
||||
return false;
|
||||
#else
|
||||
auto it = publics.find(name);
|
||||
if (it == publics.end())
|
||||
throw runtime_error("Public with name \"" + name + "\" does not exist");
|
||||
|
||||
return it->second->script_type == SCRIPT_LUA;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Public::IsPAWN(const std::string &name)
|
||||
{
|
||||
#if !defined(ENABLE_PAWN)
|
||||
return false;
|
||||
#else
|
||||
auto it = publics.find(name);
|
||||
|
||||
if (it == publics.end())
|
||||
throw runtime_error("Public with name \"" + name + "\" does not exist");
|
||||
|
||||
return it->second->script_type == SCRIPT_PAWN;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void Public::DeleteAll()
|
||||
{
|
||||
for (auto it = publics.begin(); it != publics.end(); it++)
|
||||
{
|
||||
Public *_public = it->second;
|
||||
delete _public;
|
||||
publics.erase(it);
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
//
|
||||
// Created by koncord on 14.05.16.
|
||||
//
|
||||
|
||||
#ifndef PLUGINSYSTEM3_PUBLICFNAPI_HPP
|
||||
#define PLUGINSYSTEM3_PUBLICFNAPI_HPP
|
||||
|
||||
#include <unordered_map>
|
||||
#include <Script/ScriptFunction.hpp>
|
||||
|
||||
|
||||
class Public : public ScriptFunction
|
||||
{
|
||||
private:
|
||||
~Public();
|
||||
|
||||
static std::unordered_map<std::string, Public *> publics;
|
||||
|
||||
Public(ScriptFunc _public, const std::string &name, char ret_type, const std::string &def);
|
||||
#if defined(ENABLE_PAWN)
|
||||
Public(ScriptFuncPAWN _public, AMX* amx, const std::string& name, char ret_type, const std::string& def);
|
||||
#endif
|
||||
#if defined(ENABLE_LUA)
|
||||
Public(ScriptFuncLua _public, lua_State *lua, const std::string &name, char ret_type, const std::string &def);
|
||||
#endif
|
||||
|
||||
public:
|
||||
template<typename... Args>
|
||||
static void MakePublic(Args &&... args)
|
||||
{ new Public(std::forward<Args>(args)...); }
|
||||
|
||||
static boost::any Call(const std::string &name, const std::vector<boost::any> &args);
|
||||
|
||||
static const std::string& GetDefinition(const std::string& name);
|
||||
|
||||
static bool IsPAWN(const std::string &name);
|
||||
static bool IsLua(const std::string &name);
|
||||
|
||||
static void DeleteAll();
|
||||
};
|
||||
|
||||
#endif //PLUGINSYSTEM3_PUBLICFNAPI_HPP
|
|
@ -1,238 +0,0 @@
|
|||
//
|
||||
// Created by koncord on 15.03.16.
|
||||
//
|
||||
|
||||
#include "TimerAPI.hpp"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <iostream>
|
||||
using namespace mwmp;
|
||||
using namespace std;
|
||||
|
||||
Timer::Timer(ScriptFunc callback, long msec, const std::string& def, std::vector<boost::any> args) : ScriptFunction(callback, 'v', def)
|
||||
{
|
||||
targetMsec = msec;
|
||||
this->args = args;
|
||||
end = true;
|
||||
}
|
||||
|
||||
#if defined(ENABLE_PAWN)
|
||||
Timer::Timer(AMX *amx, ScriptFuncPAWN callback, long msec, const std::string &def, std::vector<boost::any> args): ScriptFunction(callback, amx, 'v', def)
|
||||
{
|
||||
targetMsec = msec;
|
||||
this->args = args;
|
||||
end = true;
|
||||
}
|
||||
#endif
|
||||
#if defined(ENABLE_LUA)
|
||||
Timer::Timer(lua_State *lua, ScriptFuncLua callback, long msec, const std::string& def, std::vector<boost::any> args): ScriptFunction(callback, lua, 'v', def)
|
||||
{
|
||||
targetMsec = msec;
|
||||
this->args = args;
|
||||
end = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Timer::Tick()
|
||||
{
|
||||
if (end)
|
||||
return;
|
||||
|
||||
const auto duration = chrono::system_clock::now().time_since_epoch();
|
||||
const auto time = chrono::duration_cast<chrono::milliseconds>(duration).count();
|
||||
|
||||
if (time - startTime >= targetMsec)
|
||||
{
|
||||
end = true;
|
||||
Call(args);
|
||||
}
|
||||
}
|
||||
|
||||
bool Timer::IsEnd()
|
||||
{
|
||||
return end;
|
||||
}
|
||||
|
||||
void Timer::Stop()
|
||||
{
|
||||
end = true;
|
||||
}
|
||||
|
||||
void Timer::Restart(int msec)
|
||||
{
|
||||
targetMsec = msec;
|
||||
Start();
|
||||
}
|
||||
|
||||
void Timer::Start()
|
||||
{
|
||||
end = false;
|
||||
|
||||
const auto duration = chrono::system_clock::now().time_since_epoch();
|
||||
const auto msec = chrono::duration_cast<chrono::milliseconds>(duration).count();
|
||||
startTime = msec;
|
||||
}
|
||||
|
||||
int TimerAPI::pointer = 0;
|
||||
std::unordered_map<int, Timer* > TimerAPI::timers;
|
||||
|
||||
#if defined(ENABLE_PAWN)
|
||||
int TimerAPI::CreateTimerPAWN(AMX *amx, ScriptFuncPAWN callback, long msec, const string& def, std::vector<boost::any> args)
|
||||
{
|
||||
int id = -1;
|
||||
|
||||
for (auto timer : timers)
|
||||
{
|
||||
if (timer.second != nullptr)
|
||||
continue;
|
||||
timer.second = new Timer(amx, callback, msec, def, args);
|
||||
id = timer.first;
|
||||
}
|
||||
|
||||
if (id == -1)
|
||||
{
|
||||
timers[pointer] = new Timer(amx, callback, msec, def, args);
|
||||
id = pointer;
|
||||
pointer++;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_LUA)
|
||||
int TimerAPI::CreateTimerLua(lua_State *lua, ScriptFuncLua callback, long msec, const std::string& def, std::vector<boost::any> args)
|
||||
{
|
||||
int id = -1;
|
||||
|
||||
for (auto timer : timers)
|
||||
{
|
||||
if (timer.second != nullptr)
|
||||
continue;
|
||||
timer.second = new Timer(lua, callback, msec, def, args);
|
||||
id = timer.first;
|
||||
}
|
||||
|
||||
if (id == -1)
|
||||
{
|
||||
timers[pointer] = new Timer(lua, callback, msec, def, args);
|
||||
id = pointer;
|
||||
pointer++;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int TimerAPI::CreateTimer(ScriptFunc callback, long msec, const std::string &def, std::vector<boost::any> args)
|
||||
{
|
||||
int id = -1;
|
||||
|
||||
for (auto timer : timers)
|
||||
{
|
||||
if (timer.second != nullptr)
|
||||
continue;
|
||||
timer.second = new Timer(callback, msec, def, args);
|
||||
id = timer.first;
|
||||
}
|
||||
|
||||
if (id == -1)
|
||||
{
|
||||
timers[pointer] = new Timer(callback, msec, def, args);
|
||||
id = pointer;
|
||||
pointer++;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void TimerAPI::FreeTimer(int timerid)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
if (timers.at(timerid) != nullptr)
|
||||
{
|
||||
delete timers[timerid];
|
||||
timers[timerid] = nullptr;
|
||||
}
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
std::cerr << "Timer " << timerid << " not found!" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void TimerAPI::ResetTimer(int timerid, long msec)
|
||||
{
|
||||
try
|
||||
{
|
||||
timers.at(timerid)->Restart(msec);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
std::cerr << "Timer " << timerid << " not found!" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void TimerAPI::StartTimer(int timerid)
|
||||
{
|
||||
try
|
||||
{
|
||||
Timer *timer = timers.at(timerid);
|
||||
if (timer == nullptr)
|
||||
throw 1;
|
||||
timer->Start();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
std::cerr << "Timer " << timerid << " not found!" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void TimerAPI::StopTimer(int timerid)
|
||||
{
|
||||
try
|
||||
{
|
||||
timers.at(timerid)->Stop();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
std::cerr << "Timer " << timerid << " not found!" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool TimerAPI::IsEndTimer(int timerid)
|
||||
{
|
||||
bool ret = false;
|
||||
try
|
||||
{
|
||||
ret = timers.at(timerid)->IsEnd();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
std::cerr << "Timer " << timerid << " not found!" << endl;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void TimerAPI::Terminate()
|
||||
{
|
||||
for (auto timer : timers)
|
||||
{
|
||||
if (timer.second != nullptr)
|
||||
delete timer.second;
|
||||
timer.second = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void TimerAPI::Tick()
|
||||
{
|
||||
for (auto timer : timers)
|
||||
{
|
||||
if (timer.second != nullptr)
|
||||
timer.second->Tick();
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
//
|
||||
// Created by koncord on 15.03.16.
|
||||
//
|
||||
|
||||
#ifndef OPENMW_TIMERAPI_HPP
|
||||
#define OPENMW_TIMERAPI_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <Script/Script.hpp>
|
||||
#include <Script/ScriptFunction.hpp>
|
||||
|
||||
namespace mwmp
|
||||
{
|
||||
|
||||
class TimerAPI;
|
||||
|
||||
class Timer: public ScriptFunction
|
||||
{
|
||||
friend class TimerAPI;
|
||||
|
||||
public:
|
||||
|
||||
Timer(ScriptFunc callback, long msec, const std::string& def, std::vector<boost::any> args);
|
||||
#if defined(ENABLE_PAWN)
|
||||
Timer(AMX *amx, ScriptFuncPAWN callback, long msec, const std::string& def, std::vector<boost::any> args);
|
||||
#endif
|
||||
#if defined(ENABLE_LUA)
|
||||
Timer(lua_State *lua, ScriptFuncLua callback, long msec, const std::string& def, std::vector<boost::any> args);
|
||||
#endif
|
||||
void Tick();
|
||||
|
||||
bool IsEnd();
|
||||
void Stop();
|
||||
void Start();
|
||||
void Restart(int msec);
|
||||
private:
|
||||
double startTime, targetMsec;
|
||||
std::string publ, arg_types;
|
||||
std::vector<boost::any> args;
|
||||
Script *scr;
|
||||
bool end;
|
||||
};
|
||||
|
||||
class TimerAPI
|
||||
{
|
||||
public:
|
||||
#if defined(ENABLE_PAWN)
|
||||
static int CreateTimerPAWN(AMX *amx, ScriptFuncPAWN callback, long msec, const std::string& def, std::vector<boost::any> args);
|
||||
#endif
|
||||
#if defined(ENABLE_LUA)
|
||||
static int CreateTimerLua(lua_State *lua, ScriptFuncLua callback, long msec, const std::string& def, std::vector<boost::any> args);
|
||||
#endif
|
||||
static int CreateTimer(ScriptFunc callback, long msec, const std::string& def, std::vector<boost::any> args);
|
||||
static void FreeTimer(int timerid);
|
||||
static void ResetTimer(int timerid, long msec);
|
||||
static void StartTimer(int timerid);
|
||||
static void StopTimer(int timerid);
|
||||
static bool IsEndTimer(int timerid);
|
||||
|
||||
static void Terminate();
|
||||
|
||||
static void Tick();
|
||||
private:
|
||||
static std::unordered_map<int, Timer* > timers;
|
||||
static int pointer;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //OPENMW_TIMERAPI_HPP
|
103
apps/openmw-mp/Script/CommandController.cpp
Normal file
103
apps/openmw-mp/Script/CommandController.cpp
Normal file
|
@ -0,0 +1,103 @@
|
|||
//
|
||||
// Created by koncord on 08.08.17.
|
||||
//
|
||||
#include "CommandController.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <boost/spirit/home/x3.hpp>
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include "Player.hpp"
|
||||
#include "LuaState.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
void CommandController::Init(LuaState &lua)
|
||||
{
|
||||
sol::table cmdCtrlTable = lua.getState()->create_named_table("CommandController");
|
||||
|
||||
cmdCtrlTable["registerCommand"] = [&lua](const std::string &command, sol::function func, const std::string &helpMessage) {
|
||||
return lua.getCmdCtrl().registerCommand(Misc::StringUtils::lowerCase(command), func, helpMessage);
|
||||
};
|
||||
|
||||
cmdCtrlTable["unregisterCommand"] = [&lua](const std::string &command) {
|
||||
lua.getCmdCtrl().unregisterCommand(Misc::StringUtils::lowerCase(command));
|
||||
};
|
||||
|
||||
cmdCtrlTable["hasCommand"] = [&lua](const std::string &command) {
|
||||
return lua.getCmdCtrl().hasCommand(Misc::StringUtils::lowerCase(command));
|
||||
};
|
||||
|
||||
cmdCtrlTable["getHelpStrings"] = [&lua]() {
|
||||
auto &commands = lua.getCmdCtrl().commands;
|
||||
sol::table helpTable = lua.getState()->create_table(commands.size(), 0);
|
||||
|
||||
for (const auto &cmd : commands)
|
||||
helpTable.add(cmd.first, cmd.second.helpMessage);
|
||||
|
||||
return helpTable;
|
||||
};
|
||||
}
|
||||
|
||||
bool CommandController::registerCommand(const std::string &command, sol::function func, const std::string &helpMessage)
|
||||
{
|
||||
auto iter = commands.find(command);
|
||||
if (iter == commands.end())
|
||||
{
|
||||
commands.emplace(command, Command {std::move(func), helpMessage});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CommandController::unregisterCommand(const std::string &command)
|
||||
{
|
||||
commands.erase(command);
|
||||
}
|
||||
|
||||
bool CommandController::hasCommand(const std::string &command)
|
||||
{
|
||||
return commands.find(command) != commands.end();
|
||||
}
|
||||
|
||||
std::pair<CommandController::ExecResult, std::string> CommandController::exec(const std::shared_ptr<Player> &player,
|
||||
const std::string &message, unsigned channel)
|
||||
{
|
||||
char cmdChar = message[0];
|
||||
if (message.size() < 2 || (cmdChar != '/' && cmdChar != '!'))
|
||||
return make_pair(ExecResult::NOT_CMD, "");
|
||||
|
||||
auto tokens = cmdParser(message);
|
||||
|
||||
auto cmd = commands.find(Misc::StringUtils::lowerCase(&tokens[0][1]));
|
||||
if (cmd != commands.end())
|
||||
{
|
||||
tokens.pop_front();
|
||||
bool result = cmd->second.func(player, sol::as_table(move(tokens)), channel);
|
||||
if (result)
|
||||
return make_pair(ExecResult::SUCCESS, "");
|
||||
|
||||
return make_pair(ExecResult::FAIL, cmd->second.helpMessage);
|
||||
}
|
||||
|
||||
return make_pair(ExecResult::NOT_FOUND, "");
|
||||
}
|
||||
|
||||
std::deque<std::string> CommandController::cmdParser(const std::string &message)
|
||||
{
|
||||
deque<string> ret;
|
||||
namespace x3 = boost::spirit::x3;
|
||||
auto const sep = ' ';
|
||||
auto const quoted = '"' >> *~x3::char_('"') >> '"';
|
||||
auto const unquoted = *~x3::char_(sep);
|
||||
auto const arguments = (quoted | unquoted) % sep;
|
||||
/*auto const command = '/' >> *~x3::char_(sep) >> sep;
|
||||
|
||||
if (!x3::phrase_parse(message.cbegin(), message.cend(), arguments, command, ret))
|
||||
Utils::throwError("failed to parse message: "+ message);*/
|
||||
|
||||
if (!x3::parse(message.cbegin(), message.cend(), arguments, ret))
|
||||
Utils::throwError("failed to parse message: " + message);
|
||||
|
||||
return ret;
|
||||
}
|
63
apps/openmw-mp/Script/CommandController.hpp
Normal file
63
apps/openmw-mp/Script/CommandController.hpp
Normal file
|
@ -0,0 +1,63 @@
|
|||
//
|
||||
// Created by koncord on 08.08.17.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sol.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include <string>
|
||||
#include <deque>
|
||||
|
||||
class Player;
|
||||
class LuaState;
|
||||
|
||||
struct Command
|
||||
{
|
||||
sol::function func;
|
||||
std::string helpMessage;
|
||||
};
|
||||
|
||||
class CommandController
|
||||
{
|
||||
typedef std::unordered_map<std::string, Command> Container;
|
||||
typedef Container::iterator Iter;
|
||||
typedef Container::const_iterator CIter;
|
||||
public:
|
||||
static void Init(LuaState &lua);
|
||||
public:
|
||||
|
||||
enum class ExecResult : int
|
||||
{
|
||||
NOT_FOUND,
|
||||
SUCCESS,
|
||||
FAIL,
|
||||
NOT_CMD
|
||||
};
|
||||
|
||||
/**
|
||||
* Register new command. Only unique commands are allowed.
|
||||
* @param command name of command. Case sensitive. No need in command prefix ('/' or '!').
|
||||
* @param helpMessage help message. Shows in the '/help' command also appears if exec() fails.
|
||||
* @param callback Will be called when command is called.
|
||||
* @return false if the command already registered.
|
||||
*/
|
||||
bool registerCommand(const std::string &command, sol::function callback, const std::string &helpMessage);
|
||||
/**
|
||||
* Removes a registered command
|
||||
* @param command name of command.
|
||||
*/
|
||||
void unregisterCommand(const std::string &command);
|
||||
|
||||
/**
|
||||
* Check a command is exist.
|
||||
* @param command name of command
|
||||
* @return false if the command did not exist.
|
||||
*/
|
||||
bool hasCommand(const std::string &command);
|
||||
|
||||
std::pair<ExecResult, std::string> exec(const std::shared_ptr<Player> &player, const std::string &message, unsigned channel);
|
||||
private:
|
||||
std::deque<std::string> cmdParser(const std::string &message);
|
||||
Container commands;
|
||||
};
|
158
apps/openmw-mp/Script/EventController.cpp
Normal file
158
apps/openmw-mp/Script/EventController.cpp
Normal file
|
@ -0,0 +1,158 @@
|
|||
//
|
||||
// Created by koncord on 30.07.17.
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include "EventController.hpp"
|
||||
|
||||
using namespace std;
|
||||
#define ADD_CORE_EVENT(event) #event, CoreEvent::event
|
||||
void EventController::Init(LuaState &lua)
|
||||
{
|
||||
sol::table eventsTable = lua.getState()->create_named_table("Event");
|
||||
|
||||
eventsTable["register"] = [&lua](int event, sol::function func, sol::this_environment te) {
|
||||
sol::environment& env = te;
|
||||
lua.getEventCtrl().registerEvent(event, env, func, false);
|
||||
};
|
||||
eventsTable["stop"] = [&lua](int event) {
|
||||
lua.getEventCtrl().stop(event);
|
||||
};
|
||||
eventsTable["create"] = [&lua]() {
|
||||
return lua.getEventCtrl().createEvent();
|
||||
};
|
||||
eventsTable["raise"] = [&lua](unsigned event, sol::table data) {
|
||||
lua.getEventCtrl().raiseEvent(event, data);
|
||||
};
|
||||
eventsTable["raiseSpecified"] = [&lua](unsigned event, const std::string &moduleName, sol::table data) {
|
||||
lua.getEventCtrl().raiseEvent(event, data, moduleName);
|
||||
};
|
||||
}
|
||||
|
||||
EventController::EventController(LuaState *luaCtrl)
|
||||
{
|
||||
this->luaCtrl = luaCtrl;
|
||||
|
||||
#ifdef SERVER_DEBUG
|
||||
luaCtrl->getState()->new_enum<false>
|
||||
#else
|
||||
luaCtrl->getState()->new_enum
|
||||
#endif
|
||||
("Events",
|
||||
ADD_CORE_EVENT(ON_POST_INIT),
|
||||
ADD_CORE_EVENT(ON_EXIT),
|
||||
ADD_CORE_EVENT(ON_PLAYER_CONNECT),
|
||||
ADD_CORE_EVENT(ON_PLAYER_DISCONNECT),
|
||||
ADD_CORE_EVENT(ON_PLAYER_DEATH),
|
||||
ADD_CORE_EVENT(ON_PLAYER_RESURRECT),
|
||||
ADD_CORE_EVENT(ON_PLAYER_CELLCHANGE),
|
||||
ADD_CORE_EVENT(ON_PLAYER_KILLCOUNT),
|
||||
ADD_CORE_EVENT(ON_PLAYER_ATTRIBUTE),
|
||||
ADD_CORE_EVENT(ON_PLAYER_SKILL),
|
||||
ADD_CORE_EVENT(ON_PLAYER_LEVEL),
|
||||
ADD_CORE_EVENT(ON_PLAYER_BOUNTY),
|
||||
ADD_CORE_EVENT(ON_PLAYER_REPUTATION),
|
||||
ADD_CORE_EVENT(ON_PLAYER_EQUIPMENT),
|
||||
ADD_CORE_EVENT(ON_PLAYER_INVENTORY),
|
||||
ADD_CORE_EVENT(ON_PLAYER_JOURNAL),
|
||||
ADD_CORE_EVENT(ON_PLAYER_FACTION),
|
||||
ADD_CORE_EVENT(ON_PLAYER_SHAPESHIFT),
|
||||
ADD_CORE_EVENT(ON_PLAYER_SPELLBOOK),
|
||||
ADD_CORE_EVENT(ON_PLAYER_QUICKKEYS),
|
||||
ADD_CORE_EVENT(ON_PLAYER_TOPIC),
|
||||
ADD_CORE_EVENT(ON_PLAYER_DISPOSITION),
|
||||
ADD_CORE_EVENT(ON_PLAYER_BOOK),
|
||||
ADD_CORE_EVENT(ON_PLAYER_MISCELLANEOUS),
|
||||
ADD_CORE_EVENT(ON_PLAYER_INTERACTION),
|
||||
ADD_CORE_EVENT(ON_PLAYER_REST),
|
||||
ADD_CORE_EVENT(ON_PLAYER_SENDMESSAGE),
|
||||
ADD_CORE_EVENT(ON_PLAYER_ENDCHARGEN),
|
||||
ADD_CORE_EVENT(ON_PLAYER_WEATHER),
|
||||
ADD_CORE_EVENT(ON_RECORD_DYNAMIC),
|
||||
ADD_CORE_EVENT(ON_CHANNEL_ACTION),
|
||||
ADD_CORE_EVENT(ON_REQUEST_PLUGIN_LIST),
|
||||
ADD_CORE_EVENT(ON_MP_REFNUM),
|
||||
ADD_CORE_EVENT(ON_ACTOR_EQUIPMENT),
|
||||
ADD_CORE_EVENT(ON_ACTOR_CELL_CHANGE),
|
||||
ADD_CORE_EVENT(ON_ACTOR_LIST),
|
||||
ADD_CORE_EVENT(ON_ACTOR_TEST),
|
||||
ADD_CORE_EVENT(ON_CELL_LOAD),
|
||||
ADD_CORE_EVENT(ON_CELL_UNLOAD),
|
||||
ADD_CORE_EVENT(ON_CELL_DELETION),
|
||||
ADD_CORE_EVENT(ON_CONTAINER),
|
||||
ADD_CORE_EVENT(ON_DOOR_STATE),
|
||||
ADD_CORE_EVENT(ON_OBJECT_PLACE),
|
||||
ADD_CORE_EVENT(ON_OBJECT_STATE),
|
||||
ADD_CORE_EVENT(ON_OBJECT_SPAWN),
|
||||
ADD_CORE_EVENT(ON_OBJECT_DELETE),
|
||||
ADD_CORE_EVENT(ON_OBJECT_LOCK),
|
||||
ADD_CORE_EVENT(ON_OBJECT_SCALE),
|
||||
ADD_CORE_EVENT(ON_OBJECT_TRAP),
|
||||
ADD_CORE_EVENT(ON_WORLD_MAP)
|
||||
);
|
||||
|
||||
sol::state &state = *luaCtrl->getState();
|
||||
sol::table eventsEnum = state["Events"];
|
||||
for (int i = CoreEvent::FIRST; i < CoreEvent::LAST; ++i)
|
||||
{
|
||||
#ifdef SERVER_DEBUG
|
||||
bool found = false;
|
||||
eventsEnum.for_each([&found, &i](sol::object key, sol::object value){
|
||||
if (value.as<int>() == i)
|
||||
{
|
||||
found = true;
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
if (!found)
|
||||
Utils::throwError("Event " + to_string(i) + " is not registered");
|
||||
#endif
|
||||
events[i]; // create core event
|
||||
}
|
||||
}
|
||||
|
||||
void EventController::registerEvent(int event, sol::environment &env, sol::function& func, bool needsOldState)
|
||||
{
|
||||
auto iter = events.find(event);
|
||||
if (iter != events.end())
|
||||
iter->second.push(env, func, needsOldState);
|
||||
}
|
||||
|
||||
void EventController::stop(int event)
|
||||
{
|
||||
printf("EventController::stop\n");
|
||||
auto iter = events.find(event);
|
||||
if (iter != events.end())
|
||||
iter->second.stop();
|
||||
}
|
||||
|
||||
CallbackCollection &EventController::GetEvents(Event event)
|
||||
{
|
||||
return events.at(event);
|
||||
}
|
||||
|
||||
Event EventController::createEvent()
|
||||
{
|
||||
events[lastEvent];
|
||||
return lastEvent++;
|
||||
}
|
||||
|
||||
void EventController::raiseEvent(Event id, sol::table data, const string &moduleName)
|
||||
{
|
||||
auto iter = events.find(id);
|
||||
if (iter != events.end())
|
||||
{
|
||||
if (!moduleName.empty())
|
||||
{
|
||||
auto f = std::find_if(iter->second.begin(), iter->second.end(), [&moduleName](const auto &item){
|
||||
return item.env["ModuleName"]["name"] == moduleName;
|
||||
});
|
||||
if (f != iter->second.end())
|
||||
f->fn.call(data); // call only specified mod
|
||||
}
|
||||
iter->second.call(CallbackCollection::type_tag<void>(), false, data); // call all registered events with this id
|
||||
}
|
||||
else
|
||||
cerr << "Event with id: " << id << " is not registered" << endl;
|
||||
}
|
204
apps/openmw-mp/Script/EventController.hpp
Normal file
204
apps/openmw-mp/Script/EventController.hpp
Normal file
|
@ -0,0 +1,204 @@
|
|||
//
|
||||
// Created by koncord on 30.07.17.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sol.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "LuaState.hpp"
|
||||
|
||||
typedef unsigned Event;
|
||||
|
||||
namespace CoreEvent
|
||||
{
|
||||
enum
|
||||
{
|
||||
ON_EXIT = 0,
|
||||
ON_POST_INIT,
|
||||
ON_REQUEST_PLUGIN_LIST,
|
||||
ON_PLAYER_CONNECT,
|
||||
ON_PLAYER_DISCONNECT,
|
||||
ON_PLAYER_DEATH,
|
||||
ON_PLAYER_RESURRECT,
|
||||
ON_PLAYER_CELLCHANGE,
|
||||
ON_PLAYER_KILLCOUNT,
|
||||
ON_PLAYER_ATTRIBUTE,
|
||||
ON_PLAYER_SKILL,
|
||||
ON_PLAYER_LEVEL,
|
||||
ON_PLAYER_BOUNTY,
|
||||
ON_PLAYER_REPUTATION,
|
||||
ON_PLAYER_EQUIPMENT,
|
||||
ON_PLAYER_INVENTORY,
|
||||
ON_PLAYER_JOURNAL,
|
||||
ON_PLAYER_FACTION,
|
||||
ON_PLAYER_SHAPESHIFT,
|
||||
ON_PLAYER_SPELLBOOK,
|
||||
ON_PLAYER_QUICKKEYS,
|
||||
ON_PLAYER_TOPIC,
|
||||
ON_PLAYER_DISPOSITION,
|
||||
ON_PLAYER_BOOK,
|
||||
ON_PLAYER_MISCELLANEOUS,
|
||||
ON_PLAYER_INTERACTION,
|
||||
ON_PLAYER_REST,
|
||||
ON_PLAYER_SENDMESSAGE,
|
||||
ON_PLAYER_ENDCHARGEN,
|
||||
ON_PLAYER_WEATHER,
|
||||
|
||||
ON_RECORD_DYNAMIC,
|
||||
|
||||
ON_CHANNEL_ACTION,
|
||||
|
||||
ON_MP_REFNUM,
|
||||
|
||||
ON_ACTOR_EQUIPMENT,
|
||||
ON_ACTOR_CELL_CHANGE,
|
||||
ON_ACTOR_LIST,
|
||||
ON_ACTOR_TEST,
|
||||
|
||||
ON_CELL_LOAD,
|
||||
ON_CELL_UNLOAD,
|
||||
ON_CELL_DELETION,
|
||||
|
||||
ON_CONTAINER,
|
||||
ON_DOOR_STATE,
|
||||
ON_OBJECT_PLACE,
|
||||
ON_OBJECT_STATE,
|
||||
ON_OBJECT_SPAWN,
|
||||
ON_OBJECT_DELETE,
|
||||
ON_OBJECT_LOCK,
|
||||
ON_OBJECT_SCALE,
|
||||
ON_OBJECT_TRAP,
|
||||
|
||||
ON_WORLD_MAP,
|
||||
|
||||
LAST,
|
||||
};
|
||||
const int FIRST = ON_EXIT;
|
||||
}
|
||||
|
||||
class CallbackCollection // todo: add sort by dependencies
|
||||
{
|
||||
public:
|
||||
struct CBType
|
||||
{
|
||||
sol::environment env;
|
||||
sol::function fn;
|
||||
bool needsOldState;
|
||||
CBType(sol::environment _env, sol::function _fn, bool _needsOldState): env(_env), fn(_fn), needsOldState(_needsOldState) {}
|
||||
};
|
||||
|
||||
typedef std::vector<CBType> Container;
|
||||
typedef Container::iterator Iterator;
|
||||
typedef Container::const_iterator CIterator;
|
||||
|
||||
template<typename> struct type_tag {};
|
||||
|
||||
void push(sol::environment &env, sol::function &func, bool needsOldState) { functions.emplace_back(env, func, needsOldState); }
|
||||
CIterator begin() const { return functions.begin(); }
|
||||
CIterator end() const { return functions.end(); }
|
||||
|
||||
void stop() {_stop = true;}
|
||||
bool isStoped() const {return _stop;}
|
||||
CIterator stopedAt() const { return lastCalled; }
|
||||
|
||||
template<typename... Args, typename OldData>
|
||||
void callWOld(const OldData &oldData, Args&&... args)
|
||||
{
|
||||
lastCalled = functions.end();
|
||||
_stop = false;
|
||||
for (CIterator iter = functions.begin(); iter != functions.end(); ++iter)
|
||||
{
|
||||
if (!_stop)
|
||||
{
|
||||
if (!iter->needsOldState)
|
||||
{
|
||||
iter->fn.call(std::forward<Args>(args)...);
|
||||
continue;
|
||||
}
|
||||
iter->fn.call(std::forward<Args>(args)..., oldData);
|
||||
}
|
||||
else
|
||||
{
|
||||
lastCalled = iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void call(type_tag<void>, Args&&... args)
|
||||
{
|
||||
lastCalled = functions.end();
|
||||
_stop = false;
|
||||
for (CIterator iter = functions.begin(); iter != functions.end(); ++iter)
|
||||
{
|
||||
if (!_stop)
|
||||
iter->fn.call(std::forward<Args>(args)...);
|
||||
else
|
||||
{
|
||||
lastCalled = iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename R, typename... Args>
|
||||
decltype(auto) call(type_tag<R>, Args&&... args)
|
||||
{
|
||||
R ret;
|
||||
|
||||
lastCalled = functions.end();
|
||||
_stop = false;
|
||||
for (CIterator iter = functions.begin(); iter != functions.end(); ++iter)
|
||||
{
|
||||
if (!_stop)
|
||||
ret = iter->fn.call(std::forward<Args>(args)...);
|
||||
else
|
||||
{
|
||||
lastCalled = iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
Container functions;
|
||||
CIterator lastCalled;
|
||||
bool _stop = false;
|
||||
};
|
||||
|
||||
class EventController
|
||||
{
|
||||
public:
|
||||
static void Init(LuaState &lua);
|
||||
public:
|
||||
explicit EventController(LuaState *luaCtrl);
|
||||
typedef std::unordered_map<int, CallbackCollection> Container;
|
||||
|
||||
void registerEvent(int event, sol::environment &env, sol::function& func, bool needsOldState);
|
||||
CallbackCollection& GetEvents(Event event);
|
||||
Event createEvent();
|
||||
void raiseEvent(Event id, sol::table data, const std::string &moduleName = "");
|
||||
void stop(int event);
|
||||
|
||||
template<Event event, bool canProvideOldState = false, typename R = void, typename... Args>
|
||||
R Call(Args&&... args)
|
||||
{
|
||||
if (canProvideOldState)
|
||||
events.at(event).callWOld(std::forward<Args>(args)...);
|
||||
else
|
||||
return events.at(event).call(CallbackCollection::type_tag<R>{}, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<Event event, bool canProvideOldState = false, typename R = void>
|
||||
R Call()
|
||||
{
|
||||
return events.at(event).call(CallbackCollection::type_tag<R>{});
|
||||
}
|
||||
private:
|
||||
Container events;
|
||||
Event lastEvent = CoreEvent::LAST;
|
||||
LuaState *luaCtrl;
|
||||
};
|
|
@ -1,367 +0,0 @@
|
|||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
#include <components/openmw-mp/Base/BaseActor.hpp>
|
||||
|
||||
#include <apps/openmw-mp/Networking.hpp>
|
||||
#include <apps/openmw-mp/Player.hpp>
|
||||
#include <apps/openmw-mp/Utils.hpp>
|
||||
#include <apps/openmw-mp/Script/ScriptFunctions.hpp>
|
||||
|
||||
#include <components/esm/creaturestats.hpp>
|
||||
|
||||
#include "Actors.hpp"
|
||||
|
||||
using namespace mwmp;
|
||||
|
||||
BaseActorList *readActorList;
|
||||
BaseActorList writeActorList;
|
||||
|
||||
BaseActor tempActor;
|
||||
const BaseActor emptyActor = {};
|
||||
|
||||
static std::string tempCellDescription;
|
||||
|
||||
void ActorFunctions::ReadLastActorList() noexcept
|
||||
{
|
||||
readActorList = mwmp::Networking::getPtr()->getLastActorList();
|
||||
}
|
||||
|
||||
void ActorFunctions::ReadCellActorList(const char* cellDescription) noexcept
|
||||
{
|
||||
ESM::Cell esmCell = Utils::getCellFromDescription(cellDescription);
|
||||
Cell *serverCell = CellController::get()->getCell(&esmCell);
|
||||
readActorList = serverCell->getActorList();
|
||||
}
|
||||
|
||||
void ActorFunctions::InitializeActorList(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, );
|
||||
|
||||
writeActorList.cell.blank();
|
||||
writeActorList.baseActors.clear();
|
||||
writeActorList.guid = player->guid;
|
||||
}
|
||||
|
||||
unsigned int ActorFunctions::GetActorListSize() noexcept
|
||||
{
|
||||
return readActorList->count;
|
||||
}
|
||||
|
||||
unsigned char ActorFunctions::GetActorListAction() noexcept
|
||||
{
|
||||
return readActorList->action;
|
||||
}
|
||||
|
||||
const char *ActorFunctions::GetActorCell(unsigned int i) noexcept
|
||||
{
|
||||
tempCellDescription = readActorList->baseActors.at(i).cell.getDescription();
|
||||
return tempCellDescription.c_str();
|
||||
}
|
||||
|
||||
const char *ActorFunctions::GetActorRefId(unsigned int i) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).refId.c_str();
|
||||
}
|
||||
|
||||
int ActorFunctions::GetActorRefNumIndex(unsigned int i) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).refNumIndex;
|
||||
}
|
||||
|
||||
int ActorFunctions::GetActorMpNum(unsigned int i) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).mpNum;
|
||||
}
|
||||
|
||||
double ActorFunctions::GetActorPosX(unsigned int i) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).position.pos[0];
|
||||
}
|
||||
|
||||
double ActorFunctions::GetActorPosY(unsigned int i) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).position.pos[1];
|
||||
}
|
||||
|
||||
double ActorFunctions::GetActorPosZ(unsigned int i) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).position.pos[2];
|
||||
}
|
||||
|
||||
double ActorFunctions::GetActorRotX(unsigned int i) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).position.rot[0];
|
||||
}
|
||||
|
||||
double ActorFunctions::GetActorRotY(unsigned int i) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).position.rot[1];
|
||||
}
|
||||
|
||||
double ActorFunctions::GetActorRotZ(unsigned int i) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).position.rot[2];
|
||||
}
|
||||
|
||||
double ActorFunctions::GetActorHealthBase(unsigned int i) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).creatureStats.mDynamic[0].mBase;
|
||||
}
|
||||
|
||||
double ActorFunctions::GetActorHealthCurrent(unsigned int i) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).creatureStats.mDynamic[0].mCurrent;
|
||||
}
|
||||
|
||||
double ActorFunctions::GetActorHealthModified(unsigned int i) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).creatureStats.mDynamic[0].mMod;
|
||||
}
|
||||
|
||||
double ActorFunctions::GetActorMagickaBase(unsigned int i) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).creatureStats.mDynamic[1].mBase;
|
||||
}
|
||||
|
||||
double ActorFunctions::GetActorMagickaCurrent(unsigned int i) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).creatureStats.mDynamic[1].mCurrent;
|
||||
}
|
||||
|
||||
double ActorFunctions::GetActorMagickaModified(unsigned int i) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).creatureStats.mDynamic[1].mMod;
|
||||
}
|
||||
|
||||
double ActorFunctions::GetActorFatigueBase(unsigned int i) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).creatureStats.mDynamic[2].mBase;
|
||||
}
|
||||
|
||||
double ActorFunctions::GetActorFatigueCurrent(unsigned int i) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).creatureStats.mDynamic[2].mCurrent;
|
||||
}
|
||||
|
||||
double ActorFunctions::GetActorFatigueModified(unsigned int i) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).creatureStats.mDynamic[2].mMod;
|
||||
}
|
||||
|
||||
const char *ActorFunctions::GetActorEquipmentItemRefId(unsigned int i, unsigned short slot) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).equipmentItems[slot].refId.c_str();
|
||||
}
|
||||
|
||||
int ActorFunctions::GetActorEquipmentItemCount(unsigned int i, unsigned short slot) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).equipmentItems[slot].count;
|
||||
}
|
||||
|
||||
int ActorFunctions::GetActorEquipmentItemCharge(unsigned int i, unsigned short slot) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).equipmentItems[slot].charge;
|
||||
}
|
||||
|
||||
double ActorFunctions::GetActorEquipmentItemEnchantmentCharge(unsigned int i, unsigned short slot) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).equipmentItems[slot].enchantmentCharge;
|
||||
}
|
||||
|
||||
bool ActorFunctions::DoesActorHavePosition(unsigned int i) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).hasPositionData;
|
||||
}
|
||||
|
||||
bool ActorFunctions::DoesActorHaveStatsDynamic(unsigned int i) noexcept
|
||||
{
|
||||
return readActorList->baseActors.at(i).hasStatsDynamicData;
|
||||
}
|
||||
|
||||
void ActorFunctions::SetActorListCell(const char* cellDescription) noexcept
|
||||
{
|
||||
writeActorList.cell = Utils::getCellFromDescription(cellDescription);
|
||||
}
|
||||
|
||||
void ActorFunctions::SetActorListAction(unsigned char action) noexcept
|
||||
{
|
||||
writeActorList.action = action;
|
||||
}
|
||||
|
||||
void ActorFunctions::SetActorCell(const char* cellDescription) noexcept
|
||||
{
|
||||
tempActor.cell = Utils::getCellFromDescription(cellDescription);
|
||||
}
|
||||
|
||||
void ActorFunctions::SetActorRefId(const char* refId) noexcept
|
||||
{
|
||||
tempActor.refId = refId;
|
||||
}
|
||||
|
||||
void ActorFunctions::SetActorRefNumIndex(int refNumIndex) noexcept
|
||||
{
|
||||
tempActor.refNumIndex = refNumIndex;
|
||||
}
|
||||
|
||||
void ActorFunctions::SetActorMpNum(int mpNum) noexcept
|
||||
{
|
||||
tempActor.mpNum = mpNum;
|
||||
}
|
||||
|
||||
void ActorFunctions::SetActorPosition(double x, double y, double z) noexcept
|
||||
{
|
||||
tempActor.position.pos[0] = x;
|
||||
tempActor.position.pos[1] = y;
|
||||
tempActor.position.pos[2] = z;
|
||||
}
|
||||
|
||||
void ActorFunctions::SetActorRotation(double x, double y, double z) noexcept
|
||||
{
|
||||
tempActor.position.rot[0] = x;
|
||||
tempActor.position.rot[1] = y;
|
||||
tempActor.position.rot[2] = z;
|
||||
}
|
||||
|
||||
void ActorFunctions::SetActorHealthBase(double value) noexcept
|
||||
{
|
||||
tempActor.creatureStats.mDynamic[0].mBase = value;
|
||||
}
|
||||
|
||||
void ActorFunctions::SetActorHealthCurrent(double value) noexcept
|
||||
{
|
||||
tempActor.creatureStats.mDynamic[0].mCurrent = value;
|
||||
}
|
||||
|
||||
void ActorFunctions::SetActorHealthModified(double value) noexcept
|
||||
{
|
||||
tempActor.creatureStats.mDynamic[0].mMod = value;
|
||||
}
|
||||
|
||||
void ActorFunctions::SetActorMagickaBase(double value) noexcept
|
||||
{
|
||||
tempActor.creatureStats.mDynamic[1].mBase = value;
|
||||
}
|
||||
|
||||
void ActorFunctions::SetActorMagickaCurrent(double value) noexcept
|
||||
{
|
||||
tempActor.creatureStats.mDynamic[1].mCurrent = value;
|
||||
}
|
||||
|
||||
void ActorFunctions::SetActorMagickaModified(double value) noexcept
|
||||
{
|
||||
tempActor.creatureStats.mDynamic[1].mMod = value;
|
||||
}
|
||||
|
||||
void ActorFunctions::SetActorFatigueBase(double value) noexcept
|
||||
{
|
||||
tempActor.creatureStats.mDynamic[2].mBase = value;
|
||||
}
|
||||
|
||||
void ActorFunctions::SetActorFatigueCurrent(double value) noexcept
|
||||
{
|
||||
tempActor.creatureStats.mDynamic[2].mCurrent = value;
|
||||
}
|
||||
|
||||
void ActorFunctions::SetActorFatigueModified(double value) noexcept
|
||||
{
|
||||
tempActor.creatureStats.mDynamic[2].mMod = value;
|
||||
}
|
||||
|
||||
void ActorFunctions::SetActorAIAction(unsigned int action) noexcept
|
||||
{
|
||||
tempActor.aiAction = action;
|
||||
}
|
||||
|
||||
void ActorFunctions::SetActorAITargetToPlayer(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, );
|
||||
|
||||
tempActor.hasAiTarget = true;
|
||||
tempActor.aiTarget.isPlayer = true;
|
||||
|
||||
tempActor.aiTarget.guid = player->guid;
|
||||
}
|
||||
|
||||
void ActorFunctions::SetActorAITargetToActor(int refNumIndex, int mpNum) noexcept
|
||||
{
|
||||
tempActor.hasAiTarget = true;
|
||||
tempActor.aiTarget.isPlayer = false;
|
||||
|
||||
tempActor.aiTarget.refNumIndex = refNumIndex;
|
||||
tempActor.aiTarget.mpNum = mpNum;
|
||||
}
|
||||
|
||||
void ActorFunctions::EquipActorItem(unsigned short slot, const char *refId, unsigned int count, int charge, double enchantmentCharge) noexcept
|
||||
{
|
||||
tempActor.equipmentItems[slot].refId = refId;
|
||||
tempActor.equipmentItems[slot].count = count;
|
||||
tempActor.equipmentItems[slot].charge = charge;
|
||||
tempActor.equipmentItems[slot].enchantmentCharge = enchantmentCharge;
|
||||
}
|
||||
|
||||
void ActorFunctions::UnequipActorItem(unsigned short slot) noexcept
|
||||
{
|
||||
ActorFunctions::EquipActorItem(slot, "", 0, -1, -1);
|
||||
}
|
||||
|
||||
void ActorFunctions::AddActor() noexcept
|
||||
{
|
||||
writeActorList.baseActors.push_back(tempActor);
|
||||
|
||||
tempActor = emptyActor;
|
||||
}
|
||||
|
||||
void ActorFunctions::SendActorList() noexcept
|
||||
{
|
||||
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_LIST)->setActorList(&writeActorList);
|
||||
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_LIST)->Send(writeActorList.guid);
|
||||
}
|
||||
|
||||
void ActorFunctions::SendActorAuthority() noexcept
|
||||
{
|
||||
Cell *serverCell = CellController::get()->getCell(&writeActorList.cell);
|
||||
|
||||
if (serverCell != nullptr)
|
||||
{
|
||||
serverCell->setAuthority(writeActorList.guid);
|
||||
|
||||
mwmp::ActorPacket *authorityPacket = mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_AUTHORITY);
|
||||
authorityPacket->setActorList(&writeActorList);
|
||||
authorityPacket->Send(writeActorList.guid);
|
||||
|
||||
// Also send this to everyone else who has the cell loaded
|
||||
serverCell->sendToLoaded(authorityPacket, &writeActorList);
|
||||
}
|
||||
}
|
||||
|
||||
void ActorFunctions::SendActorPosition() noexcept
|
||||
{
|
||||
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_POSITION)->setActorList(&writeActorList);
|
||||
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_POSITION)->Send(writeActorList.guid);
|
||||
}
|
||||
|
||||
void ActorFunctions::SendActorStatsDynamic() noexcept
|
||||
{
|
||||
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_STATS_DYNAMIC)->setActorList(&writeActorList);
|
||||
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_STATS_DYNAMIC)->Send(writeActorList.guid);
|
||||
}
|
||||
|
||||
void ActorFunctions::SendActorEquipment() noexcept
|
||||
{
|
||||
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_EQUIPMENT)->setActorList(&writeActorList);
|
||||
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_EQUIPMENT)->Send(writeActorList.guid);
|
||||
}
|
||||
|
||||
void ActorFunctions::SendActorAI() noexcept
|
||||
{
|
||||
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_AI)->setActorList(&writeActorList);
|
||||
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_AI)->Send(writeActorList.guid);
|
||||
}
|
||||
|
||||
void ActorFunctions::SendActorCellChange() noexcept
|
||||
{
|
||||
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_CELL_CHANGE)->setActorList(&writeActorList);
|
||||
mwmp::Networking::get().getActorPacketController()->GetPacket(ID_ACTOR_CELL_CHANGE)->Send(writeActorList.guid);
|
||||
}
|
||||
|
|
@ -1,614 +0,0 @@
|
|||
#ifndef OPENMW_ACTORAPI_HPP
|
||||
#define OPENMW_ACTORAPI_HPP
|
||||
|
||||
#define ACTORAPI \
|
||||
{"ReadLastActorList", ActorFunctions::ReadLastActorList},\
|
||||
{"ReadCellActorList", ActorFunctions::ReadCellActorList},\
|
||||
{"InitializeActorList", ActorFunctions::InitializeActorList},\
|
||||
\
|
||||
{"GetActorListSize", ActorFunctions::GetActorListSize},\
|
||||
{"GetActorListAction", ActorFunctions::GetActorListAction},\
|
||||
\
|
||||
{"GetActorCell", ActorFunctions::GetActorCell},\
|
||||
{"GetActorRefId", ActorFunctions::GetActorRefId},\
|
||||
{"GetActorRefNumIndex", ActorFunctions::GetActorRefNumIndex},\
|
||||
{"GetActorMpNum", ActorFunctions::GetActorMpNum},\
|
||||
\
|
||||
{"GetActorPosX", ActorFunctions::GetActorPosX},\
|
||||
{"GetActorPosY", ActorFunctions::GetActorPosY},\
|
||||
{"GetActorPosZ", ActorFunctions::GetActorPosZ},\
|
||||
{"GetActorRotX", ActorFunctions::GetActorRotX},\
|
||||
{"GetActorRotY", ActorFunctions::GetActorRotY},\
|
||||
{"GetActorRotZ", ActorFunctions::GetActorRotZ},\
|
||||
\
|
||||
{"GetActorHealthBase", ActorFunctions::GetActorHealthBase},\
|
||||
{"GetActorHealthCurrent", ActorFunctions::GetActorHealthCurrent},\
|
||||
{"GetActorHealthModified", ActorFunctions::GetActorHealthModified},\
|
||||
{"GetActorMagickaBase", ActorFunctions::GetActorMagickaBase},\
|
||||
{"GetActorMagickaCurrent", ActorFunctions::GetActorMagickaCurrent},\
|
||||
{"GetActorMagickaModified", ActorFunctions::GetActorMagickaModified},\
|
||||
{"GetActorFatigueBase", ActorFunctions::GetActorFatigueBase},\
|
||||
{"GetActorFatigueCurrent", ActorFunctions::GetActorFatigueCurrent},\
|
||||
{"GetActorFatigueModified", ActorFunctions::GetActorFatigueModified},\
|
||||
\
|
||||
{"GetActorEquipmentItemRefId", ActorFunctions::GetActorEquipmentItemRefId},\
|
||||
{"GetActorEquipmentItemCount", ActorFunctions::GetActorEquipmentItemCount},\
|
||||
{"GetActorEquipmentItemCharge", ActorFunctions::GetActorEquipmentItemCharge},\
|
||||
{"GetActorEquipmentItemEnchantmentCharge", ActorFunctions::GetActorEquipmentItemEnchantmentCharge},\
|
||||
\
|
||||
{"DoesActorHavePosition", ActorFunctions::DoesActorHavePosition},\
|
||||
{"DoesActorHaveStatsDynamic", ActorFunctions::DoesActorHaveStatsDynamic},\
|
||||
\
|
||||
{"SetActorListCell", ActorFunctions::SetActorListCell},\
|
||||
{"SetActorListAction", ActorFunctions::SetActorListAction},\
|
||||
\
|
||||
{"SetActorCell", ActorFunctions::SetActorCell},\
|
||||
{"SetActorRefId", ActorFunctions::SetActorRefId},\
|
||||
{"SetActorRefNumIndex", ActorFunctions::SetActorRefNumIndex},\
|
||||
{"SetActorMpNum", ActorFunctions::SetActorMpNum},\
|
||||
\
|
||||
{"SetActorPosition", ActorFunctions::SetActorPosition},\
|
||||
{"SetActorRotation", ActorFunctions::SetActorRotation},\
|
||||
\
|
||||
{"SetActorHealthBase", ActorFunctions::SetActorHealthBase},\
|
||||
{"SetActorHealthCurrent", ActorFunctions::SetActorHealthCurrent},\
|
||||
{"SetActorHealthModified", ActorFunctions::SetActorHealthModified},\
|
||||
{"SetActorMagickaBase", ActorFunctions::SetActorMagickaBase},\
|
||||
{"SetActorMagickaCurrent", ActorFunctions::SetActorMagickaCurrent},\
|
||||
{"SetActorMagickaModified", ActorFunctions::SetActorMagickaModified},\
|
||||
{"SetActorFatigueBase", ActorFunctions::SetActorFatigueBase},\
|
||||
{"SetActorFatigueCurrent", ActorFunctions::SetActorFatigueCurrent},\
|
||||
{"SetActorFatigueModified", ActorFunctions::SetActorFatigueModified},\
|
||||
\
|
||||
{"SetActorAIAction", ActorFunctions::SetActorAIAction},\
|
||||
{"SetActorAITargetToPlayer", ActorFunctions::SetActorAITargetToPlayer},\
|
||||
{"SetActorAITargetToActor", ActorFunctions::SetActorAITargetToActor},\
|
||||
\
|
||||
{"EquipActorItem", ActorFunctions::EquipActorItem},\
|
||||
{"UnequipActorItem", ActorFunctions::UnequipActorItem},\
|
||||
\
|
||||
{"AddActor", ActorFunctions::AddActor},\
|
||||
\
|
||||
{"SendActorList", ActorFunctions::SendActorList},\
|
||||
{"SendActorAuthority", ActorFunctions::SendActorAuthority},\
|
||||
{"SendActorPosition", ActorFunctions::SendActorPosition},\
|
||||
{"SendActorStatsDynamic", ActorFunctions::SendActorStatsDynamic},\
|
||||
{"SendActorEquipment", ActorFunctions::SendActorEquipment},\
|
||||
{"SendActorAI", ActorFunctions::SendActorAI},\
|
||||
{"SendActorCellChange", ActorFunctions::SendActorCellChange}
|
||||
|
||||
class ActorFunctions
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief Use the last actor list received by the server as the one being read.
|
||||
*
|
||||
* \return void
|
||||
*/
|
||||
static void ReadLastActorList() noexcept;
|
||||
|
||||
/**
|
||||
* \brief Use the temporary actor list stored for a cell as the one being read.
|
||||
*
|
||||
* This type of actor list is used to store actor positions and dynamic stats and is deleted
|
||||
* when the cell is unloaded.
|
||||
*
|
||||
* \param cellDescription The description of the cell whose actor list should be read.
|
||||
* \return void
|
||||
*/
|
||||
static void ReadCellActorList(const char* cellDescription) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Clear the data from the last actor list sent by the server.
|
||||
*
|
||||
* This is used to initialize the sending of new Actor packets.
|
||||
*
|
||||
* \param pid The player ID to whom the actor list should be attached.
|
||||
* \return void
|
||||
*/
|
||||
static void InitializeActorList(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the number of indexes in the read actor list.
|
||||
*
|
||||
* \return The number of indexes.
|
||||
*/
|
||||
static unsigned int GetActorListSize() noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the action type used in the read actor list.
|
||||
*
|
||||
* \return The action type (0 for SET, 1 for ADD, 2 for REMOVE, 3 for REQUEST).
|
||||
*/
|
||||
static unsigned char GetActorListAction() noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the cell description of the actor at a certain index in the read actor list.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \return The cell description.
|
||||
*/
|
||||
static const char *GetActorCell(unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the refId of the actor at a certain index in the read actor list.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \return The refId.
|
||||
*/
|
||||
static const char *GetActorRefId(unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the refNumIndex of the actor at a certain index in the read actor list.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \return The refNumIndex.
|
||||
*/
|
||||
static int GetActorRefNumIndex(unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the mpNum of the actor at a certain index in the read actor list.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \return The mpNum.
|
||||
*/
|
||||
static int GetActorMpNum(unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the X position of the actor at a certain index in the read actor list.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \return The X position.
|
||||
*/
|
||||
static double GetActorPosX(unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the Y position of the actor at a certain index in the read actor list.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \return The Y position.
|
||||
*/
|
||||
static double GetActorPosY(unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the Z position of the actor at a certain index in the read actor list.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \return The Z position.
|
||||
*/
|
||||
static double GetActorPosZ(unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the X rotation of the actor at a certain index in the read actor list.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \return The X rotation.
|
||||
*/
|
||||
static double GetActorRotX(unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the Y rotation of the actor at a certain index in the read actor list.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \return The Y rotation.
|
||||
*/
|
||||
static double GetActorRotY(unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the Z rotation of the actor at a certain index in the read actor list.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \return The Z rotation.
|
||||
*/
|
||||
static double GetActorRotZ(unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the base health of the actor at a certain index in the read actor list.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \return The base health.
|
||||
*/
|
||||
static double GetActorHealthBase(unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the current health of the actor at a certain index in the read actor list.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \return The current health.
|
||||
*/
|
||||
static double GetActorHealthCurrent(unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the modified health of the actor at a certain index in the read actor list.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \return The modified health.
|
||||
*/
|
||||
static double GetActorHealthModified(unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the base magicka of the actor at a certain index in the read actor list.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \return The base magicka.
|
||||
*/
|
||||
static double GetActorMagickaBase(unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the current magicka of the actor at a certain index in the read actor list.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \return The current magicka.
|
||||
*/
|
||||
static double GetActorMagickaCurrent(unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the modified magicka of the actor at a certain index in the read actor list.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \return The modified magicka.
|
||||
*/
|
||||
static double GetActorMagickaModified(unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the base fatigue of the actor at a certain index in the read actor list.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \return The base fatigue.
|
||||
*/
|
||||
static double GetActorFatigueBase(unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the current fatigue of the actor at a certain index in the read actor list.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \return The current fatigue.
|
||||
*/
|
||||
static double GetActorFatigueCurrent(unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the modified fatigue of the actor at a certain index in the read actor list.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \return The modified fatigue.
|
||||
*/
|
||||
static double GetActorFatigueModified(unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the refId of the item in a certain slot of the equipment of the actor at a
|
||||
* certain index in the read actor list.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \param slot The slot of the equipment item.
|
||||
* \return The refId.
|
||||
*/
|
||||
static const char *GetActorEquipmentItemRefId(unsigned int i, unsigned short slot) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the count of the item in a certain slot of the equipment of the actor at a
|
||||
* certain index in the read actor list.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \param slot The slot of the equipment item.
|
||||
* \return The item count.
|
||||
*/
|
||||
static int GetActorEquipmentItemCount(unsigned int i, unsigned short slot) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the charge of the item in a certain slot of the equipment of the actor at a
|
||||
* certain index in the read actor list.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \param slot The slot of the equipment item.
|
||||
* \return The charge.
|
||||
*/
|
||||
static int GetActorEquipmentItemCharge(unsigned int i, unsigned short slot) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the enchantment charge of the item in a certain slot of the equipment of the actor at a
|
||||
* certain index in the read actor list.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \param slot The slot of the equipment item.
|
||||
* \return The enchantment charge.
|
||||
*/
|
||||
static double GetActorEquipmentItemEnchantmentCharge(unsigned int i, unsigned short slot) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Check whether there is any positional data for the actor at a certain index in
|
||||
* the read actor list.
|
||||
*
|
||||
* This is only useful when reading the actor list data recorded for a particular cell.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \return Whether the read actor list contains positional data.
|
||||
*/
|
||||
static bool DoesActorHavePosition(unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Check whether there is any dynamic stats data for the actor at a certain index in
|
||||
* the read actor list.
|
||||
*
|
||||
* This is only useful when reading the actor list data recorded for a particular cell.
|
||||
*
|
||||
* \param i The index of the actor.
|
||||
* \return Whether the read actor list contains dynamic stats data.
|
||||
*/
|
||||
static bool DoesActorHaveStatsDynamic(unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the cell of the temporary actor list stored on the server.
|
||||
*
|
||||
* The cell is determined to be an exterior cell if it fits the pattern of a number followed
|
||||
* by a comma followed by another number.
|
||||
*
|
||||
* \param cellDescription The description of the cell.
|
||||
* \return void
|
||||
*/
|
||||
static void SetActorListCell(const char* cellDescription) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the action type of the temporary actor list stored on the server.
|
||||
*
|
||||
* \param action The action type (0 for SET, 1 for ADD, 2 for REMOVE, 3 for REQUEST).
|
||||
* \return void
|
||||
*/
|
||||
static void SetActorListAction(unsigned char action) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the cell of the temporary actor stored on the server.
|
||||
*
|
||||
* Used for ActorCellChange packets, where a specific actor's cell now differs from that of the
|
||||
* actor list.
|
||||
*
|
||||
* The cell is determined to be an exterior cell if it fits the pattern of a number followed
|
||||
* by a comma followed by another number.
|
||||
*
|
||||
* \param cellDescription The description of the cell.
|
||||
* \return void
|
||||
*/
|
||||
static void SetActorCell(const char* cellDescription) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the refId of the temporary actor stored on the server.
|
||||
*
|
||||
* \param refId The refId.
|
||||
* \return void
|
||||
*/
|
||||
static void SetActorRefId(const char* refId) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the refNumIndex of the temporary actor stored on the server.
|
||||
*
|
||||
* \param refNumIndex The refNumIndex.
|
||||
* \return void
|
||||
*/
|
||||
static void SetActorRefNumIndex(int refNumIndex) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the mpNum of the temporary actor stored on the server.
|
||||
*
|
||||
* \param mpNum The mpNum.
|
||||
* \return void
|
||||
*/
|
||||
static void SetActorMpNum(int mpNum) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the position of the temporary actor stored on the server.
|
||||
*
|
||||
* \param x The X position.
|
||||
* \param y The Y position.
|
||||
* \param z The Z position.
|
||||
* \return void
|
||||
*/
|
||||
static void SetActorPosition(double x, double y, double z) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the rotation of the temporary actor stored on the server.
|
||||
*
|
||||
* \param x The X rotation.
|
||||
* \param y The Y rotation.
|
||||
* \param z The Z rotation.
|
||||
* \return void
|
||||
*/
|
||||
static void SetActorRotation(double x, double y, double z) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the base health of the temporary actor stored on the server.
|
||||
*
|
||||
* \param value The new value.
|
||||
* \return void
|
||||
*/
|
||||
static void SetActorHealthBase(double value) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the current health of the temporary actor stored on the server.
|
||||
*
|
||||
* \param value The new value.
|
||||
* \return void
|
||||
*/
|
||||
static void SetActorHealthCurrent(double value) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the modified health of the temporary actor stored on the server.
|
||||
*
|
||||
* \param value The new value.
|
||||
* \return void
|
||||
*/
|
||||
static void SetActorHealthModified(double value) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the base magicka of the temporary actor stored on the server.
|
||||
*
|
||||
* \param value The new value.
|
||||
* \return void
|
||||
*/
|
||||
static void SetActorMagickaBase(double value) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the current magicka of the temporary actor stored on the server.
|
||||
*
|
||||
* \param value The new value.
|
||||
* \return void
|
||||
*/
|
||||
static void SetActorMagickaCurrent(double value) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the modified magicka of the temporary actor stored on the server.
|
||||
*
|
||||
* \param value The new value.
|
||||
* \return void
|
||||
*/
|
||||
static void SetActorMagickaModified(double value) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the base fatigue of the temporary actor stored on the server.
|
||||
*
|
||||
* \param value The new value.
|
||||
* \return void
|
||||
*/
|
||||
static void SetActorFatigueBase(double value) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the current fatigue of the temporary actor stored on the server.
|
||||
*
|
||||
* \param value The new value.
|
||||
* \return void
|
||||
*/
|
||||
static void SetActorFatigueCurrent(double value) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the modified fatigue of the temporary actor stored on the server.
|
||||
*
|
||||
* \param value The new value.
|
||||
* \return void
|
||||
*/
|
||||
static void SetActorFatigueModified(double value) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the AI action of the temporary actor stored on the server.
|
||||
*
|
||||
* \param action The new action.
|
||||
* \return void
|
||||
*/
|
||||
static void SetActorAIAction(unsigned int action) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set a player as the AI target of the temporary actor stored on the server.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \return void
|
||||
*/
|
||||
static void SetActorAITargetToPlayer(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set another actor as the AI target of the temporary actor stored on the server.
|
||||
*
|
||||
* \param refNumIndex The refNumIndex of the target actor.
|
||||
* \param mpNum The mpNum of the target actor.
|
||||
* \return void
|
||||
*/
|
||||
static void SetActorAITargetToActor(int refNumIndex, int mpNum) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Equip an item in a certain slot of the equipment of the temporary actor stored
|
||||
* on the server.
|
||||
*
|
||||
* \param slot The equipment slot.
|
||||
* \param refId The refId of the item.
|
||||
* \param count The count of the item.
|
||||
* \param charge The charge of the item.
|
||||
* \param enchantmentCharge The enchantment charge of the item.
|
||||
* \return void
|
||||
*/
|
||||
static void EquipActorItem(unsigned short slot, const char* refId, unsigned int count, int charge, double enchantmentCharge = -1) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Unequip the item in a certain slot of the equipment of the temporary actor stored
|
||||
* on the server.
|
||||
*
|
||||
* \param slot The equipment slot.
|
||||
* \return void
|
||||
*/
|
||||
static void UnequipActorItem(unsigned short slot) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Add a copy of the server's temporary actor to the server's temporary actor list.
|
||||
*
|
||||
* In the process, the server's temporary actor will automatically be cleared so a new
|
||||
* one can be set up.
|
||||
*
|
||||
* \return void
|
||||
*/
|
||||
static void AddActor() noexcept;
|
||||
|
||||
/**
|
||||
* \brief Send an ActorList packet.
|
||||
*
|
||||
* It is sent only to the player for whom the current actor list was initialized.
|
||||
*
|
||||
* \return void
|
||||
*/
|
||||
static void SendActorList() noexcept;
|
||||
|
||||
/**
|
||||
* \brief Send an ActorAuthority packet.
|
||||
*
|
||||
* The player for whom the current actor list was initialized is recorded in the server memory
|
||||
* as the new actor authority for the actor list's cell.
|
||||
*
|
||||
* The packet is sent to that player as well as all other players who have the cell loaded.
|
||||
*
|
||||
* \return void
|
||||
*/
|
||||
static void SendActorAuthority() noexcept;
|
||||
|
||||
/**
|
||||
* \brief Send an ActorPosition packet.
|
||||
*
|
||||
* It is sent only to the player for whom the current actor list was initialized.
|
||||
*
|
||||
* \return void
|
||||
*/
|
||||
static void SendActorPosition() noexcept;
|
||||
|
||||
/**
|
||||
* \brief Send an ActorStatsDynamic packet.
|
||||
*
|
||||
* It is sent only to the player for whom the current actor list was initialized.
|
||||
*
|
||||
* \return void
|
||||
*/
|
||||
static void SendActorStatsDynamic() noexcept;
|
||||
|
||||
/**
|
||||
* \brief Send an ActorEquipment packet.
|
||||
*
|
||||
* It is sent only to the player for whom the current actor list was initialized.
|
||||
*
|
||||
* \return void
|
||||
*/
|
||||
static void SendActorEquipment() noexcept;
|
||||
|
||||
/**
|
||||
* \brief Send an ActorAI packet.
|
||||
*
|
||||
* It is sent only to the player for whom the current actor list was initialized.
|
||||
*
|
||||
* \return void
|
||||
*/
|
||||
static void SendActorAI() noexcept;
|
||||
|
||||
/**
|
||||
* \brief Send an ActorCellChange packet.
|
||||
*
|
||||
* It is sent only to the player for whom the current actor list was initialized.
|
||||
*
|
||||
* \return void
|
||||
*/
|
||||
static void SendActorCellChange() noexcept;
|
||||
};
|
||||
|
||||
|
||||
#endif //OPENMW_ACTORAPI_HPP
|
|
@ -1,55 +0,0 @@
|
|||
#include "Books.hpp"
|
||||
|
||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
|
||||
#include <apps/openmw-mp/Script/ScriptFunctions.hpp>
|
||||
#include <apps/openmw-mp/Networking.hpp>
|
||||
|
||||
using namespace mwmp;
|
||||
|
||||
void BookFunctions::InitializeBookChanges(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, );
|
||||
|
||||
return player->bookChanges.books.clear();
|
||||
}
|
||||
|
||||
unsigned int BookFunctions::GetBookChangesSize(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
|
||||
return player->bookChanges.count;
|
||||
}
|
||||
|
||||
void BookFunctions::AddBook(unsigned short pid, const char* bookId) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, );
|
||||
|
||||
mwmp::Book book;
|
||||
book.bookId = bookId;
|
||||
|
||||
player->bookChanges.books.push_back(book);
|
||||
}
|
||||
|
||||
const char *BookFunctions::GetBookId(unsigned short pid, unsigned int i) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, "");
|
||||
|
||||
if (i >= player->bookChanges.count)
|
||||
return "invalid";
|
||||
|
||||
return player->bookChanges.books.at(i).bookId.c_str();
|
||||
}
|
||||
|
||||
void BookFunctions::SendBookChanges(unsigned short pid, bool toOthers) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, );
|
||||
|
||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_BOOK)->setPlayer(player);
|
||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_BOOK)->Send(toOthers);
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
#ifndef OPENMW_BOOKAPI_HPP
|
||||
#define OPENMW_BOOKAPI_HPP
|
||||
|
||||
#define BOOKAPI \
|
||||
{"InitializeBookChanges", BookFunctions::InitializeBookChanges},\
|
||||
\
|
||||
{"GetBookChangesSize", BookFunctions::GetBookChangesSize},\
|
||||
\
|
||||
{"AddBook", BookFunctions::AddBook},\
|
||||
\
|
||||
{"GetBookId", BookFunctions::GetBookId},\
|
||||
\
|
||||
{"SendBookChanges", BookFunctions::SendBookChanges}
|
||||
|
||||
class BookFunctions
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief Clear the last recorded book changes for a player.
|
||||
*
|
||||
* This is used to initialize the sending of new PlayerBook packets.
|
||||
*
|
||||
* \param pid The player ID whose book changes should be used.
|
||||
* \return void
|
||||
*/
|
||||
static void InitializeBookChanges(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the number of indexes in a player's latest book changes.
|
||||
*
|
||||
* \param pid The player ID whose book changes should be used.
|
||||
* \return The number of indexes.
|
||||
*/
|
||||
static unsigned int GetBookChangesSize(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Add a new book to the book changes for a player.
|
||||
*
|
||||
* \param pid The player ID whose book changes should be used.
|
||||
* \param bookId The bookId of the book.
|
||||
* \return void
|
||||
*/
|
||||
static void AddBook(unsigned short pid, const char* bookId) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the bookId at a certain index in a player's latest book changes.
|
||||
*
|
||||
* \param pid The player ID whose book changes should be used.
|
||||
* \param i The index of the book.
|
||||
* \return The bookId.
|
||||
*/
|
||||
static const char *GetBookId(unsigned short pid, unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Send a PlayerBook packet with a player's recorded book changes.
|
||||
*
|
||||
* \param pid The player ID whose book changes should be used.
|
||||
* \param toOthers Whether this packet should be sent only to other players or
|
||||
* only to the player it is about.
|
||||
* \return void
|
||||
*/
|
||||
static void SendBookChanges(unsigned short pid, bool toOthers = false) noexcept;
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
#endif //OPENMW_BOOKAPI_HPP
|
|
@ -1,125 +0,0 @@
|
|||
#include "Cells.hpp"
|
||||
|
||||
#include <components/openmw-mp/Log.hpp>
|
||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
|
||||
#include <apps/openmw-mp/Script/ScriptFunctions.hpp>
|
||||
#include <apps/openmw-mp/Player.hpp>
|
||||
#include <apps/openmw-mp/Networking.hpp>
|
||||
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
static std::string tempCellDescription;
|
||||
|
||||
unsigned int CellFunctions::GetCellStateChangesSize(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
|
||||
return player->cellStateChanges.count;
|
||||
}
|
||||
|
||||
unsigned int CellFunctions::GetCellStateType(unsigned short pid, unsigned int i) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
|
||||
return player->cellStateChanges.cellStates.at(i).type;
|
||||
}
|
||||
|
||||
const char *CellFunctions::GetCellStateDescription(unsigned short pid, unsigned int i) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, "");
|
||||
|
||||
if (i >= player->cellStateChanges.count)
|
||||
return "invalid";
|
||||
|
||||
tempCellDescription = player->cellStateChanges.cellStates.at(i).cell.getDescription();
|
||||
return tempCellDescription.c_str();
|
||||
}
|
||||
|
||||
const char *CellFunctions::GetCell(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
|
||||
tempCellDescription = player->cell.getDescription().c_str();
|
||||
return tempCellDescription.c_str();
|
||||
}
|
||||
|
||||
int CellFunctions::GetExteriorX(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
return player->cell.mData.mX;
|
||||
}
|
||||
|
||||
int CellFunctions::GetExteriorY(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
return player->cell.mData.mY;
|
||||
}
|
||||
|
||||
bool CellFunctions::IsInExterior(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, false);
|
||||
|
||||
return player->cell.isExterior();
|
||||
}
|
||||
|
||||
const char *CellFunctions::GetRegion(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
|
||||
return player->cell.mRegion.c_str();
|
||||
}
|
||||
|
||||
bool CellFunctions::IsChangingRegion(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, false);
|
||||
|
||||
return player->isChangingRegion;
|
||||
}
|
||||
|
||||
void CellFunctions::SetCell(unsigned short pid, const char *cellDescription) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player,);
|
||||
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Script is moving %s from %s to %s", player->npc.mName.c_str(),
|
||||
player->cell.getDescription().c_str(), cellDescription);
|
||||
|
||||
player->cell = Utils::getCellFromDescription(cellDescription);
|
||||
}
|
||||
|
||||
void CellFunctions::SetExteriorCell(unsigned short pid, int x, int y) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player,);
|
||||
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Script is moving %s from %s to %i,%i", player->npc.mName.c_str(),
|
||||
player->cell.getDescription().c_str(), x, y);
|
||||
|
||||
// If the player is currently in an interior, turn off the interior flag
|
||||
// from the cell
|
||||
if (!player->cell.isExterior())
|
||||
player->cell.mData.mFlags &= ~ESM::Cell::Interior;
|
||||
|
||||
player->cell.mData.mX = x;
|
||||
player->cell.mData.mY = y;
|
||||
}
|
||||
|
||||
void CellFunctions::SendCell(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, );
|
||||
|
||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_CELL_CHANGE)->setPlayer(player);
|
||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_CELL_CHANGE)->Send(false);
|
||||
}
|
|
@ -1,146 +0,0 @@
|
|||
#ifndef OPENMW_CELLAPI_HPP
|
||||
#define OPENMW_CELLAPI_HPP
|
||||
|
||||
#include "../Types.hpp"
|
||||
|
||||
#define CELLAPI \
|
||||
{"GetCellStateChangesSize", CellFunctions::GetCellStateChangesSize},\
|
||||
\
|
||||
{"GetCellStateType", CellFunctions::GetCellStateType},\
|
||||
{"GetCellStateDescription", CellFunctions::GetCellStateDescription},\
|
||||
\
|
||||
{"GetCell", CellFunctions::GetCell},\
|
||||
{"GetExteriorX", CellFunctions::GetExteriorX},\
|
||||
{"GetExteriorY", CellFunctions::GetExteriorY},\
|
||||
{"IsInExterior", CellFunctions::IsInExterior},\
|
||||
\
|
||||
{"GetRegion", CellFunctions::GetRegion},\
|
||||
{"IsChangingRegion", CellFunctions::IsChangingRegion},\
|
||||
\
|
||||
{"SetCell", CellFunctions::SetCell},\
|
||||
{"SetExteriorCell", CellFunctions::SetExteriorCell},\
|
||||
\
|
||||
{"SendCell", CellFunctions::SendCell}
|
||||
|
||||
|
||||
class CellFunctions
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief Get the number of indexes in a player's latest cell state changes.
|
||||
*
|
||||
* \param pid The player ID whose cell state changes should be used.
|
||||
* \return The number of indexes.
|
||||
*/
|
||||
static unsigned int GetCellStateChangesSize(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the cell state type at a certain index in a player's latest cell state changes.
|
||||
*
|
||||
* \param pid The player ID whose cell state changes should be used.
|
||||
* \param i The index of the cell state.
|
||||
* \return The cell state type (0 for LOAD, 1 for UNLOAD).
|
||||
*/
|
||||
static unsigned int GetCellStateType(unsigned short pid, unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the cell description at a certain index in a player's latest cell state changes.
|
||||
*
|
||||
* \param pid The player ID whose cell state changes should be used.
|
||||
* \param i The index of the cell state.
|
||||
* \return The cell description.
|
||||
*/
|
||||
static const char *GetCellStateDescription(unsigned short pid, unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the cell description of a player's cell.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \return The cell description.
|
||||
*/
|
||||
static const char *GetCell(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the X coordinate of the player's exterior cell.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \return The X coordinate of the cell.
|
||||
*/
|
||||
static int GetExteriorX(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the Y coordinate of the player's exterior cell.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \return The Y coordinate of the cell.
|
||||
*/
|
||||
static int GetExteriorY(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Check whether the player is in an exterior cell or not.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \return Whether the player is in an exterior cell.
|
||||
*/
|
||||
static bool IsInExterior(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the region of the player's exterior cell.
|
||||
*
|
||||
* A blank value will be returned if the player is in an interior.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \return The region.
|
||||
*/
|
||||
static const char *GetRegion(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Check whether the player's last cell change has involved a region change.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \return Whether the player has changed their region.
|
||||
*/
|
||||
static bool IsChangingRegion(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the cell of a player.
|
||||
*
|
||||
* This changes the cell recorded for that player in the server memory, but does not by itself
|
||||
* send a packet.
|
||||
*
|
||||
* The cell is determined to be an exterior cell if it fits the pattern of a number followed
|
||||
* by a comma followed by another number.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \param cellDescription The cell description.
|
||||
* \return void
|
||||
*/
|
||||
static void SetCell(unsigned short pid, const char *cellDescription) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the cell of a player to an exterior cell.
|
||||
*
|
||||
* This changes the cell recorded for that player in the server memory, but does not by itself
|
||||
* send a packet.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \param x The X coordinate of the cell.
|
||||
* \param y The Y coordinate of the cell.
|
||||
* \return void
|
||||
*/
|
||||
static void SetExteriorCell(unsigned short pid, int x, int y) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Send a PlayerCellChange packet about a player.
|
||||
*
|
||||
* It is only sent to the affected player.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \return void
|
||||
*/
|
||||
static void SendCell(unsigned short pid) noexcept;
|
||||
|
||||
};
|
||||
|
||||
#endif //OPENMW_CELLAPI_HPP
|
|
@ -1,137 +0,0 @@
|
|||
//
|
||||
// Created by koncord on 29.08.16.
|
||||
//
|
||||
|
||||
#include "CharClass.hpp"
|
||||
|
||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
|
||||
#include <apps/openmw-mp/Networking.hpp>
|
||||
#include <apps/openmw-mp/Script/ScriptFunctions.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace ESM;
|
||||
|
||||
const char *CharClassFunctions::GetDefaultClass(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, "");
|
||||
return player->charClass.mId.c_str();
|
||||
}
|
||||
|
||||
const char *CharClassFunctions::GetClassName(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, "");
|
||||
return player->charClass.mName.c_str();
|
||||
}
|
||||
|
||||
const char *CharClassFunctions::GetClassDesc(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, "");
|
||||
return player->charClass.mDescription.c_str();
|
||||
}
|
||||
|
||||
int CharClassFunctions::GetClassMajorAttribute(unsigned short pid, unsigned char slot) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
if (slot > 1)
|
||||
throw invalid_argument("Incorrect attribute slot id");
|
||||
return player->charClass.mData.mAttribute[slot];
|
||||
}
|
||||
|
||||
int CharClassFunctions::GetClassSpecialization(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
return player->charClass.mData.mSpecialization;
|
||||
}
|
||||
|
||||
int CharClassFunctions::GetClassMajorSkill(unsigned short pid, unsigned char slot) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
if (slot > 4)
|
||||
throw invalid_argument("Incorrect skill slot id");
|
||||
return player->charClass.mData.mSkills[slot][1];
|
||||
}
|
||||
|
||||
int CharClassFunctions::GetClassMinorSkill(unsigned short pid, unsigned char slot) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
if (slot > 4)
|
||||
throw invalid_argument("Incorrect skill slot id");
|
||||
return player->charClass.mData.mSkills[slot][0];
|
||||
}
|
||||
|
||||
int CharClassFunctions::IsClassDefault(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
return !player->charClass.mId.empty(); // true if default
|
||||
}
|
||||
|
||||
void CharClassFunctions::SetDefaultClass(unsigned short pid, const char *id) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player,);
|
||||
player->charClass.mId = id;
|
||||
}
|
||||
void CharClassFunctions::SetClassName(unsigned short pid, const char *name) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player,);
|
||||
player->charClass.mName = name;
|
||||
player->charClass.mId = "";
|
||||
}
|
||||
void CharClassFunctions::SetClassDesc(unsigned short pid, const char *desc) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player,);
|
||||
player->charClass.mDescription = desc;
|
||||
}
|
||||
void CharClassFunctions::SetClassMajorAttribute(unsigned short pid, unsigned char slot, int attrId) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player,);
|
||||
|
||||
if (slot > 1)
|
||||
throw invalid_argument("Incorrect attribute slot id");
|
||||
|
||||
player->charClass.mData.mAttribute[slot] = attrId;
|
||||
|
||||
}
|
||||
void CharClassFunctions::SetClassSpecialization(unsigned short pid, int spec) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player,);
|
||||
player->charClass.mData.mSpecialization = spec;
|
||||
}
|
||||
void CharClassFunctions::SetClassMajorSkill(unsigned short pid, unsigned char slot, int skillId) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player,);
|
||||
if (slot > 4)
|
||||
throw invalid_argument("Incorrect skill slot id");
|
||||
player->charClass.mData.mSkills[slot][1] = skillId;
|
||||
}
|
||||
void CharClassFunctions::SetClassMinorSkill(unsigned short pid, unsigned char slot, int skillId) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player,);
|
||||
if (slot > 4)
|
||||
throw invalid_argument("Incorrect skill slot id");
|
||||
player->charClass.mData.mSkills[slot][0] = skillId;
|
||||
}
|
||||
|
||||
void CharClassFunctions::SendClass(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, );
|
||||
|
||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_CHARCLASS)->setPlayer(player);
|
||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_CHARCLASS)->Send(false);
|
||||
}
|
|
@ -1,181 +0,0 @@
|
|||
//
|
||||
// Created by koncord on 29.08.16.
|
||||
//
|
||||
|
||||
#ifndef OPENMW_CHARCLASSAPI_HPP
|
||||
#define OPENMW_CHARCLASSAPI_HPP
|
||||
|
||||
#include "../Types.hpp"
|
||||
|
||||
#define CHARCLASSAPI \
|
||||
{"GetDefaultClass", CharClassFunctions::GetDefaultClass},\
|
||||
{"GetClassName", CharClassFunctions::GetClassName},\
|
||||
{"GetClassDesc", CharClassFunctions::GetClassDesc},\
|
||||
{"GetClassMajorAttribute", CharClassFunctions::GetClassMajorAttribute},\
|
||||
{"GetClassSpecialization", CharClassFunctions::GetClassSpecialization},\
|
||||
{"GetClassMajorSkill", CharClassFunctions::GetClassMajorSkill},\
|
||||
{"GetClassMinorSkill", CharClassFunctions::GetClassMinorSkill},\
|
||||
{"IsClassDefault", CharClassFunctions::IsClassDefault},\
|
||||
\
|
||||
{"SetDefaultClass", CharClassFunctions::SetDefaultClass},\
|
||||
{"SetClassName", CharClassFunctions::SetClassName},\
|
||||
{"SetClassDesc", CharClassFunctions::SetClassDesc},\
|
||||
{"SetClassMajorAttribute", CharClassFunctions::SetClassMajorAttribute},\
|
||||
{"SetClassSpecialization", CharClassFunctions::SetClassSpecialization},\
|
||||
{"SetClassMajorSkill", CharClassFunctions::SetClassMajorSkill},\
|
||||
{"SetClassMinorSkill", CharClassFunctions::SetClassMinorSkill},\
|
||||
\
|
||||
{"SendClass", CharClassFunctions::SendClass}
|
||||
|
||||
|
||||
class CharClassFunctions
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief Get the default class used by a player.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \return The ID of the default class.
|
||||
*/
|
||||
static const char *GetDefaultClass(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the name of the custom class used by a player.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \return The name of the custom class.
|
||||
*/
|
||||
static const char *GetClassName(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the description of the custom class used by a player.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \return The description of the custom class.
|
||||
*/
|
||||
static const char *GetClassDesc(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the ID of one of the two major attributes of a custom class used by a player.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \param slot The slot of the major attribute (0 or 1).
|
||||
* \return The ID of the major attribute.
|
||||
*/
|
||||
static int GetClassMajorAttribute(unsigned short pid, unsigned char slot) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the specialization ID of the custom class used by a player.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \return The specialization ID of the custom class (0 for Combat, 1 for Magic, 2 for Stealth).
|
||||
*/
|
||||
static int GetClassSpecialization(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the ID of one of the five major skills of a custom class used by a player.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \param slot The slot of the major skill (0 to 4).
|
||||
* \return The ID of the major skill.
|
||||
*/
|
||||
static int GetClassMajorSkill(unsigned short pid, unsigned char slot) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the ID of one of the five minor skills of a custom class used by a player.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \param slot The slot of the minor skill (0 to 4).
|
||||
* \return The ID of the minor skill.
|
||||
*/
|
||||
static int GetClassMinorSkill(unsigned short pid, unsigned char slot) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Check whether the player is using a default class instead of a custom one.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \return Whether the player is using a default class.
|
||||
*/
|
||||
static int IsClassDefault(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the default class used by a player.
|
||||
*
|
||||
* If this is left blank, the custom class data set for the player will be used instead.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \param id The ID of the default class.
|
||||
* \return void
|
||||
*/
|
||||
static void SetDefaultClass(unsigned short pid, const char *id) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the name of the custom class used by a player.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \param name The name of the custom class.
|
||||
* \return void
|
||||
*/
|
||||
static void SetClassName(unsigned short pid, const char *name) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the description of the custom class used by a player.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \param desc The description of the custom class.
|
||||
* \return void
|
||||
*/
|
||||
static void SetClassDesc(unsigned short pid, const char *desc) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the ID of one of the two major attributes of the custom class used by a player.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \param slot The slot of the major attribute (0 or 1).
|
||||
* \param attrId The ID to use for the attribute.
|
||||
* \return void
|
||||
*/
|
||||
static void SetClassMajorAttribute(unsigned short pid, unsigned char slot, int attrId) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the specialization of the custom class used by a player.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \param spec The specialization ID to use (0 for Combat, 1 for Magic, 2 for Stealth).
|
||||
* \return void
|
||||
*/
|
||||
static void SetClassSpecialization(unsigned short pid, int spec) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the ID of one of the five major skills of the custom class used by a player.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \param slot The slot of the major skill (0 to 4).
|
||||
* \param skillId The ID to use for the skill.
|
||||
* \return void
|
||||
*/
|
||||
static void SetClassMajorSkill(unsigned short pid, unsigned char slot, int skillId) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Set the ID of one of the five minor skills of the custom class used by a player.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \param slot The slot of the minor skill (0 to 4).
|
||||
* \param skillId The ID to use for the skill.
|
||||
* \return void
|
||||
*/
|
||||
static void SetClassMinorSkill(unsigned short pid, unsigned char slot, int skillId) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Send a PlayerCharClass packet about a player.
|
||||
*
|
||||
* It is only sent to the affected player.
|
||||
*
|
||||
* \param pid The player ID.
|
||||
* \return void
|
||||
*/
|
||||
static void SendClass(unsigned short pid) noexcept;
|
||||
};
|
||||
|
||||
#endif //OPENMW_CHARCLASSAPI_HPP
|
|
@ -1,47 +0,0 @@
|
|||
//
|
||||
// Created by koncord on 29.04.16.
|
||||
//
|
||||
|
||||
#include <apps/openmw-mp/Script/ScriptFunctions.hpp>
|
||||
#include <apps/openmw-mp/Networking.hpp>
|
||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
|
||||
void ScriptFunctions::SendMessage(unsigned short pid, const char *message, bool broadcast) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player,);
|
||||
|
||||
player->chatMessage = message;
|
||||
|
||||
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "System: %s", message);
|
||||
|
||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_CHAT_MESSAGE)->setPlayer(player);
|
||||
|
||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_CHAT_MESSAGE)->Send(false);
|
||||
|
||||
if (broadcast)
|
||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_CHAT_MESSAGE)->Send(true);
|
||||
}
|
||||
|
||||
void ScriptFunctions::CleanChatForPid(unsigned short pid)
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player,);
|
||||
|
||||
player->chatMessage.clear();
|
||||
|
||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_CHAT_MESSAGE)->setPlayer(player);
|
||||
|
||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_CHAT_MESSAGE)->Send(false);
|
||||
}
|
||||
|
||||
void ScriptFunctions::CleanChat()
|
||||
{
|
||||
for (auto player : *Players::getPlayers())
|
||||
{
|
||||
player.second->chatMessage.clear();
|
||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_CHAT_MESSAGE)->setPlayer(player.second);
|
||||
|
||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_CHAT_MESSAGE)->Send(false);
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
#include "Dialogue.hpp"
|
||||
|
||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||
|
||||
#include <apps/openmw-mp/Script/ScriptFunctions.hpp>
|
||||
#include <apps/openmw-mp/Networking.hpp>
|
||||
|
||||
using namespace mwmp;
|
||||
|
||||
void DialogueFunctions::InitializeTopicChanges(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, );
|
||||
|
||||
player->topicChanges.topics.clear();
|
||||
}
|
||||
|
||||
unsigned int DialogueFunctions::GetTopicChangesSize(unsigned short pid) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, 0);
|
||||
|
||||
return player->topicChanges.count;
|
||||
}
|
||||
|
||||
void DialogueFunctions::AddTopic(unsigned short pid, const char* topicId) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, );
|
||||
|
||||
mwmp::Topic topic;
|
||||
topic.topicId = topicId;
|
||||
|
||||
player->topicChanges.topics.push_back(topic);
|
||||
}
|
||||
|
||||
const char *DialogueFunctions::GetTopicId(unsigned short pid, unsigned int i) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, "");
|
||||
|
||||
if (i >= player->topicChanges.count)
|
||||
return "invalid";
|
||||
|
||||
return player->topicChanges.topics.at(i).topicId.c_str();
|
||||
}
|
||||
|
||||
void DialogueFunctions::SendTopicChanges(unsigned short pid, bool toOthers) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, );
|
||||
|
||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_TOPIC)->setPlayer(player);
|
||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_TOPIC)->Send(toOthers);
|
||||
}
|
||||
|
||||
void DialogueFunctions::PlayAnimation(unsigned short pid, const char* groupname, int mode, int count, bool persist) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, );
|
||||
|
||||
player->animation.groupname = groupname;
|
||||
player->animation.mode = mode;
|
||||
player->animation.count = count;
|
||||
player->animation.persist = persist;
|
||||
|
||||
mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_ANIM_PLAY);
|
||||
packet->setPlayer(player);
|
||||
packet->Send(false);
|
||||
player->sendToLoaded(packet);
|
||||
}
|
||||
|
||||
void DialogueFunctions::PlaySpeech(unsigned short pid, const char* sound) noexcept
|
||||
{
|
||||
Player *player;
|
||||
GET_PLAYER(pid, player, );
|
||||
|
||||
player->sound = sound;
|
||||
|
||||
mwmp::PlayerPacket *packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_SPEECH);
|
||||
packet->setPlayer(player);
|
||||
packet->Send(false);
|
||||
player->sendToLoaded(packet);
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
#ifndef OPENMW_DIALOGUEAPI_HPP
|
||||
#define OPENMW_DIALOGUEAPI_HPP
|
||||
|
||||
#define DIALOGUEAPI \
|
||||
{"InitializeTopicChanges", DialogueFunctions::InitializeTopicChanges},\
|
||||
\
|
||||
{"GetTopicChangesSize", DialogueFunctions::GetTopicChangesSize},\
|
||||
\
|
||||
{"AddTopic", DialogueFunctions::AddTopic},\
|
||||
\
|
||||
{"GetTopicId", DialogueFunctions::GetTopicId},\
|
||||
\
|
||||
{"SendTopicChanges", DialogueFunctions::SendTopicChanges},\
|
||||
\
|
||||
{"PlayAnimation", DialogueFunctions::PlayAnimation},\
|
||||
{"PlaySpeech", DialogueFunctions::PlaySpeech}
|
||||
|
||||
class DialogueFunctions
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief Clear the last recorded topic changes for a player.
|
||||
*
|
||||
* This is used to initialize the sending of new PlayerTopic packets.
|
||||
*
|
||||
* \param pid The player ID whose topic changes should be used.
|
||||
* \return void
|
||||
*/
|
||||
static void InitializeTopicChanges(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the number of indexes in a player's latest topic changes.
|
||||
*
|
||||
* \param pid The player ID whose topic changes should be used.
|
||||
* \return The number of indexes.
|
||||
*/
|
||||
static unsigned int GetTopicChangesSize(unsigned short pid) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Add a new topic to the topic changes for a player.
|
||||
*
|
||||
* \param pid The player ID whose topic changes should be used.
|
||||
* \param topicId The topicId of the topic.
|
||||
* \return void
|
||||
*/
|
||||
static void AddTopic(unsigned short pid, const char* topicId) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Get the topicId at a certain index in a player's latest topic changes.
|
||||
*
|
||||
* \param pid The player ID whose topic changes should be used.
|
||||
* \param i The index of the topic.
|
||||
* \return The topicId.
|
||||
*/
|
||||
static const char *GetTopicId(unsigned short pid, unsigned int i) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Send a PlayerTopic packet with a player's recorded topic changes.
|
||||
*
|
||||
* \param pid The player ID whose topic changes should be used.
|
||||
* \param toOthers Whether this packet should be sent only to other players or
|
||||
* only to the player it is about.
|
||||
* \return void
|
||||
*/
|
||||
static void SendTopicChanges(unsigned short pid, bool toOthers = false) noexcept;
|
||||
|
||||
static void PlayAnimation(unsigned short pid, const char* groupname, int mode = 0, int count = 1, bool persist = false) noexcept;
|
||||
static void PlaySpeech(unsigned short pid, const char* sound) noexcept;
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
#endif //OPENMW_DIALOGUEAPI_HPP
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue