forked from teamnwah/openmw-tes3coop
Compare commits
153 commits
0.7.0
...
new-script
Author | SHA1 | Date | |
---|---|---|---|
|
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 |
346 changed files with 10029 additions and 33656 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,3 +1,6 @@
|
||||||
[submodule "extern/breakpad"]
|
[submodule "extern/breakpad"]
|
||||||
path = extern/breakpad
|
path = extern/breakpad
|
||||||
url = https://chromium.googlesource.com/breakpad/breakpad
|
url = https://chromium.googlesource.com/breakpad/breakpad
|
||||||
|
[submodule "extern/sol"]
|
||||||
|
path = extern/sol
|
||||||
|
url = https://github.com/ThePhD/sol2
|
||||||
|
|
|
@ -213,7 +213,6 @@ endif()
|
||||||
IF(BUILD_OPENMW OR BUILD_OPENCS)
|
IF(BUILD_OPENMW OR BUILD_OPENCS)
|
||||||
|
|
||||||
find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgParticle osgUtil osgFX)
|
find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgParticle osgUtil osgFX)
|
||||||
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
|
|
||||||
|
|
||||||
set(USED_OSG_PLUGINS
|
set(USED_OSG_PLUGINS
|
||||||
osgdb_bmp
|
osgdb_bmp
|
||||||
|
@ -257,6 +256,8 @@ IF(BUILD_OPENMW OR BUILD_OPENCS)
|
||||||
|
|
||||||
ENDIF(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)
|
set(BOOST_COMPONENTS system filesystem program_options)
|
||||||
if(WIN32)
|
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 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.1
|
* TES3MP version: 0.7-alpha
|
||||||
* OpenMW version: 0.43.0
|
* OpenMW version: 0.43.0
|
||||||
* License: GPLv3 (see [LICENSE](https://github.com/TES3MP/openmw-tes3mp/blob/master/LICENSE) for more information)
|
* License: GPLv3 (see [LICENSE](https://github.com/TES3MP/openmw-tes3mp/blob/master/LICENSE) for more information)
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,7 @@ MainWindow::MainWindow(QWidget *parent)
|
||||||
connect(tblServerBrowser, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(play()));
|
connect(tblServerBrowser, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(play()));
|
||||||
connect(cBoxNotFull, SIGNAL(toggled(bool)), this, SLOT(notFullSwitch(bool)));
|
connect(cBoxNotFull, SIGNAL(toggled(bool)), this, SLOT(notFullSwitch(bool)));
|
||||||
connect(cBoxWithPlayers, SIGNAL(toggled(bool)), this, SLOT(havePlayersSwitch(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(comboLatency, SIGNAL(currentIndexChanged(int)), this, SLOT(maxLatencyChanged(int)));
|
||||||
connect(leGamemode, SIGNAL(textChanged(const QString &)), this, SLOT(gamemodeChanged(const QString &)));
|
connect(leGamemode, SIGNAL(textChanged(const QString &)), this, SLOT(gamemodeChanged(const QString &)));
|
||||||
loadFavorites();
|
loadFavorites();
|
||||||
|
@ -65,7 +66,7 @@ MainWindow::~MainWindow()
|
||||||
delete mGameInvoker;
|
delete mGameInvoker;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::addServerAndUpdate(QString addr)
|
void MainWindow::addServerAndUpdate(const QString &addr)
|
||||||
{
|
{
|
||||||
favorites->insertRow(0);
|
favorites->insertRow(0);
|
||||||
QModelIndex mi = favorites->index(0, ServerData::ADDR);
|
QModelIndex mi = favorites->index(0, ServerData::ADDR);
|
||||||
|
@ -142,7 +143,8 @@ void MainWindow::play()
|
||||||
if (sm->myData[sourceId].GetPassword() == 1)
|
if (sm->myData[sourceId].GetPassword() == 1)
|
||||||
{
|
{
|
||||||
bool ok;
|
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)
|
if (!ok)
|
||||||
return;
|
return;
|
||||||
arguments.append(QLatin1String("--password=") + passw.toLatin1());
|
arguments.append(QLatin1String("--password=") + passw.toLatin1());
|
||||||
|
@ -228,6 +230,11 @@ void MainWindow::havePlayersSwitch(bool state)
|
||||||
proxyModel->filterEmptyServers(state);
|
proxyModel->filterEmptyServers(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::noPasswordSwitch(bool state)
|
||||||
|
{
|
||||||
|
proxyModel->filterPassworded(state);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::maxLatencyChanged(int index)
|
void MainWindow::maxLatencyChanged(int index)
|
||||||
{
|
{
|
||||||
int maxLatency = index * 50;
|
int maxLatency = index * 50;
|
||||||
|
|
|
@ -17,11 +17,11 @@ class MainWindow : public QMainWindow, private Ui::MainWindow
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit MainWindow(QWidget *parent = 0);
|
explicit MainWindow(QWidget *parent = nullptr);
|
||||||
virtual ~MainWindow();
|
~MainWindow() override;
|
||||||
protected:
|
protected:
|
||||||
void closeEvent(QCloseEvent * event) Q_DECL_OVERRIDE;
|
void closeEvent(QCloseEvent * event) Q_DECL_OVERRIDE;
|
||||||
void addServerAndUpdate(QString addr);
|
void addServerAndUpdate(const QString &addr);
|
||||||
protected slots:
|
protected slots:
|
||||||
void tabSwitched(int index);
|
void tabSwitched(int index);
|
||||||
void addServer();
|
void addServer();
|
||||||
|
@ -31,6 +31,7 @@ protected slots:
|
||||||
void serverSelected();
|
void serverSelected();
|
||||||
void notFullSwitch(bool state);
|
void notFullSwitch(bool state);
|
||||||
void havePlayersSwitch(bool state);
|
void havePlayersSwitch(bool state);
|
||||||
|
void noPasswordSwitch(bool state);
|
||||||
void maxLatencyChanged(int index);
|
void maxLatencyChanged(int index);
|
||||||
void gamemodeChanged(const QString &text);
|
void gamemodeChanged(const QString &text);
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "ServerModel.hpp"
|
#include "ServerModel.hpp"
|
||||||
|
|
||||||
#include <qdebug.h>
|
#include <qdebug.h>
|
||||||
|
#include <apps/browser/netutils/Utils.hpp>
|
||||||
|
|
||||||
bool MySortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
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 pingIndex = sourceModel()->index(sourceRow, ServerData::PING, sourceParent);
|
||||||
QModelIndex plIndex = sourceModel()->index(sourceRow, ServerData::PLAYERS, sourceParent);
|
QModelIndex plIndex = sourceModel()->index(sourceRow, ServerData::PLAYERS, sourceParent);
|
||||||
QModelIndex maxPlIndex = sourceModel()->index(sourceRow, ServerData::MAX_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 players = sourceModel()->data(plIndex).toInt();
|
||||||
int maxPlayers = sourceModel()->data(maxPlIndex).toInt();
|
int maxPlayers = sourceModel()->data(maxPlIndex).toInt();
|
||||||
|
|
||||||
if (maxPing > 0 && (ping == -1 || ping > maxPing))
|
if (maxPing > 0 && (ping == -1 || ping > maxPing || !pingOk))
|
||||||
return false;
|
return false;
|
||||||
if (filterEmpty && players == 0)
|
if (filterEmpty && players == 0)
|
||||||
return false;
|
return false;
|
||||||
if (filterFull && players >= maxPlayers)
|
if (filterFull && players >= maxPlayers)
|
||||||
return false;
|
return false;
|
||||||
|
if(filterPasswEnabled && sourceModel()->data(passwordIndex).toString() == "Yes")
|
||||||
|
return false;
|
||||||
|
|
||||||
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
|
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)
|
MySortFilterProxyModel::MySortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent)
|
||||||
{
|
{
|
||||||
filterEmpty = false;
|
filterEmpty = false;
|
||||||
filterFull = false;
|
filterFull = false;
|
||||||
|
filterPasswEnabled = false;
|
||||||
maxPing = 0;
|
maxPing = 0;
|
||||||
|
setSortCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MySortFilterProxyModel::filterEmptyServers(bool state)
|
void MySortFilterProxyModel::filterEmptyServers(bool state)
|
||||||
|
@ -52,3 +76,9 @@ void MySortFilterProxyModel::pingLessThan(int maxPing)
|
||||||
this->maxPing = maxPing;
|
this->maxPing = maxPing;
|
||||||
invalidateFilter();
|
invalidateFilter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MySortFilterProxyModel::filterPassworded(bool state)
|
||||||
|
{
|
||||||
|
filterPasswEnabled = state;
|
||||||
|
invalidateFilter();
|
||||||
|
}
|
||||||
|
|
|
@ -13,13 +13,15 @@ class MySortFilterProxyModel : public QSortFilterProxyModel
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
protected:
|
protected:
|
||||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const Q_DECL_FINAL;
|
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:
|
public:
|
||||||
MySortFilterProxyModel(QObject *parent);
|
explicit MySortFilterProxyModel(QObject *parent);
|
||||||
void filterFullServer(bool state);
|
void filterFullServer(bool state);
|
||||||
void filterEmptyServers(bool state);
|
void filterEmptyServers(bool state);
|
||||||
|
void filterPassworded(bool state);
|
||||||
void pingLessThan(int maxPing);
|
void pingLessThan(int maxPing);
|
||||||
private:
|
private:
|
||||||
bool filterEmpty, filterFull;
|
bool filterEmpty, filterFull, filterPasswEnabled;
|
||||||
int maxPing;
|
int maxPing;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include "PingUpdater.hpp"
|
#include "PingUpdater.hpp"
|
||||||
|
|
||||||
void PingHelper::Add(int row, AddrPair addrPair)
|
void PingHelper::Add(int row, const AddrPair &addrPair)
|
||||||
{
|
{
|
||||||
pingUpdater->addServer(row, addrPair);
|
pingUpdater->addServer(row, addrPair);
|
||||||
if (!pingThread->isRunning())
|
if (!pingThread->isRunning())
|
||||||
|
@ -35,9 +35,8 @@ PingHelper &PingHelper::Get()
|
||||||
return helper;
|
return helper;
|
||||||
}
|
}
|
||||||
|
|
||||||
PingHelper::PingHelper()
|
PingHelper::PingHelper() : QObject()
|
||||||
{
|
{
|
||||||
QObject();
|
|
||||||
pingThread = new QThread;
|
pingThread = new QThread;
|
||||||
pingUpdater = new PingUpdater;
|
pingUpdater = new PingUpdater;
|
||||||
pingUpdater->moveToThread(pingThread);
|
pingUpdater->moveToThread(pingThread);
|
||||||
|
@ -48,11 +47,4 @@ PingHelper::PingHelper()
|
||||||
connect(this, SIGNAL(stop()), pingUpdater, SLOT(stop()));
|
connect(this, SIGNAL(stop()), pingUpdater, SLOT(stop()));
|
||||||
//connect(pingUpdater, SIGNAL(finished()), pingUpdater, SLOT(deleteLater()));
|
//connect(pingUpdater, SIGNAL(finished()), pingUpdater, SLOT(deleteLater()));
|
||||||
connect(pingUpdater, SIGNAL(updateModel(int, unsigned)), this, SLOT(update(int, unsigned)));
|
connect(pingUpdater, SIGNAL(updateModel(int, unsigned)), this, SLOT(update(int, unsigned)));
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
PingHelper::~PingHelper()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,17 +17,16 @@ class PingHelper : public QObject
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
|
||||||
void Add(int row, AddrPair addrPair);
|
void Add(int row, const AddrPair &addrPair);
|
||||||
void Stop();
|
void Stop();
|
||||||
void SetModel(QAbstractTableModel *model);
|
void SetModel(QAbstractTableModel *model);
|
||||||
//void UpdateImmedialy(PingUpdater::AddrPair addrPair);
|
//void UpdateImmedialy(PingUpdater::AddrPair addrPair);
|
||||||
static PingHelper &Get();
|
static PingHelper &Get();
|
||||||
private:
|
|
||||||
PingHelper();
|
|
||||||
~PingHelper();
|
|
||||||
|
|
||||||
PingHelper(const PingHelper&) = delete;
|
PingHelper(const PingHelper&) = delete;
|
||||||
PingHelper& operator=(const PingHelper&) = delete;
|
PingHelper& operator=(const PingHelper&) = delete;
|
||||||
|
private:
|
||||||
|
PingHelper();
|
||||||
signals:
|
signals:
|
||||||
void stop();
|
void stop();
|
||||||
public slots:
|
public slots:
|
||||||
|
|
|
@ -14,7 +14,7 @@ void PingUpdater::stop()
|
||||||
run = false;
|
run = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PingUpdater::addServer(int row, AddrPair addr)
|
void PingUpdater::addServer(int row, const AddrPair &addr)
|
||||||
{
|
{
|
||||||
servers.push_back({row, addr});
|
servers.push_back({row, addr});
|
||||||
run = true;
|
run = true;
|
||||||
|
|
|
@ -14,7 +14,7 @@ class PingUpdater : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
void addServer(int row, AddrPair addrPair);
|
void addServer(int row, const AddrPair &addrPair);
|
||||||
public slots:
|
public slots:
|
||||||
void stop();
|
void stop();
|
||||||
void process();
|
void process();
|
||||||
|
|
|
@ -19,8 +19,8 @@ QueryHelper::QueryHelper(QAbstractItemModel *model)
|
||||||
connect(queryThread, SIGNAL(started()), queryUpdate, SLOT(process()));
|
connect(queryThread, SIGNAL(started()), queryUpdate, SLOT(process()));
|
||||||
connect(queryUpdate, SIGNAL(finished()), queryThread, SLOT(quit()));
|
connect(queryUpdate, SIGNAL(finished()), queryThread, SLOT(quit()));
|
||||||
connect(queryUpdate, &QueryUpdate::finished, [this](){emit finished();});
|
connect(queryUpdate, &QueryUpdate::finished, [this](){emit finished();});
|
||||||
connect(queryUpdate, SIGNAL(updateModel(QString, unsigned short, QueryData)),
|
connect(queryUpdate, SIGNAL(updateModel(const QString&, unsigned short, const QueryData&)),
|
||||||
this, SLOT(update(QString, unsigned short, QueryData)));
|
this, SLOT(update(const QString&, unsigned short, const QueryData&)));
|
||||||
queryUpdate->moveToThread(queryThread);
|
queryUpdate->moveToThread(queryThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ void QueryHelper::terminate()
|
||||||
queryThread->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);
|
ServerModel *model = ((ServerModel*)_model);
|
||||||
model->insertRow(model->rowCount());
|
model->insertRow(model->rowCount());
|
||||||
|
@ -80,7 +80,7 @@ void QueryUpdate::process()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto server : data)
|
for (const auto &server : data)
|
||||||
emit updateModel(server.first.ToString(false), server.first.GetPort(), server.second);
|
emit updateModel(server.first.ToString(false), server.first.GetPort(), server.second);
|
||||||
emit finished();
|
emit finished();
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ public slots:
|
||||||
void refresh();
|
void refresh();
|
||||||
void terminate();
|
void terminate();
|
||||||
private slots:
|
private slots:
|
||||||
void update(QString addr, unsigned short port, QueryData data);
|
void update(const QString &addr, unsigned short port, const QueryData& data);
|
||||||
signals:
|
signals:
|
||||||
void finished();
|
void finished();
|
||||||
void started();
|
void started();
|
||||||
|
@ -38,7 +38,7 @@ class QueryUpdate : public QObject
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
signals:
|
signals:
|
||||||
void finished();
|
void finished();
|
||||||
void updateModel(QString addr, unsigned short port, QueryData data);
|
void updateModel(const QString &addr, unsigned short port, const QueryData& data);
|
||||||
public slots:
|
public slots:
|
||||||
void process();
|
void process();
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "ServerInfoDialog.hpp"
|
#include "ServerInfoDialog.hpp"
|
||||||
#include <apps/browser/netutils/Utils.hpp>
|
#include <apps/browser/netutils/Utils.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace RakNet;
|
using namespace RakNet;
|
||||||
|
@ -18,12 +19,7 @@ ServerInfoDialog::ServerInfoDialog(QWidget *parent): QDialog(parent)
|
||||||
connect(btnRefresh, SIGNAL(clicked()), this, SLOT(refresh()));
|
connect(btnRefresh, SIGNAL(clicked()), this, SLOT(refresh()));
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerInfoDialog::~ServerInfoDialog()
|
void ServerInfoDialog::Server(const QString &addr)
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void ServerInfoDialog::Server(QString addr)
|
|
||||||
{
|
{
|
||||||
this->addr = addr;
|
this->addr = addr;
|
||||||
}
|
}
|
||||||
|
@ -42,7 +38,7 @@ bool ServerInfoDialog::refresh()
|
||||||
|
|
||||||
listPlayers->clear();
|
listPlayers->clear();
|
||||||
|
|
||||||
for (auto player : sd.second.players)
|
for (const auto &player : sd.second.players)
|
||||||
listPlayers->addItem(QString::fromStdString(player));
|
listPlayers->addItem(QString::fromStdString(player));
|
||||||
|
|
||||||
listPlugins->clear();
|
listPlugins->clear();
|
||||||
|
|
|
@ -11,9 +11,8 @@ class ServerInfoDialog : public QDialog, public Ui::Dialog
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit ServerInfoDialog(QWidget *parent = 0);
|
explicit ServerInfoDialog(QWidget *parent = nullptr);
|
||||||
virtual ~ServerInfoDialog();
|
void Server(const QString &addr);
|
||||||
void Server(QString addr);
|
|
||||||
public slots:
|
public slots:
|
||||||
bool refresh();
|
bool refresh();
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -1,16 +1,11 @@
|
||||||
#include <qmessagebox.h>
|
#include <qmessagebox.h>
|
||||||
#include "ServerModel.hpp"
|
#include "ServerModel.hpp"
|
||||||
#include <qdebug.h>
|
#include <qdebug.h>
|
||||||
|
#include <apps/browser/netutils/Utils.hpp>
|
||||||
|
|
||||||
ServerModel::ServerModel(QObject *parent) : QAbstractTableModel(parent)
|
ServerModel::ServerModel(QObject *parent) : QAbstractTableModel(parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerModel::~ServerModel()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*QHash<int, QByteArray> ServerModel::roleNames() const
|
/*QHash<int, QByteArray> ServerModel::roleNames() const
|
||||||
{
|
{
|
||||||
return roles;
|
return roles;
|
||||||
|
@ -49,7 +44,7 @@ QVariant ServerModel::data(const QModelIndex &index, int role) const
|
||||||
var = QString(sd.rules.at("name").str.c_str());
|
var = QString(sd.rules.at("name").str.c_str());
|
||||||
break;
|
break;
|
||||||
case ServerData::PING:
|
case ServerData::PING:
|
||||||
var = sd.ping;
|
var = sd.ping == PING_UNREACHABLE ? QVariant("Unreachable") : sd.ping;
|
||||||
break;
|
break;
|
||||||
case ServerData::MODNAME:
|
case ServerData::MODNAME:
|
||||||
if (sd.rules.at("gamemode").str == "")
|
if (sd.rules.at("gamemode").str == "")
|
||||||
|
|
|
@ -29,8 +29,7 @@ class ServerModel: public QAbstractTableModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit ServerModel(QObject *parent = 0);
|
explicit ServerModel(QObject *parent = nullptr);
|
||||||
~ServerModel();
|
|
||||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_FINAL;
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_FINAL;
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_FINAL;
|
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_FINAL;
|
||||||
int columnCount(const QModelIndex &parent) const Q_DECL_FINAL;
|
int columnCount(const QModelIndex &parent) const Q_DECL_FINAL;
|
||||||
|
|
|
@ -30,7 +30,7 @@ QueryClient::~QueryClient()
|
||||||
RakPeerInterface::DestroyInstance(peer);
|
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);
|
masterAddr = SystemAddress(addr.c_str(), port);
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ map<SystemAddress, QueryData> QueryClient::Query()
|
||||||
return 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)";
|
qDebug() << "Locking mutex in QueryClient::Update(RakNet::SystemAddress addr)";
|
||||||
pair<SystemAddress, QueryData> server;
|
pair<SystemAddress, QueryData> server;
|
||||||
|
@ -179,7 +179,7 @@ ConnectionState QueryClient::Connect()
|
||||||
{
|
{
|
||||||
|
|
||||||
ConnectionAttemptResult car = peer->Connect(masterAddr.ToString(false), masterAddr.GetPort(), TES3MP_MASTERSERVER_PASSW,
|
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)
|
while (true)
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,16 +14,16 @@
|
||||||
|
|
||||||
class QueryClient
|
class QueryClient
|
||||||
{
|
{
|
||||||
private:
|
public:
|
||||||
QueryClient(QueryClient const &) = delete;
|
QueryClient(QueryClient const &) = delete;
|
||||||
QueryClient(QueryClient &&) = delete;
|
QueryClient(QueryClient &&) = delete;
|
||||||
QueryClient &operator=(QueryClient const &) = delete;
|
QueryClient &operator=(QueryClient const &) = delete;
|
||||||
QueryClient &operator=(QueryClient &&) = delete;
|
QueryClient &operator=(QueryClient &&) = delete;
|
||||||
public:
|
|
||||||
static QueryClient &Get();
|
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::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();
|
int Status();
|
||||||
private:
|
private:
|
||||||
RakNet::ConnectionState Connect();
|
RakNet::ConnectionState Connect();
|
||||||
|
|
|
@ -44,12 +44,16 @@ unsigned int PingRakNetServer(const char *addr, unsigned short port)
|
||||||
break;
|
break;
|
||||||
case ID_CONNECTED_PING:
|
case ID_CONNECTED_PING:
|
||||||
case ID_UNCONNECTED_PONG:
|
case ID_UNCONNECTED_PONG:
|
||||||
|
{
|
||||||
RakNet::BitStream bsIn(&packet->data[1], packet->length, false);
|
RakNet::BitStream bsIn(&packet->data[1], packet->length, false);
|
||||||
bsIn.Read(time);
|
bsIn.Read(time);
|
||||||
time = now - time;
|
time = now - time;
|
||||||
done = true;
|
done = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
peer->DeallocatePacket(packet);
|
peer->DeallocatePacket(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,9 +73,9 @@ ServerExtendedData getExtendedData(const char *addr, unsigned short port)
|
||||||
sstr << TES3MP_VERSION;
|
sstr << TES3MP_VERSION;
|
||||||
sstr << TES3MP_PROTO_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";
|
msg = "Connection attempt failed.\n";
|
||||||
|
|
||||||
|
|
||||||
|
@ -142,14 +146,14 @@ ServerExtendedData getExtendedData(const char *addr, unsigned short port)
|
||||||
{
|
{
|
||||||
RakNet::RakString str;
|
RakNet::RakString str;
|
||||||
bs.Read(str);
|
bs.Read(str);
|
||||||
data.players.push_back(str.C_String());
|
data.players.emplace_back(str.C_String());
|
||||||
}
|
}
|
||||||
bs.Read(length);
|
bs.Read(length);
|
||||||
for (size_t i = 0; i < length; i++)
|
for (size_t i = 0; i < length; i++)
|
||||||
{
|
{
|
||||||
RakNet::RakString str;
|
RakNet::RakString str;
|
||||||
bs.Read(str);
|
bs.Read(str);
|
||||||
data.plugins.push_back(str.C_String());
|
data.plugins.emplace_back(str.C_String());
|
||||||
}
|
}
|
||||||
done = true;
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,15 @@ project(masterserver)
|
||||||
#set(CMAKE_CXX_STANDARD 14)
|
#set(CMAKE_CXX_STANDARD 14)
|
||||||
add_definitions(-std=gnu++14)
|
add_definitions(-std=gnu++14)
|
||||||
|
|
||||||
include_directories("./")
|
find_package(LuaJit REQUIRED)
|
||||||
|
find_package(OpenSSL REQUIRED)
|
||||||
|
|
||||||
set(SOURCE_FILES main.cpp MasterServer.cpp MasterServer.hpp RestServer.cpp RestServer.hpp)
|
include_directories("./" ${LUAJIT_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/extern/sol ${OPENSSL_INCLUDE_DIR})
|
||||||
|
|
||||||
|
set(SOURCE_FILES main.cpp MasterServer.cpp MasterServer.hpp RestServer.cpp RestServer.hpp AdminRest.cpp)
|
||||||
|
|
||||||
add_executable(masterserver ${SOURCE_FILES})
|
add_executable(masterserver ${SOURCE_FILES})
|
||||||
target_link_libraries(masterserver ${RakNet_LIBRARY} components)
|
target_link_libraries(masterserver ${RakNet_LIBRARY} ${LUAJIT_LIBRARY} ${OPENSSL_LIBRARIES} components)
|
||||||
|
|
||||||
option(BUILD_MASTER_TEST "build master server test program" OFF)
|
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/PacketMasterUpdate.hpp>
|
||||||
#include <components/openmw-mp/Master/PacketMasterAnnounce.hpp>
|
#include <components/openmw-mp/Master/PacketMasterAnnounce.hpp>
|
||||||
#include <components/openmw-mp/Version.hpp>
|
#include <components/openmw-mp/Version.hpp>
|
||||||
|
#include <components/openmw-mp/Utils.hpp>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
using namespace RakNet;
|
using namespace RakNet;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace mwmp;
|
using namespace mwmp;
|
||||||
using namespace chrono;
|
using namespace chrono;
|
||||||
|
|
||||||
MasterServer::MasterServer(unsigned short maxConnections, unsigned short port)
|
MasterServer::MasterServer(const std::string &luaScript)
|
||||||
{
|
{
|
||||||
peer = RakPeerInterface::GetInstance();
|
state.open_libraries();
|
||||||
sockdescr = SocketDescriptor(port, 0);
|
|
||||||
peer->Startup(maxConnections, &sockdescr, 1, 1000);
|
|
||||||
|
|
||||||
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));
|
peer->SetIncomingPassword(TES3MP_MASTERSERVER_PASSW, (int) strlen(TES3MP_MASTERSERVER_PASSW));
|
||||||
run = false;
|
run = false;
|
||||||
}
|
}
|
||||||
|
@ -52,6 +87,13 @@ void MasterServer::Thread()
|
||||||
PacketMasterAnnounce pma(peer);
|
PacketMasterAnnounce pma(peer);
|
||||||
pma.SetSendStream(&send);
|
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)
|
while (run)
|
||||||
{
|
{
|
||||||
Packet *packet = peer->Receive();
|
Packet *packet = peer->Receive();
|
||||||
|
@ -113,7 +155,7 @@ void MasterServer::Thread()
|
||||||
SystemAddress addr;
|
SystemAddress addr;
|
||||||
data.Read(addr); // update 1 server
|
data.Read(addr); // update 1 server
|
||||||
|
|
||||||
ServerIter it = servers.find(addr);
|
auto it = servers.find(addr);
|
||||||
if (it != servers.end())
|
if (it != servers.end())
|
||||||
{
|
{
|
||||||
pair<SystemAddress, QueryData> pairPtr(it->first, static_cast<QueryData>(it->second));
|
pair<SystemAddress, QueryData> pairPtr(it->first, static_cast<QueryData>(it->second));
|
||||||
|
@ -127,7 +169,7 @@ void MasterServer::Thread()
|
||||||
}
|
}
|
||||||
case ID_MASTER_ANNOUNCE:
|
case ID_MASTER_ANNOUNCE:
|
||||||
{
|
{
|
||||||
ServerIter iter = servers.find(packet->systemAddress);
|
auto iter = servers.find(packet->systemAddress);
|
||||||
|
|
||||||
pma.SetReadStream(&data);
|
pma.SetReadStream(&data);
|
||||||
SServer server;
|
SServer server;
|
||||||
|
@ -141,6 +183,26 @@ void MasterServer::Thread()
|
||||||
pendingACKs[packet->guid] = steady_clock::now();
|
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 (iter != servers.end())
|
||||||
{
|
{
|
||||||
if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_DELETE)
|
if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_DELETE)
|
||||||
|
@ -151,23 +213,38 @@ void MasterServer::Thread()
|
||||||
pendingACKs[packet->guid] = steady_clock::now();
|
pendingACKs[packet->guid] = steady_clock::now();
|
||||||
}
|
}
|
||||||
else if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_ANNOUNCE)
|
else if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_ANNOUNCE)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (isServerValid(server))
|
||||||
{
|
{
|
||||||
cout << "Updated";
|
cout << "Updated";
|
||||||
iter->second = server;
|
iter->second = server;
|
||||||
keepAliveFunc();
|
keepAliveFunc();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
cout << "Update rejected";
|
||||||
|
servers.erase(iter);
|
||||||
|
pendingACKs.erase(packet->guid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
cout << "Keeping alive";
|
cout << "Keeping alive";
|
||||||
keepAliveFunc();
|
keepAliveFunc();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_ANNOUNCE)
|
else if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_ANNOUNCE)
|
||||||
|
{
|
||||||
|
if (isServerValid(server))
|
||||||
{
|
{
|
||||||
cout << "Added";
|
cout << "Added";
|
||||||
iter = servers.insert({packet->systemAddress, server}).first;
|
iter = servers.insert({packet->systemAddress, server}).first;
|
||||||
keepAliveFunc();
|
keepAliveFunc();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
cout << "Adding rejected";
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cout << "Unknown";
|
cout << "Unknown";
|
||||||
|
@ -186,7 +263,8 @@ void MasterServer::Thread()
|
||||||
peer->CloseConnection(packet->systemAddress, true);
|
peer->CloseConnection(packet->systemAddress, true);
|
||||||
break;
|
break;
|
||||||
default:
|
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);
|
peer->CloseConnection(packet->systemAddress, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,3 +312,22 @@ MasterServer::ServerMap *MasterServer::GetServers()
|
||||||
{
|
{
|
||||||
return &servers;
|
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 <chrono>
|
||||||
#include <RakPeerInterface.h>
|
#include <RakPeerInterface.h>
|
||||||
#include <components/openmw-mp/Master/MasterData.hpp>
|
#include <components/openmw-mp/Master/MasterData.hpp>
|
||||||
|
#include <extern/sol/sol.hpp>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
class MasterServer
|
class MasterServer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct Ban
|
|
||||||
{
|
|
||||||
RakNet::SystemAddress sa;
|
|
||||||
bool permanent;
|
|
||||||
struct Date
|
|
||||||
{
|
|
||||||
} date;
|
|
||||||
};
|
|
||||||
struct SServer : QueryData
|
struct SServer : QueryData
|
||||||
{
|
{
|
||||||
std::chrono::steady_clock::time_point lastUpdate;
|
std::chrono::steady_clock::time_point lastUpdate;
|
||||||
|
@ -29,7 +23,7 @@ public:
|
||||||
//typedef ServerMap::const_iterator ServerCIter;
|
//typedef ServerMap::const_iterator ServerCIter;
|
||||||
typedef ServerMap::iterator ServerIter;
|
typedef ServerMap::iterator ServerIter;
|
||||||
|
|
||||||
MasterServer(unsigned short maxConnections, unsigned short port);
|
explicit MasterServer(const std::string &luaScript);
|
||||||
~MasterServer();
|
~MasterServer();
|
||||||
|
|
||||||
void Start();
|
void Start();
|
||||||
|
@ -38,6 +32,10 @@ public:
|
||||||
void Wait();
|
void Wait();
|
||||||
|
|
||||||
ServerMap* GetServers();
|
ServerMap* GetServers();
|
||||||
|
void luaStuff(std::function<void(sol::state &)> f);
|
||||||
|
|
||||||
|
void ban(const std::string &addr);
|
||||||
|
void unban(const std::string &addr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Thread();
|
void Thread();
|
||||||
|
@ -49,6 +47,9 @@ private:
|
||||||
ServerMap servers;
|
ServerMap servers;
|
||||||
bool run;
|
bool run;
|
||||||
std::map<RakNet::RakNetGUID, std::chrono::steady_clock::time_point> pendingACKs;
|
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/ptree.hpp>
|
||||||
#include <boost/property_tree/json_parser.hpp>
|
#include <boost/property_tree/json_parser.hpp>
|
||||||
|
|
||||||
|
#include "RestUtils.hpp"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace chrono;
|
using namespace chrono;
|
||||||
using namespace boost::property_tree;
|
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)
|
inline void ptreeToServer(boost::property_tree::ptree &pt, MasterServer::SServer &server)
|
||||||
{
|
{
|
||||||
server.SetName(pt.get<string>("hostname").c_str());
|
server.SetName(pt.get<string>("hostname").c_str());
|
||||||
|
@ -70,7 +60,7 @@ void RestServer::start()
|
||||||
auto port = (unsigned short)stoi(&(addr[addr.find(':')+1]));
|
auto port = (unsigned short)stoi(&(addr[addr.find(':')+1]));
|
||||||
queryToStringStream(ss, "server", serverMap->at(RakNet::SystemAddress(addr.c_str(), port)));
|
queryToStringStream(ss, "server", serverMap->at(RakNet::SystemAddress(addr.c_str(), port)));
|
||||||
ss << "}";
|
ss << "}";
|
||||||
ResponseStr(*response, ss.str(), "application/json");
|
ResponseStr<SimpleWeb::HTTP>(response, ss.str(), "application/json");
|
||||||
}
|
}
|
||||||
catch(out_of_range e)
|
catch(out_of_range e)
|
||||||
{
|
{
|
||||||
|
@ -93,7 +83,7 @@ void RestServer::start()
|
||||||
ss << ", ";
|
ss << ", ";
|
||||||
}
|
}
|
||||||
ss << "}}";
|
ss << "}}";
|
||||||
ResponseStr(*response, ss.str(), "application/json");
|
ResponseStr<SimpleWeb::HTTP>(response, ss.str(), "application/json");
|
||||||
updatedCache = false;
|
updatedCache = false;
|
||||||
}
|
}
|
||||||
*response << str;
|
*response << str;
|
||||||
|
@ -110,7 +100,7 @@ void RestServer::start()
|
||||||
MasterServer::SServer server;
|
MasterServer::SServer server;
|
||||||
ptreeToServer(pt, server);
|
ptreeToServer(pt, server);
|
||||||
|
|
||||||
unsigned short port = pt.get<unsigned short>("port");
|
auto port = pt.get<unsigned short>("port");
|
||||||
server.lastUpdate = steady_clock::now();
|
server.lastUpdate = steady_clock::now();
|
||||||
serverMap->insert({RakNet::SystemAddress(request->remote_endpoint_address.c_str(), port), server});
|
serverMap->insert({RakNet::SystemAddress(request->remote_endpoint_address.c_str(), port), server});
|
||||||
updatedCache = true;
|
updatedCache = true;
|
||||||
|
@ -137,7 +127,6 @@ void RestServer::start()
|
||||||
*response << response400;
|
*response << response400;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request->content.size() != 0)
|
if (request->content.size() != 0)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -171,14 +160,14 @@ void RestServer::start()
|
||||||
ss << ", \"players\": " << players;
|
ss << ", \"players\": " << players;
|
||||||
ss << "}";
|
ss << "}";
|
||||||
|
|
||||||
ResponseStr(*response, ss.str(), "application/json");
|
ResponseStr<SimpleWeb::HTTP>(response, ss.str(), "application/json");
|
||||||
};
|
};
|
||||||
|
|
||||||
httpServer.default_resource["GET"]=[](auto response, auto /*request*/) {
|
httpServer.default_resource["GET"]=[](auto response, auto /*request*/) {
|
||||||
*response << response400;
|
*response << response400;
|
||||||
};
|
};
|
||||||
|
|
||||||
httpServer.start();
|
thr = thread([this](){httpServer.start();});
|
||||||
}
|
}
|
||||||
|
|
||||||
void RestServer::cacheUpdated()
|
void RestServer::cacheUpdated()
|
||||||
|
@ -189,4 +178,6 @@ void RestServer::cacheUpdated()
|
||||||
void RestServer::stop()
|
void RestServer::stop()
|
||||||
{
|
{
|
||||||
httpServer.stop();
|
httpServer.stop();
|
||||||
|
if(thr.joinable())
|
||||||
|
thr.join();
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ private:
|
||||||
HttpServer httpServer;
|
HttpServer httpServer;
|
||||||
MasterServer::ServerMap *serverMap;
|
MasterServer::ServerMap *serverMap;
|
||||||
bool updatedCache = true;
|
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
|
#pragma once
|
||||||
#define BASE_SERVER_HPP
|
|
||||||
|
|
||||||
#include <boost/asio.hpp>
|
#include "utility.hpp"
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <condition_variable>
|
||||||
#include <boost/functional/hash.hpp>
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <thread>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <thread>
|
||||||
|
#include <unordered_set>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
||||||
#ifndef CASE_INSENSITIVE_EQUALS_AND_HASH
|
#ifdef USE_STANDALONE_ASIO
|
||||||
#define CASE_INSENSITIVE_EQUALS_AND_HASH
|
#include <asio.hpp>
|
||||||
|
#include <asio/steady_timer.hpp>
|
||||||
//Based on http://www.boost.org/doc/libs/1_60_0/doc/html/unordered/hash_equality.html
|
namespace SimpleWeb {
|
||||||
struct case_insensitive_equals
|
using error_code = std::error_code;
|
||||||
{
|
using errc = std::errc;
|
||||||
bool operator()(const std::string &key1, const std::string &key2) const
|
namespace make_error_code = std;
|
||||||
{
|
} // namespace SimpleWeb
|
||||||
return boost::algorithm::iequals(key1, key2);
|
#else
|
||||||
}
|
#include <boost/asio.hpp>
|
||||||
};
|
#include <boost/asio/steady_timer.hpp>
|
||||||
|
namespace SimpleWeb {
|
||||||
struct case_insensitive_hash
|
namespace asio = boost::asio;
|
||||||
{
|
using error_code = boost::system::error_code;
|
||||||
size_t operator()(const std::string &key) const
|
namespace errc = boost::system::errc;
|
||||||
{
|
namespace make_error_code = boost::system::errc;
|
||||||
std::size_t seed = 0;
|
} // namespace SimpleWeb
|
||||||
for (auto &c: key)
|
|
||||||
boost::hash_combine(seed, std::tolower(c));
|
|
||||||
return seed;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace SimpleWeb
|
namespace SimpleWeb {
|
||||||
{
|
|
||||||
template <class socket_type>
|
template <class socket_type>
|
||||||
class Server;
|
class Server;
|
||||||
|
|
||||||
template <class socket_type>
|
template <class socket_type>
|
||||||
class ServerBase
|
class ServerBase {
|
||||||
{
|
protected:
|
||||||
public:
|
class Session;
|
||||||
virtual ~ServerBase()
|
|
||||||
{}
|
|
||||||
|
|
||||||
class Response : public std::ostream
|
public:
|
||||||
{
|
class Response : public std::enable_shared_from_this<Response>, public std::ostream {
|
||||||
friend class ServerBase<socket_type>;
|
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:
|
public:
|
||||||
size_t size()
|
size_t size() noexcept {
|
||||||
{
|
|
||||||
return streambuf.size();
|
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.
|
/// 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
|
/// This is useful when implementing a HTTP/1.0-server sending content
|
||||||
|
@ -74,157 +139,198 @@ namespace SimpleWeb
|
||||||
bool close_connection_after_response = false;
|
bool close_connection_after_response = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Content : public std::istream
|
class Content : public std::istream {
|
||||||
{
|
|
||||||
friend class ServerBase<socket_type>;
|
friend class ServerBase<socket_type>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
size_t size()
|
size_t size() noexcept {
|
||||||
{
|
|
||||||
return streambuf.size();
|
return streambuf.size();
|
||||||
}
|
}
|
||||||
|
/// Convenience function to return std::string. The stream buffer is consumed.
|
||||||
std::string string()
|
std::string string() noexcept {
|
||||||
{
|
try {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << rdbuf();
|
ss << rdbuf();
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
catch(...) {
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
boost::asio::streambuf &streambuf;
|
asio::streambuf &streambuf;
|
||||||
|
Content(asio::streambuf &streambuf) noexcept : std::istream(&streambuf), streambuf(streambuf) {}
|
||||||
Content(boost::asio::streambuf &streambuf) : std::istream(&streambuf), streambuf(streambuf)
|
|
||||||
{}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Request
|
class Request {
|
||||||
{
|
|
||||||
friend class ServerBase<socket_type>;
|
friend class ServerBase<socket_type>;
|
||||||
|
|
||||||
friend class Server<socket_type>;
|
friend class Server<socket_type>;
|
||||||
|
friend class Session;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::string method, path, http_version;
|
std::string method, path, query_string, http_version;
|
||||||
|
|
||||||
Content content;
|
Content content;
|
||||||
|
|
||||||
std::unordered_multimap<std::string, std::string, case_insensitive_hash, case_insensitive_equals> header;
|
CaseInsensitiveMultimap header;
|
||||||
|
|
||||||
std::smatch path_match;
|
std::smatch path_match;
|
||||||
|
|
||||||
std::string remote_endpoint_address;
|
std::string remote_endpoint_address;
|
||||||
unsigned short remote_endpoint_port;
|
unsigned short remote_endpoint_port;
|
||||||
|
|
||||||
private:
|
/// Returns query keys with percent-decoded values.
|
||||||
Request(const socket_type &socket) : content(streambuf)
|
CaseInsensitiveMultimap parse_query_string() noexcept {
|
||||||
{
|
return SimpleWeb::QueryString::parse(query_string);
|
||||||
try
|
|
||||||
{
|
|
||||||
remote_endpoint_address = socket.lowest_layer().remote_endpoint().address().to_string();
|
|
||||||
remote_endpoint_port = socket.lowest_layer().remote_endpoint().port();
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::asio::streambuf streambuf;
|
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>;
|
friend class ServerBase<socket_type>;
|
||||||
|
|
||||||
Config(unsigned short port) : port(port)
|
Config(unsigned short port) noexcept : port(port) {}
|
||||||
{}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Port number to use. Defaults to 80 for HTTP and 443 for HTTPS.
|
/// Port number to use. Defaults to 80 for HTTP and 443 for HTTPS.
|
||||||
unsigned short port;
|
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;
|
size_t thread_pool_size = 1;
|
||||||
/// Timeout on request handling. Defaults to 5 seconds.
|
/// 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.
|
/// 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.
|
/// IPv4 address in dotted decimal form or IPv6 address in hexadecimal notation.
|
||||||
/// If empty, the address will be any address.
|
/// If empty, the address will be any address.
|
||||||
std::string address;
|
std::string address;
|
||||||
/// Set to false to avoid binding the socket to an address that is already in use. Defaults to true.
|
/// Set to false to avoid binding the socket to an address that is already in use. Defaults to true.
|
||||||
bool reuse_address = true;
|
bool reuse_address = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Set before calling start().
|
/// Set before calling start().
|
||||||
Config config;
|
Config config;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class regex_orderable : public std::regex
|
class regex_orderable : public std::regex {
|
||||||
{
|
|
||||||
std::string str;
|
std::string str;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
regex_orderable(const char *regex_cstr) : std::regex(regex_cstr), str(regex_cstr)
|
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 {
|
||||||
regex_orderable(const std::string ®ex_str) : std::regex(regex_str), str(regex_str)
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool operator<(const regex_orderable &rhs) const
|
|
||||||
{
|
|
||||||
return str < rhs.str;
|
return str < rhs.str;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Warning: do not add or remove resources after start() is called
|
/// Warning: do not add or remove resources after start() is called
|
||||||
std::map<regex_orderable, std::map<std::string,
|
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::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>,
|
|
||||||
std::shared_ptr<typename ServerBase<socket_type>::Request>)>>>
|
|
||||||
resource;
|
|
||||||
|
|
||||||
std::map<std::string,
|
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>::Response>,
|
|
||||||
std::shared_ptr<typename ServerBase<socket_type>::Request>)>> default_resource;
|
|
||||||
|
|
||||||
std::function<
|
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Request>, const error_code &)> on_error;
|
||||||
void(std::shared_ptr<typename ServerBase<socket_type>::Request>,
|
|
||||||
const boost::system::error_code &)>
|
|
||||||
on_error;
|
|
||||||
|
|
||||||
std::function<void(std::shared_ptr<socket_type> socket,
|
std::function<void(std::unique_ptr<socket_type> &, std::shared_ptr<typename ServerBase<socket_type>::Request>)> on_upgrade;
|
||||||
std::shared_ptr<typename ServerBase<socket_type>::Request>)> on_upgrade;
|
|
||||||
|
|
||||||
virtual void start()
|
/// 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)
|
|
||||||
io_service = std::make_shared<boost::asio::io_service>();
|
virtual void start() {
|
||||||
|
if(!io_service) {
|
||||||
|
io_service = std::make_shared<asio::io_service>();
|
||||||
|
internal_io_service = true;
|
||||||
|
}
|
||||||
|
|
||||||
if(io_service->stopped())
|
if(io_service->stopped())
|
||||||
io_service->reset();
|
io_service->reset();
|
||||||
|
|
||||||
boost::asio::ip::tcp::endpoint endpoint;
|
asio::ip::tcp::endpoint endpoint;
|
||||||
if(config.address.size() > 0)
|
if(config.address.size() > 0)
|
||||||
endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(config.address),
|
endpoint = asio::ip::tcp::endpoint(asio::ip::address::from_string(config.address), config.port);
|
||||||
config.port);
|
|
||||||
else
|
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)
|
if(!acceptor)
|
||||||
acceptor = std::unique_ptr<boost::asio::ip::tcp::acceptor>(
|
acceptor = std::unique_ptr<asio::ip::tcp::acceptor>(new asio::ip::tcp::acceptor(*io_service));
|
||||||
new boost::asio::ip::tcp::acceptor(*io_service));
|
|
||||||
acceptor->open(endpoint.protocol());
|
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->bind(endpoint);
|
||||||
acceptor->listen();
|
acceptor->listen();
|
||||||
|
|
||||||
accept();
|
accept();
|
||||||
|
|
||||||
|
if(internal_io_service) {
|
||||||
// If thread_pool_size>1, start m_io_service.run() in (thread_pool_size-1) threads for thread-pooling
|
// If thread_pool_size>1, start m_io_service.run() in (thread_pool_size-1) threads for thread-pooling
|
||||||
threads.clear();
|
threads.clear();
|
||||||
for (size_t c = 1; c < config.thread_pool_size; c++)
|
for(size_t c = 1; c < config.thread_pool_size; c++) {
|
||||||
{
|
threads.emplace_back([this]() {
|
||||||
threads.emplace_back([this]()
|
this->io_service->run();
|
||||||
{
|
|
||||||
io_service->run();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,278 +340,198 @@ namespace SimpleWeb
|
||||||
|
|
||||||
// Wait for the rest of the threads, if any, to finish as well
|
// Wait for the rest of the threads, if any, to finish as well
|
||||||
for(auto &t : threads)
|
for(auto &t : threads)
|
||||||
{
|
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop()
|
/// Stop accepting new requests, and close current connections.
|
||||||
|
void stop() noexcept {
|
||||||
|
if(acceptor) {
|
||||||
|
error_code ec;
|
||||||
|
acceptor->close(ec);
|
||||||
|
|
||||||
{
|
{
|
||||||
acceptor->close();
|
std::unique_lock<std::mutex> lock(*connections_mutex);
|
||||||
if (config.thread_pool_size > 0)
|
for(auto &connection : *connections)
|
||||||
|
connection->close();
|
||||||
|
connections->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(internal_io_service)
|
||||||
io_service->stop();
|
io_service->stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
///Use this function if you need to recursively send parts of a longer message
|
|
||||||
void send(const std::shared_ptr<Response> &response,
|
|
||||||
const std::function<void(const boost::system::error_code &)> &callback = nullptr) const
|
|
||||||
{
|
|
||||||
boost::asio::async_write(*response->socket, response->streambuf, [this, response, callback]
|
|
||||||
(const boost::system::error_code &ec, size_t /*bytes_transferred*/)
|
|
||||||
{
|
|
||||||
if (callback)
|
|
||||||
callback(ec);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If you have your own boost::asio::io_service, store its pointer here before running start().
|
virtual ~ServerBase() noexcept {
|
||||||
/// You might also want to set config.thread_pool_size to 0.
|
handler_runner->stop();
|
||||||
std::shared_ptr<boost::asio::io_service> io_service;
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
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;
|
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;
|
||||||
|
|
||||||
|
std::shared_ptr<ScopeRunner> handler_runner;
|
||||||
|
|
||||||
|
ServerBase(unsigned short port) noexcept : config(port), connections(new std::unordered_set<Connection *>()), connections_mutex(new std::mutex()), handler_runner(new ScopeRunner()) {}
|
||||||
|
|
||||||
virtual void accept() = 0;
|
virtual void accept() = 0;
|
||||||
|
|
||||||
std::shared_ptr<boost::asio::deadline_timer>
|
template <typename... Args>
|
||||||
get_timeout_timer(const std::shared_ptr<socket_type> &socket, long seconds)
|
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) {
|
||||||
{
|
{
|
||||||
if (seconds == 0)
|
std::unique_lock<std::mutex> lock(*connections_mutex);
|
||||||
return nullptr;
|
auto it = connections->find(connection);
|
||||||
|
if(it != connections->end())
|
||||||
auto timer = std::make_shared<boost::asio::deadline_timer>(*io_service);
|
connections->erase(it);
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
delete connection;
|
||||||
});
|
});
|
||||||
return timer;
|
{
|
||||||
|
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)
|
void read_request_and_content(const std::shared_ptr<Session> &session) {
|
||||||
{
|
session->connection->set_timeout(config.timeout_request);
|
||||||
//Create new streambuf (Request::streambuf) for async_read_until()
|
asio::async_read_until(*session->connection->socket, session->request->streambuf, "\r\n\r\n", [this, session](const error_code &ec, size_t bytes_transferred) {
|
||||||
//shared_ptr is used to pass temporary objects to the asynchronous functions
|
session->connection->cancel_timeout();
|
||||||
std::shared_ptr<Request> request(new Request(*socket));
|
auto lock = session->connection->handler_runner->continue_lock();
|
||||||
|
if(!lock)
|
||||||
//Set timeout on the following boost::asio::async-read or write function
|
return;
|
||||||
auto timer = this->get_timeout_timer(socket, config.timeout_request);
|
if(!ec) {
|
||||||
|
|
||||||
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:
|
// 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"
|
// "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
|
// 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).
|
// streambuf (maybe some bytes of the content) is appended to in the async_read-function below (for retrieving content).
|
||||||
size_t num_additional_bytes =
|
size_t num_additional_bytes = session->request->streambuf.size() - bytes_transferred;
|
||||||
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;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// If content, read that as well
|
// If content, read that as well
|
||||||
auto it = request->header.find("Content-Length");
|
auto it = session->request->header.find("Content-Length");
|
||||||
if (it != request->header.end())
|
if(it != session->request->header.end()) {
|
||||||
{
|
unsigned long long content_length = 0;
|
||||||
unsigned long long content_length;
|
try {
|
||||||
try
|
|
||||||
{
|
|
||||||
content_length = stoull(it->second);
|
content_length = stoull(it->second);
|
||||||
}
|
}
|
||||||
catch (const std::exception &e)
|
catch(const std::exception &e) {
|
||||||
{
|
if(this->on_error)
|
||||||
if (on_error)
|
this->on_error(session->request, make_error_code::make_error_code(errc::protocol_error));
|
||||||
on_error(request, boost::system::error_code(
|
|
||||||
boost::system::errc::protocol_error,
|
|
||||||
boost::system::generic_category()));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (content_length > num_additional_bytes)
|
if(content_length > num_additional_bytes) {
|
||||||
{
|
session->connection->set_timeout(config.timeout_content);
|
||||||
//Set timeout on the following boost::asio::async-read or write function
|
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*/) {
|
||||||
auto timer = this->get_timeout_timer(socket,
|
session->connection->cancel_timeout();
|
||||||
config.timeout_content);
|
auto lock = session->connection->handler_runner->continue_lock();
|
||||||
boost::asio::async_read(*socket, request->streambuf,
|
if(!lock)
|
||||||
boost::asio::transfer_exactly(
|
return;
|
||||||
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)
|
if(!ec)
|
||||||
this->find_resource(socket,
|
this->find_resource(session);
|
||||||
request);
|
else if(this->on_error)
|
||||||
else if (on_error)
|
this->on_error(session->request, ec);
|
||||||
on_error(request, ec);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this->find_resource(socket, request);
|
this->find_resource(session);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this->find_resource(socket, request);
|
this->find_resource(session);
|
||||||
}
|
}
|
||||||
else if (on_error)
|
else if(this->on_error)
|
||||||
on_error(request, ec);
|
this->on_error(session->request, ec);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool parse_request(const std::shared_ptr<Request> &request) const
|
void find_resource(const std::shared_ptr<Session> &session) {
|
||||||
{
|
|
||||||
std::string line;
|
|
||||||
getline(request->content, line);
|
|
||||||
size_t method_end;
|
|
||||||
if ((method_end = line.find(' ')) != std::string::npos)
|
|
||||||
{
|
|
||||||
size_t path_end;
|
|
||||||
if ((path_end = line.find(' ', method_end + 1)) != std::string::npos)
|
|
||||||
{
|
|
||||||
request->method = line.substr(0, method_end);
|
|
||||||
request->path = line.substr(method_end + 1, path_end - method_end - 1);
|
|
||||||
|
|
||||||
size_t protocol_end;
|
|
||||||
if ((protocol_end = line.find('/', path_end + 1)) != std::string::npos)
|
|
||||||
{
|
|
||||||
if (line.compare(path_end + 1, protocol_end - path_end - 1, "HTTP") != 0)
|
|
||||||
return false;
|
|
||||||
request->http_version = line.substr(protocol_end + 1, line.size() - protocol_end - 2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
|
|
||||||
getline(request->content, line);
|
|
||||||
size_t param_end;
|
|
||||||
while ((param_end = line.find(':')) != std::string::npos)
|
|
||||||
{
|
|
||||||
size_t value_start = param_end + 1;
|
|
||||||
if ((value_start) < line.size())
|
|
||||||
{
|
|
||||||
if (line[value_start] == ' ')
|
|
||||||
value_start++;
|
|
||||||
if (value_start < line.size())
|
|
||||||
request->header.emplace(line.substr(0, param_end),
|
|
||||||
line.substr(value_start, line.size() - value_start - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
getline(request->content, line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void find_resource(const std::shared_ptr<socket_type> &socket, const std::shared_ptr<Request> &request)
|
|
||||||
{
|
|
||||||
// Upgrade connection
|
// Upgrade connection
|
||||||
if (on_upgrade)
|
if(on_upgrade) {
|
||||||
|
auto it = session->request->header.find("Upgrade");
|
||||||
|
if(it != session->request->header.end()) {
|
||||||
|
// remove connection from connections
|
||||||
{
|
{
|
||||||
auto it = request->header.find("Upgrade");
|
std::unique_lock<std::mutex> lock(*connections_mutex);
|
||||||
if (it != request->header.end())
|
auto it = connections->find(session->connection.get());
|
||||||
{
|
if(it != connections->end())
|
||||||
on_upgrade(socket, request);
|
connections->erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
on_upgrade(session->connection->socket, session->request);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Find path- and method-match, and call write_response
|
// Find path- and method-match, and call write_response
|
||||||
for (auto ®ex_method: resource)
|
for(auto ®ex_method : resource) {
|
||||||
{
|
auto it = regex_method.second.find(session->request->method);
|
||||||
auto it = regex_method.second.find(request->method);
|
if(it != regex_method.second.end()) {
|
||||||
if (it != regex_method.second.end())
|
|
||||||
{
|
|
||||||
std::smatch sm_res;
|
std::smatch sm_res;
|
||||||
if (std::regex_match(request->path, sm_res, regex_method.first))
|
if(std::regex_match(session->request->path, sm_res, regex_method.first)) {
|
||||||
{
|
session->request->path_match = std::move(sm_res);
|
||||||
request->path_match = std::move(sm_res);
|
write_response(session, it->second);
|
||||||
write_response(socket, request, it->second);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto it = default_resource.find(request->method);
|
auto it = default_resource.find(session->request->method);
|
||||||
if(it != default_resource.end())
|
if(it != default_resource.end())
|
||||||
{
|
write_response(session, it->second);
|
||||||
write_response(socket, request, it->second);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_response(const std::shared_ptr<socket_type> &socket, const std::shared_ptr<Request> &request,
|
void write_response(const std::shared_ptr<Session> &session,
|
||||||
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>,
|
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>, std::shared_ptr<typename ServerBase<socket_type>::Request>)> &resource_function) {
|
||||||
std::shared_ptr<
|
session->connection->set_timeout(config.timeout_content);
|
||||||
typename ServerBase<socket_type>::Request>)> &resource_function)
|
auto response = std::shared_ptr<Response>(new Response(session, config.timeout_content), [this](Response *response_ptr) {
|
||||||
{
|
|
||||||
//Set timeout on the following boost::asio::async-read or write function
|
|
||||||
auto timer = this->get_timeout_timer(socket, config.timeout_content);
|
|
||||||
|
|
||||||
auto response = std::shared_ptr<Response>(new Response(socket), [this, request, timer]
|
|
||||||
(Response *response_ptr)
|
|
||||||
{
|
|
||||||
auto response = std::shared_ptr<Response>(response_ptr);
|
auto response = std::shared_ptr<Response>(response_ptr);
|
||||||
this->send(response, [this, response, request, timer](
|
response->send([this, response](const error_code &ec) {
|
||||||
const boost::system::error_code &ec)
|
if(!ec) {
|
||||||
{
|
|
||||||
if (timer)
|
|
||||||
timer->cancel();
|
|
||||||
if (!ec)
|
|
||||||
{
|
|
||||||
if(response->close_connection_after_response)
|
if(response->close_connection_after_response)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto range = request->header.equal_range(
|
auto range = response->session->request->header.equal_range("Connection");
|
||||||
"Connection");
|
for(auto it = range.first; it != range.second; it++) {
|
||||||
for (auto it = range.first; it != range.second; it++)
|
if(case_insensitive_equal(it->second, "close"))
|
||||||
{
|
|
||||||
if (boost::iequals(it->second, "close"))
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
else if(case_insensitive_equal(it->second, "keep-alive")) {
|
||||||
else if (boost::iequals(it->second, "keep-alive"))
|
auto new_session = std::make_shared<Session>(response->session->connection);
|
||||||
{
|
this->read_request_and_content(new_session);
|
||||||
this->read_request_and_content(
|
|
||||||
response->socket);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (request->http_version >= "1.1")
|
if(response->session->request->http_version >= "1.1") {
|
||||||
this->read_request_and_content(response->socket);
|
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
|
try {
|
||||||
{
|
resource_function(response, session->request);
|
||||||
resource_function(response, request);
|
|
||||||
}
|
}
|
||||||
catch (const std::exception &e)
|
catch(const std::exception &e) {
|
||||||
{
|
|
||||||
if(on_error)
|
if(on_error)
|
||||||
on_error(request, boost::system::error_code(boost::system::errc::operation_canceled,
|
on_error(session->request, make_error_code::make_error_code(errc::operation_canceled));
|
||||||
boost::system::generic_category()));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif //BASE_SERVER_HPP
|
|
||||||
|
|
|
@ -1,55 +1,42 @@
|
||||||
/*
|
#pragma once
|
||||||
* https://github.com/eidheim/Simple-Web-Server/
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
* Copyright (c) 2014-2016 Ole Christian Eidheim
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SERVER_HTTP_HPP
|
|
||||||
#define SERVER_HTTP_HPP
|
|
||||||
|
|
||||||
#include "base_server.hpp"
|
#include "base_server.hpp"
|
||||||
|
|
||||||
namespace SimpleWeb
|
namespace SimpleWeb {
|
||||||
{
|
|
||||||
|
|
||||||
template <class socket_type>
|
template <class socket_type>
|
||||||
class Server : public ServerBase<socket_type> {};
|
class Server : public ServerBase<socket_type> {};
|
||||||
|
|
||||||
typedef boost::asio::ip::tcp::socket HTTP;
|
using HTTP = asio::ip::tcp::socket;
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
class Server<HTTP> : public ServerBase<HTTP>
|
class Server<HTTP> : public ServerBase<HTTP> {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
Server() : ServerBase<HTTP>::ServerBase(80)
|
Server() noexcept : ServerBase<HTTP>::ServerBase(80) {}
|
||||||
{}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void accept()
|
void accept() override {
|
||||||
{
|
auto session = std::make_shared<Session>(create_connection(*io_service));
|
||||||
//Create new socket for this connection
|
|
||||||
//Shared_ptr is used to pass temporary objects to the asynchronous functions
|
|
||||||
auto socket = std::make_shared<HTTP>(*io_service);
|
|
||||||
|
|
||||||
acceptor->async_accept(*socket, [this, socket](const boost::system::error_code &ec)
|
acceptor->async_accept(*session->connection->socket, [this, session](const error_code &ec) {
|
||||||
{
|
auto lock = session->connection->handler_runner->continue_lock();
|
||||||
//Immediately start accepting a new connection (if io_service hasn't been stopped)
|
if(!lock)
|
||||||
if (ec != boost::asio::error::operation_aborted)
|
return;
|
||||||
accept();
|
|
||||||
|
|
||||||
if (!ec)
|
// Immediately start accepting a new connection (unless io_service has been stopped)
|
||||||
{
|
if(ec != asio::error::operation_aborted)
|
||||||
boost::asio::ip::tcp::no_delay option(true);
|
this->accept();
|
||||||
socket->set_option(option);
|
|
||||||
|
|
||||||
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)
|
else if(this->on_error)
|
||||||
on_error(std::shared_ptr<Request>(new Request(*socket)), ec);
|
this->on_error(session->request, ec);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
} // namespace SimpleWeb
|
||||||
|
|
||||||
#endif //SERVER_HTTP_HPP
|
|
||||||
|
|
|
@ -1,91 +1,82 @@
|
||||||
#ifndef HTTPS_SERVER_HPP
|
#pragma once
|
||||||
#define HTTPS_SERVER_HPP
|
|
||||||
|
|
||||||
#include "base_server.hpp"
|
#include "base_server.hpp"
|
||||||
#include <boost/asio/ssl.hpp>
|
|
||||||
#include <openssl/ssl.h>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace SimpleWeb
|
#ifdef USE_STANDALONE_ASIO
|
||||||
{
|
#include <asio/ssl.hpp>
|
||||||
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> HTTPS;
|
#else
|
||||||
|
#include <boost/asio/ssl.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
|
||||||
|
namespace SimpleWeb {
|
||||||
|
using HTTPS = asio::ssl::stream<asio::ip::tcp::socket>;
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
class Server<HTTPS> : public ServerBase<HTTPS>
|
class Server<HTTPS> : public ServerBase<HTTPS> {
|
||||||
{
|
|
||||||
std::string session_id_context;
|
std::string session_id_context;
|
||||||
bool set_session_id_context = false;
|
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.load_verify_file(verify_file);
|
||||||
context.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert |
|
context.set_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert | asio::ssl::verify_client_once);
|
||||||
boost::asio::ssl::verify_client_once);
|
|
||||||
set_session_id_context = true;
|
set_session_id_context = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void start()
|
void start() override {
|
||||||
{
|
if(set_session_id_context) {
|
||||||
if (set_session_id_context)
|
|
||||||
{
|
|
||||||
// Creating session_id_context from address:port but reversed due to small SSL_MAX_SSL_SESSION_ID_LENGTH
|
// 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 = std::to_string(config.port) + ':';
|
||||||
session_id_context.append(config.address.rbegin(), config.address.rend());
|
session_id_context.append(config.address.rbegin(), config.address.rend());
|
||||||
SSL_CTX_set_session_id_context(context.native_handle(),
|
SSL_CTX_set_session_id_context(context.native_handle(), reinterpret_cast<const unsigned char *>(session_id_context.data()),
|
||||||
reinterpret_cast<const unsigned char *>(session_id_context.data()),
|
std::min<size_t>(session_id_context.size(), SSL_MAX_SSL_SESSION_ID_LENGTH));
|
||||||
std::min<size_t>(session_id_context.size(),
|
|
||||||
SSL_MAX_SSL_SESSION_ID_LENGTH));
|
|
||||||
}
|
}
|
||||||
ServerBase::start();
|
ServerBase::start();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
boost::asio::ssl::context context;
|
asio::ssl::context context;
|
||||||
|
|
||||||
virtual void accept()
|
void accept() override {
|
||||||
{
|
auto session = std::make_shared<Session>(create_connection(*io_service, context));
|
||||||
//Create new socket for this connection
|
|
||||||
//Shared_ptr is used to pass temporary objects to the asynchronous functions
|
|
||||||
auto socket = std::make_shared<HTTPS>(*io_service, context);
|
|
||||||
|
|
||||||
acceptor->async_accept((*socket).lowest_layer(), [this, socket](const boost::system::error_code &ec)
|
acceptor->async_accept(session->connection->socket->lowest_layer(), [this, session](const error_code &ec) {
|
||||||
{
|
auto lock = session->connection->handler_runner->continue_lock();
|
||||||
//Immediately start accepting a new connection (if io_service hasn't been stopped)
|
if(!lock)
|
||||||
if (ec != boost::asio::error::operation_aborted)
|
return;
|
||||||
accept();
|
|
||||||
|
|
||||||
|
if(ec != asio::error::operation_aborted)
|
||||||
|
this->accept();
|
||||||
|
|
||||||
|
if(!ec) {
|
||||||
|
asio::ip::tcp::no_delay option(true);
|
||||||
|
error_code ec;
|
||||||
|
session->connection->socket->lowest_layer().set_option(option, 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)
|
if(!ec)
|
||||||
{
|
this->read_request_and_content(session);
|
||||||
boost::asio::ip::tcp::no_delay option(true);
|
else if(this->on_error)
|
||||||
socket->lowest_layer().set_option(option);
|
this->on_error(session->request, 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);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (on_error)
|
else if(this->on_error)
|
||||||
on_error(std::shared_ptr<Request>(new Request(*socket)), ec);
|
this->on_error(session->request, ec);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
} // namespace SimpleWeb
|
||||||
|
|
||||||
#endif //HTTPS_SERVER_HPP
|
|
||||||
|
|
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 <iostream>
|
||||||
#include <Kbhit.h>
|
#include <Kbhit.h>
|
||||||
#include <RakSleep.h>
|
#include <RakSleep.h>
|
||||||
|
#include <extern/sol/sol.hpp>
|
||||||
#include "MasterServer.hpp"
|
#include "MasterServer.hpp"
|
||||||
#include "RestServer.hpp"
|
#include "RestServer.hpp"
|
||||||
|
#include "AdminRest.hpp"
|
||||||
|
|
||||||
using namespace RakNet;
|
using namespace RakNet;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
unique_ptr<RestServer> restServer;
|
unique_ptr<RestServer> restServer;
|
||||||
unique_ptr<MasterServer> masterServer;
|
shared_ptr<MasterServer> masterServer;
|
||||||
bool run = true;
|
unique_ptr<AdminRest> restAdminServer;
|
||||||
|
|
||||||
int main()
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
masterServer.reset(new MasterServer(2000, 25560));
|
if (argc != 2)
|
||||||
restServer.reset(new RestServer(8080, masterServer->GetServers()));
|
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*/){
|
auto onExit = [](int /*sig*/){
|
||||||
restServer->stop();
|
restServer->stop();
|
||||||
|
restAdminServer->stop();
|
||||||
|
masterServer->luaStuff([](sol::state &state) {
|
||||||
|
sol::protected_function func = state["OnExit"];
|
||||||
|
if (func.valid())
|
||||||
|
func.call();
|
||||||
|
});
|
||||||
masterServer->Stop(false);
|
masterServer->Stop(false);
|
||||||
masterServer->Wait();
|
masterServer->Wait();
|
||||||
run = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
signal(SIGINT, onExit);
|
signal(SIGINT, onExit);
|
||||||
signal(SIGTERM, onExit);
|
signal(SIGTERM, onExit);
|
||||||
|
|
||||||
masterServer->Start();
|
masterServer->Start();
|
||||||
|
restServer->start();
|
||||||
thread server_thread([]() { restServer->start(); });
|
restAdminServer->start();
|
||||||
|
|
||||||
server_thread.join();
|
|
||||||
masterServer->Wait();
|
masterServer->Wait();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
281
apps/openmw-mp/Actors.cpp
Normal file
281
apps/openmw-mp/Actors.cpp
Normal file
|
@ -0,0 +1,281 @@
|
||||||
|
//
|
||||||
|
// 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"
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
|
"refId", sol::property(&Actor::getRefId, &Actor::setRefId),
|
||||||
|
"refNumIndex", sol::property(&Actor::getRefNumIndex, &Actor::setRefNumIndex),
|
||||||
|
"mpNum", sol::property(&Actor::getMpNum, &Actor::setMpNum)
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Actor::Actor() : NetActor()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Actor::getRefId() const
|
||||||
|
{
|
||||||
|
return actor->refId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Actor::setRefId(const std::string &refId)
|
||||||
|
{
|
||||||
|
actor->refId = refId;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Actor::getRefNumIndex() const
|
||||||
|
{
|
||||||
|
return actor->refNumIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Actor::setRefNumIndex(int refNumIndex)
|
||||||
|
{
|
||||||
|
actor->refNumIndex = refNumIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Actor::getMpNum() const
|
||||||
|
{
|
||||||
|
return actor->mpNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Actor::setMpNum(int 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 playersTable = lua.getState()->create_named_table("Actors");
|
||||||
|
|
||||||
|
playersTable.set_function("createActor", [&lua](){
|
||||||
|
return lua.getActorCtrl().createActor();
|
||||||
|
});
|
||||||
|
|
||||||
|
playersTable.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);
|
||||||
|
});
|
||||||
|
|
||||||
|
playersTable.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);
|
||||||
|
});
|
||||||
|
|
||||||
|
playersTable.set_function("requestList", [&lua](shared_ptr<Player> player, const std::string &cellDescription){
|
||||||
|
lua.getActorCtrl().requestList(player, Utils::getCellFromDescription(cellDescription));
|
||||||
|
});
|
||||||
|
|
||||||
|
playersTable.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 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->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 (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::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::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 (auto actor : serverCell->getActorList()->baseActors)
|
||||||
|
{
|
||||||
|
Actor *a = new Actor;
|
||||||
|
a->actor = actor;
|
||||||
|
actorList.emplace_back(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
return actorList;
|
||||||
|
}
|
55
apps/openmw-mp/Actors.hpp
Normal file
55
apps/openmw-mp/Actors.hpp
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
//
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
int getRefNumIndex() const;
|
||||||
|
void setRefNumIndex(int refNumIndex);
|
||||||
|
int getMpNum() const;
|
||||||
|
void setMpNum(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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
69
apps/openmw-mp/Books.cpp
Normal file
69
apps/openmw-mp/Books.cpp
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
//
|
||||||
|
// 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) : player(player), changed(false)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Books::~Books()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Books::addBook(const std::string &bookId)
|
||||||
|
{
|
||||||
|
if (!changed)
|
||||||
|
reset();
|
||||||
|
player->bookChanges.books.push_back({bookId});
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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::update()
|
||||||
|
{
|
||||||
|
if (!changed)
|
||||||
|
return;
|
||||||
|
changed = false;
|
||||||
|
|
||||||
|
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_BOOK);
|
||||||
|
|
||||||
|
packet->setPlayer(player);
|
||||||
|
packet->Send(/*toOthers*/ false);
|
||||||
|
}
|
32
apps/openmw-mp/Books.hpp
Normal file
32
apps/openmw-mp/Books.hpp
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
//
|
||||||
|
// Created by koncord on 15.08.17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class LuaState;
|
||||||
|
class Player;
|
||||||
|
|
||||||
|
class Books
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void Init(LuaState &lua);
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit Books(Player *player);
|
||||||
|
~Books();
|
||||||
|
|
||||||
|
void addBook(const std::string &bookId);
|
||||||
|
std::string getBookId(unsigned i) const;
|
||||||
|
unsigned getChanges() const;
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
void update();
|
||||||
|
private:
|
||||||
|
Player *player;
|
||||||
|
bool changed;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
project(tes3mp-server)
|
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(BUILD_WITH_PAWN "Enable Pawn language" OFF)
|
||||||
option(ENABLE_BREAKPAD "Enable Google Breakpad for Crash reporting" OFF)
|
option(ENABLE_BREAKPAD "Enable Google Breakpad for Crash reporting" OFF)
|
||||||
|
|
||||||
|
@ -22,89 +15,58 @@ if(ENABLE_BREAKPAD)
|
||||||
include_directories(${CMAKE_SOURCE_DIR}/extern/breakpad/src ${Breakpad_Headers})
|
include_directories(${CMAKE_SOURCE_DIR}/extern/breakpad/src ${Breakpad_Headers})
|
||||||
endif(ENABLE_BREAKPAD)
|
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(BUILD_WITH_LUA "Enable Terra/Lua language" ON)
|
||||||
option(FORCE_LUA "Use Lua instead Terra" OFF)
|
option(FORCE_LUA "Use Lua instead Terra" OFF)
|
||||||
if(BUILD_WITH_LUA)
|
|
||||||
|
|
||||||
#set(Terra_ROOT ${CMAKE_SOURCE_DIR}/external/terra/)
|
#set(Terra_ROOT ${CMAKE_SOURCE_DIR}/external/terra/)
|
||||||
if(WIN32 OR FORCE_LUA)
|
#if(WIN32 OR FORCE_LUA)
|
||||||
find_package(Lua51 REQUIRED)
|
find_package(LuaJit REQUIRED)
|
||||||
MESSAGE(STATUS "Found LUA_LIBRARY: ${LUA_LIBRARY}")
|
#[[else()
|
||||||
MESSAGE(STATUS "Found LUA_INCLUDE_DIR: ${LUA_INCLUDE_DIR}")
|
|
||||||
else()
|
|
||||||
find_package(Terra REQUIRED)
|
find_package(Terra REQUIRED)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DENABLE_TERRA")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DENABLE_TERRA")]]
|
||||||
endif()
|
#endif()
|
||||||
set(LuaScript_Sources
|
set(LuaScript_Headers ${Terra_INCLUDES} ${LUA_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/extern/sol/sol.hpp)
|
||||||
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")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DENABLE_LUA")
|
||||||
include_directories(${Terra_INCLUDES} ${LUA_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/extern/LuaBridge)
|
include_directories(${Terra_INCLUDES} ${LUA_INCLUDE_DIR} ${LUAJIT_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/extern/sol)
|
||||||
endif(BUILD_WITH_LUA)
|
|
||||||
|
|
||||||
set(NativeScript_Sources
|
|
||||||
Script/LangNative/LangNative.cpp
|
|
||||||
)
|
|
||||||
set(NativeScript_Headers
|
|
||||||
Script/LangNative/LangNative.hpp
|
|
||||||
)
|
|
||||||
|
|
||||||
# local files
|
# local files
|
||||||
set(SERVER
|
set(SERVER
|
||||||
main.cpp
|
main.cpp
|
||||||
Player.cpp
|
Player.cpp Players.cpp
|
||||||
Networking.cpp
|
Networking.cpp
|
||||||
MasterClient.cpp
|
MasterClient.cpp
|
||||||
Cell.cpp
|
Cell.cpp
|
||||||
CellController.cpp
|
CellController.cpp
|
||||||
Utils.cpp
|
Utils.cpp
|
||||||
Script/Script.cpp Script/ScriptFunction.cpp
|
CharClass.cpp
|
||||||
Script/ScriptFunctions.cpp
|
Inventory.cpp
|
||||||
|
Settings.cpp
|
||||||
Script/Functions/Actors.cpp Script/Functions/World.cpp Script/Functions/Miscellaneous.cpp
|
Timer.cpp
|
||||||
|
Books.cpp
|
||||||
Script/Functions/Books.cpp Script/Functions/Cells.cpp Script/Functions/CharClass.cpp
|
GUI.cpp
|
||||||
Script/Functions/Chat.cpp Script/Functions/Dialogue.cpp Script/Functions/Factions.cpp
|
Dialogue.cpp
|
||||||
Script/Functions/GUI.cpp Script/Functions/Items.cpp Script/Functions/Mechanics.cpp
|
Factions.cpp
|
||||||
Script/Functions/Positions.cpp Script/Functions/Quests.cpp Script/Functions/Settings.cpp
|
Cells.cpp
|
||||||
Script/Functions/Spells.cpp Script/Functions/Stats.cpp Script/Functions/Timer.cpp
|
Quests.cpp
|
||||||
|
Spells.cpp
|
||||||
Script/API/TimerAPI.cpp Script/API/PublicFnAPI.cpp
|
Actors.cpp
|
||||||
${PawnScript_Sources}
|
NetActor.cpp
|
||||||
${LuaScript_Sources}
|
CellState.cpp
|
||||||
${NativeScript_Sources}
|
Object.cpp
|
||||||
|
stacktrace.cpp
|
||||||
|
Window.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
|
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})
|
source_group(tes3mp-server FILES ${SERVER} ${SERVER_HEADER})
|
||||||
|
|
||||||
|
@ -134,7 +96,7 @@ set(PROCESSORS_PLAYER
|
||||||
processors/player/ProcessorPlayerResurrect.hpp processors/player/ProcessorPlayerShapeshift.hpp
|
processors/player/ProcessorPlayerResurrect.hpp processors/player/ProcessorPlayerShapeshift.hpp
|
||||||
processors/player/ProcessorPlayerSkill.hpp processors/player/ProcessorPlayerSpeech.hpp
|
processors/player/ProcessorPlayerSkill.hpp processors/player/ProcessorPlayerSpeech.hpp
|
||||||
processors/player/ProcessorPlayerSpellbook.hpp processors/player/ProcessorPlayerStatsDynamic.hpp
|
processors/player/ProcessorPlayerSpellbook.hpp processors/player/ProcessorPlayerStatsDynamic.hpp
|
||||||
processors/player/ProcessorPlayerTopic.hpp
|
processors/player/ProcessorPlayerTopic.hpp processors/player/ProcessorGUIWindow.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
source_group(tes3mp-server\\processors\\player FILES ${PROCESSORS_PLAYER})
|
source_group(tes3mp-server\\processors\\player FILES ${PROCESSORS_PLAYER})
|
||||||
|
@ -171,7 +133,15 @@ add_executable(tes3mp-server
|
||||||
${PROCESSORS_ACTOR} ${PROCESSORS_PLAYER} ${PROCESSORS_WORLD} ${PROCESSORS}
|
${PROCESSORS_ACTOR} ${PROCESSORS_PLAYER} ${PROCESSORS_WORLD} ${PROCESSORS}
|
||||||
${APPLE_BUNDLE_RESOURCES}
|
${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>)
|
||||||
|
|
||||||
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||||
|
target_compile_options(tes3mp-server PRIVATE -std=gnu++14 -Wno-ignored-qualifiers -ftemplate-depth=2048)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_link_libraries(tes3mp-server
|
target_link_libraries(tes3mp-server
|
||||||
#${Boost_SYSTEM_LIBRARY}
|
#${Boost_SYSTEM_LIBRARY}
|
||||||
|
@ -182,9 +152,8 @@ target_link_libraries(tes3mp-server
|
||||||
components
|
components
|
||||||
${Terra_LIBRARY}
|
${Terra_LIBRARY}
|
||||||
${LUA_LIBRARIES}
|
${LUA_LIBRARIES}
|
||||||
${Pawn_LIBRARY}
|
${LUAJIT_LIBRARY}
|
||||||
${Breakpad_Library}
|
${Breakpad_Library}
|
||||||
${CallFF_LIBRARY}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (UNIX)
|
if (UNIX)
|
||||||
|
|
|
@ -2,13 +2,14 @@
|
||||||
// Created by koncord on 18.02.17.
|
// Created by koncord on 18.02.17.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "Cell.hpp"
|
#include <iostream>
|
||||||
|
|
||||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||||
|
|
||||||
#include <iostream>
|
#include "Script/EventController.hpp"
|
||||||
|
#include "Networking.hpp"
|
||||||
|
|
||||||
|
#include "Cell.hpp"
|
||||||
#include "Player.hpp"
|
#include "Player.hpp"
|
||||||
#include "Script/Script.hpp"
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
@ -48,7 +49,8 @@ void Cell::addPlayer(Player *player)
|
||||||
|
|
||||||
LOG_APPEND(Log::LOG_INFO, "- Adding %s to Cell %s", player->npc.mName.c_str(), getDescription().c_str());
|
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);
|
players.push_back(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());
|
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);
|
players.erase(it);
|
||||||
return;
|
return;
|
||||||
|
@ -81,27 +84,27 @@ void Cell::readActorList(unsigned char packetID, const mwmp::BaseActorList *newA
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < newActorList->count; i++)
|
for (unsigned int i = 0; i < newActorList->count; i++)
|
||||||
{
|
{
|
||||||
mwmp::BaseActor newActor = newActorList->baseActors.at(i);
|
auto &newActor = newActorList->baseActors.at(i);
|
||||||
mwmp::BaseActor *cellActor;
|
mwmp::BaseActor *cellActor;
|
||||||
|
|
||||||
if (containsActor(newActor.refNumIndex, newActor.mpNum))
|
if (containsActor(newActor->refNumIndex, newActor->mpNum))
|
||||||
{
|
{
|
||||||
cellActor = getActor(newActor.refNumIndex, newActor.mpNum);
|
cellActor = getActor(newActor->refNumIndex, newActor->mpNum);
|
||||||
|
|
||||||
switch (packetID)
|
switch (packetID)
|
||||||
{
|
{
|
||||||
case ID_ACTOR_POSITION:
|
case ID_ACTOR_POSITION:
|
||||||
|
|
||||||
cellActor->hasPositionData = true;
|
cellActor->hasPositionData = true;
|
||||||
cellActor->position = newActor.position;
|
cellActor->position = newActor->position;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ID_ACTOR_STATS_DYNAMIC:
|
case ID_ACTOR_STATS_DYNAMIC:
|
||||||
|
|
||||||
cellActor->hasStatsDynamicData = true;
|
cellActor->hasStatsDynamicData = true;
|
||||||
cellActor->creatureStats.mDynamic[0] = newActor.creatureStats.mDynamic[0];
|
cellActor->creatureStats.mDynamic[0] = newActor->creatureStats.mDynamic[0];
|
||||||
cellActor->creatureStats.mDynamic[1] = newActor.creatureStats.mDynamic[1];
|
cellActor->creatureStats.mDynamic[1] = newActor->creatureStats.mDynamic[1];
|
||||||
cellActor->creatureStats.mDynamic[2] = newActor.creatureStats.mDynamic[2];
|
cellActor->creatureStats.mDynamic[2] = newActor->creatureStats.mDynamic[2];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,9 +119,9 @@ bool Cell::containsActor(int refNumIndex, int mpNum)
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < cellActorList.baseActors.size(); i++)
|
for (unsigned int i = 0; i < cellActorList.baseActors.size(); i++)
|
||||||
{
|
{
|
||||||
mwmp::BaseActor actor = cellActorList.baseActors.at(i);
|
auto &actor = cellActorList.baseActors.at(i);
|
||||||
|
|
||||||
if (actor.refNumIndex == refNumIndex && actor.mpNum == mpNum)
|
if (actor->refNumIndex == refNumIndex && actor->mpNum == mpNum)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -128,28 +131,28 @@ mwmp::BaseActor *Cell::getActor(int refNumIndex, int mpNum)
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < cellActorList.baseActors.size(); i++)
|
for (unsigned int i = 0; i < cellActorList.baseActors.size(); i++)
|
||||||
{
|
{
|
||||||
mwmp::BaseActor *actor = &cellActorList.baseActors.at(i);
|
auto &actor = cellActorList.baseActors.at(i);
|
||||||
|
|
||||||
if (actor->refNumIndex == refNumIndex && actor->mpNum == mpNum)
|
if (actor->refNumIndex == refNumIndex && actor->mpNum == mpNum)
|
||||||
return actor;
|
return actor.get();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 refNumIndex = (*it)->refNumIndex;
|
||||||
int mpNum = (*it).mpNum;
|
int mpNum = (*it)->mpNum;
|
||||||
|
|
||||||
bool foundActor = false;
|
bool foundActor = false;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < newActorList->count; i++)
|
for (unsigned int i = 0; i < newActorList->count; i++)
|
||||||
{
|
{
|
||||||
mwmp::BaseActor newActor = newActorList->baseActors.at(i);
|
auto &newActor = newActorList->baseActors.at(i);
|
||||||
|
|
||||||
if (newActor.refNumIndex == refNumIndex && newActor.mpNum == mpNum)
|
if (newActor->refNumIndex == refNumIndex && newActor->mpNum == mpNum)
|
||||||
{
|
{
|
||||||
it = cellActorList.baseActors.erase(it);
|
it = cellActorList.baseActors.erase(it);
|
||||||
foundActor = true;
|
foundActor = true;
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
#include "CellController.hpp"
|
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <apps/openmw-mp/Script/EventController.hpp>
|
||||||
|
#include "Networking.hpp"
|
||||||
|
|
||||||
|
#include "CellController.hpp"
|
||||||
#include "Cell.hpp"
|
#include "Cell.hpp"
|
||||||
#include "Player.hpp"
|
#include "Player.hpp"
|
||||||
#include "Script/Script.hpp"
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
@ -123,7 +125,8 @@ void CellController::removeCell(Cell *cell)
|
||||||
{
|
{
|
||||||
if (*it != nullptr && *it == 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());
|
LOG_APPEND(Log::LOG_INFO, "- Removing %s from CellController", cell->getDescription().c_str());
|
||||||
|
|
||||||
delete *it;
|
delete *it;
|
||||||
|
|
30
apps/openmw-mp/CellState.cpp
Normal file
30
apps/openmw-mp/CellState.cpp
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
//
|
||||||
|
// Created by koncord on 25.08.17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Script/LuaState.hpp"
|
||||||
|
|
||||||
|
#include "CellState.hpp"
|
||||||
|
|
||||||
|
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(state)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int 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:
|
||||||
|
int getStateType() const;
|
||||||
|
std::string getDescription() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
mwmp::CellState state;
|
||||||
|
};
|
100
apps/openmw-mp/Cells.cpp
Normal file
100
apps/openmw-mp/Cells.cpp
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
//
|
||||||
|
// Created by koncord on 25.08.17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||||
|
|
||||||
|
#include "Script/LuaState.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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
changedCell = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
changedCell = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
43
apps/openmw-mp/Cells.hpp
Normal file
43
apps/openmw-mp/Cells.hpp
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
//
|
||||||
|
// 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
156
apps/openmw-mp/CharClass.cpp
Normal file
156
apps/openmw-mp/CharClass.cpp
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
//
|
||||||
|
// 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) : player(player), changed(false)
|
||||||
|
{
|
||||||
|
printf("CharClass::CharClass()\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
CharClass::~CharClass()
|
||||||
|
{
|
||||||
|
printf("CharClass::~CharClass()\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
string CharClass::getDefault() const
|
||||||
|
{
|
||||||
|
return player->charClass.mId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CharClass::setDefault(const string &className)
|
||||||
|
{
|
||||||
|
player->charClass.mId = className;
|
||||||
|
changed = true;
|
||||||
|
printf("CharClass::setDefault()\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CharClass::isCustom() const
|
||||||
|
{
|
||||||
|
return player->charClass.mId.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CharClass::setName(const string &className)
|
||||||
|
{
|
||||||
|
player->charClass.mName = className;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CharClass::getSpecialization() const
|
||||||
|
{
|
||||||
|
return player->charClass.mData.mSpecialization;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CharClass::setSpecialization(int spec)
|
||||||
|
{
|
||||||
|
auto &data = player->charClass.mData;
|
||||||
|
data.mSpecialization = spec;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CharClass::update()
|
||||||
|
{
|
||||||
|
if (!changed)
|
||||||
|
return;
|
||||||
|
changed = false;
|
||||||
|
printf("CharClass::update()\n");
|
||||||
|
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_CHARCLASS);
|
||||||
|
packet->setPlayer(player);
|
||||||
|
packet->Send(false);
|
||||||
|
}
|
50
apps/openmw-mp/CharClass.hpp
Normal file
50
apps/openmw-mp/CharClass.hpp
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
//
|
||||||
|
// Created by koncord on 12.08.17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
class LuaState;
|
||||||
|
class Player;
|
||||||
|
|
||||||
|
class CharClass
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void Init(LuaState &lua);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CharClass(Player *player);
|
||||||
|
~CharClass();
|
||||||
|
void update();
|
||||||
|
|
||||||
|
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:
|
||||||
|
// not controlled pointer
|
||||||
|
Player *player;
|
||||||
|
bool changed;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
63
apps/openmw-mp/Dialogue.cpp
Normal file
63
apps/openmw-mp/Dialogue.cpp
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
//
|
||||||
|
// 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,
|
||||||
|
"getChanges", &Dialogue::getChanges,
|
||||||
|
"reset", &Dialogue::reset);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Dialogue::Dialogue(Player *player) : player(player), changed(false)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Dialogue::reset()
|
||||||
|
{
|
||||||
|
player->topicChanges.topics.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dialogue::update()
|
||||||
|
{
|
||||||
|
if (!changed)
|
||||||
|
return;
|
||||||
|
changed = false;
|
||||||
|
|
||||||
|
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 (!changed)
|
||||||
|
reset();
|
||||||
|
changed = true;
|
||||||
|
player->topicChanges.topics.push_back({topicId});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Dialogue::getTopicId(unsigned int i) const
|
||||||
|
{
|
||||||
|
return player->topicChanges.topics.at(i).topicId;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int Dialogue::getChanges() const
|
||||||
|
{
|
||||||
|
return player->topicChanges.topics.size();
|
||||||
|
}
|
30
apps/openmw-mp/Dialogue.hpp
Normal file
30
apps/openmw-mp/Dialogue.hpp
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
//
|
||||||
|
// Created by koncord on 15.08.17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class LuaState;
|
||||||
|
class Player;
|
||||||
|
|
||||||
|
class Dialogue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void Init(LuaState &lua);
|
||||||
|
public:
|
||||||
|
explicit Dialogue(Player *player);
|
||||||
|
|
||||||
|
void addTopic(const std::string &topicId);
|
||||||
|
std::string getTopicId(unsigned int i) const;
|
||||||
|
unsigned int getChanges() const;
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
void update();
|
||||||
|
private:
|
||||||
|
Player *player;
|
||||||
|
bool changed;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
155
apps/openmw-mp/Factions.cpp
Normal file
155
apps/openmw-mp/Factions.cpp
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
//
|
||||||
|
// 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,
|
||||||
|
"changesAction", sol::property(&Factions::getFactionChangesAction, &Factions::setFactionChangesAction),
|
||||||
|
"getFaction", &Factions::getFaction,
|
||||||
|
"setFaction", &Factions::setFaction,
|
||||||
|
"clear", &Factions::clear,
|
||||||
|
"size", &Factions::size
|
||||||
|
|
||||||
|
);
|
||||||
|
/*"InitializeFactionChanges", FactionFunctions::InitializeFactionChanges,
|
||||||
|
"GetFactionChangesSize", FactionFunctions::GetFactionChangesSize,
|
||||||
|
"GetFactionChangesAction", FactionFunctions::GetFactionChangesAction,
|
||||||
|
"GetFactionId", FactionFunctions::GetFactionId,
|
||||||
|
"GetFactionRank", FactionFunctions::GetFactionRank,
|
||||||
|
"GetFactionExpulsionState", FactionFunctions::GetFactionExpulsionState,
|
||||||
|
"GetFactionReputation", FactionFunctions::GetFactionReputation,
|
||||||
|
"SetFactionChangesAction", FactionFunctions::SetFactionChangesAction,
|
||||||
|
"SetFactionId", FactionFunctions::SetFactionId,
|
||||||
|
"SetFactionRank", FactionFunctions::SetFactionRank,
|
||||||
|
"SetFactionExpulsionState", FactionFunctions::SetFactionExpulsionState,
|
||||||
|
"SetFactionReputation", FactionFunctions::SetFactionReputation,
|
||||||
|
"AddFaction", FactionFunctions::AddFaction,
|
||||||
|
"SendFactionChanges", FactionFunctions::SendFactionChanges*/
|
||||||
|
}
|
||||||
|
|
||||||
|
Factions::Factions(Player *player): player(player), changed(false)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Factions::~Factions()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Factions::update()
|
||||||
|
{
|
||||||
|
if (!changed)
|
||||||
|
return;
|
||||||
|
changed = false;
|
||||||
|
|
||||||
|
auto packet =mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_FACTION);
|
||||||
|
packet->setPlayer(player);
|
||||||
|
packet->Send(/*toOthers*/ false);
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Factions::getFactionChangesAction() const
|
||||||
|
{
|
||||||
|
return player->factionChanges.action;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Factions::setFactionChangesAction(int action)
|
||||||
|
{
|
||||||
|
player->factionChanges.action = action;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Factions::addFaction(Faction faction)
|
||||||
|
{
|
||||||
|
player->factionChanges.factions.push_back(faction.faction);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Faction Factions::getFaction(int id) const
|
||||||
|
{
|
||||||
|
return Faction(player->factionChanges.factions.at(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Factions::setFaction(int id, Faction faction)
|
||||||
|
{
|
||||||
|
player->factionChanges.factions.at(id) = faction.faction;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Factions::clear()
|
||||||
|
{
|
||||||
|
player->factionChanges.factions.clear();
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.rank = rank;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Faction::getFactionExpulsionState() const
|
||||||
|
{
|
||||||
|
return faction.isExpelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Faction::setFactionExpulsionState(bool expulsionState)
|
||||||
|
{
|
||||||
|
faction.isExpelled = expulsionState;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Faction::getFactionReputation() const
|
||||||
|
{
|
||||||
|
return faction.reputation;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Faction::setFactionReputation(int reputation)
|
||||||
|
{
|
||||||
|
faction.reputation = reputation;
|
||||||
|
}
|
59
apps/openmw-mp/Factions.hpp
Normal file
59
apps/openmw-mp/Factions.hpp
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
//
|
||||||
|
// Created by koncord on 17.08.17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <components/openmw-mp/Base/BasePlayer.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
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void Init(LuaState &lua);
|
||||||
|
public:
|
||||||
|
explicit Factions(Player *player);
|
||||||
|
~Factions();
|
||||||
|
|
||||||
|
void update();
|
||||||
|
|
||||||
|
int getFactionChangesAction() const;
|
||||||
|
void setFactionChangesAction(int action);
|
||||||
|
|
||||||
|
void addFaction(Faction faction);
|
||||||
|
Faction getFaction(int id) const;
|
||||||
|
void setFaction(int id, Faction faction);
|
||||||
|
size_t size() const;
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
private:
|
||||||
|
mwmp::Faction tempFaction;
|
||||||
|
Player *player;
|
||||||
|
bool changed;
|
||||||
|
};
|
145
apps/openmw-mp/GUI.cpp
Normal file
145
apps/openmw-mp/GUI.cpp
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
//
|
||||||
|
// Created by koncord on 15.08.17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||||
|
#include "GUI.hpp"
|
||||||
|
#include "Player.hpp"
|
||||||
|
#include "Networking.hpp"
|
||||||
|
|
||||||
|
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): player(player), changed(false)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
GUI::~GUI()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void GUI::update()
|
||||||
|
{
|
||||||
|
if (!changed)
|
||||||
|
return;
|
||||||
|
changed = false;
|
||||||
|
|
||||||
|
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_GUI_MESSAGEBOX);
|
||||||
|
packet->setPlayer(player);
|
||||||
|
packet->Send(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GUI::messageBox(int id, const char *label)
|
||||||
|
{
|
||||||
|
player->guiMessageBox.id = id;
|
||||||
|
player->guiMessageBox.label = label;
|
||||||
|
player->guiMessageBox.type = Player::GUIMessageBox::MessageBox;
|
||||||
|
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GUI::customMessageBox(int id, const char *label, const char *buttons)
|
||||||
|
{
|
||||||
|
player->guiMessageBox.id = id;
|
||||||
|
player->guiMessageBox.label = label;
|
||||||
|
player->guiMessageBox.buttons = buttons;
|
||||||
|
player->guiMessageBox.type = Player::GUIMessageBox::CustomMessageBox;
|
||||||
|
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GUI::inputDialog(int id, const char *label)
|
||||||
|
{
|
||||||
|
player->guiMessageBox.id = id;
|
||||||
|
player->guiMessageBox.label = label;
|
||||||
|
player->guiMessageBox.type = Player::GUIMessageBox::InputDialog;
|
||||||
|
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GUI::passwordDialog(int id, const char *label, const char *note)
|
||||||
|
{
|
||||||
|
player->guiMessageBox.id = id;
|
||||||
|
player->guiMessageBox.label = label;
|
||||||
|
player->guiMessageBox.note = note;
|
||||||
|
player->guiMessageBox.type = Player::GUIMessageBox::PasswordDialog;
|
||||||
|
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GUI::listBox(int id, const char *label, const char *items)
|
||||||
|
{
|
||||||
|
player->guiMessageBox.id = id;
|
||||||
|
player->guiMessageBox.label = label;
|
||||||
|
player->guiMessageBox.data = items;
|
||||||
|
player->guiMessageBox.type = Player::GUIMessageBox::ListBox;
|
||||||
|
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
44
apps/openmw-mp/GUI.hpp
Normal file
44
apps/openmw-mp/GUI.hpp
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
//
|
||||||
|
// Created by koncord on 15.08.17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Window.hpp"
|
||||||
|
|
||||||
|
class LuaState;
|
||||||
|
class Player;
|
||||||
|
|
||||||
|
class GUI
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void Init(LuaState &lua);
|
||||||
|
public:
|
||||||
|
explicit GUI(Player *player);
|
||||||
|
~GUI();
|
||||||
|
|
||||||
|
void update();
|
||||||
|
|
||||||
|
void messageBox(int id, const char *label);
|
||||||
|
|
||||||
|
void customMessageBox(int id, const char *label, const char *buttons);
|
||||||
|
void inputDialog(int id, const char *label);
|
||||||
|
void passwordDialog(int id, const char *label, const char *note);
|
||||||
|
|
||||||
|
void listBox(int id, const char *label, const char *items);
|
||||||
|
|
||||||
|
//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();
|
||||||
|
private:
|
||||||
|
Player *player;
|
||||||
|
bool changed;
|
||||||
|
std::unordered_map<int, std::shared_ptr<Window>> windows;
|
||||||
|
int lastWindowId;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
173
apps/openmw-mp/Inventory.cpp
Normal file
173
apps/openmw-mp/Inventory.cpp
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
//
|
||||||
|
// 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"
|
||||||
|
|
||||||
|
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(0)
|
||||||
|
{
|
||||||
|
printf("Inventory::Inventory()\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
Inventory::~Inventory()
|
||||||
|
{
|
||||||
|
printf("Inventory::~Inventory()\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Inventory::update()
|
||||||
|
{
|
||||||
|
printf("Inventory::update()");
|
||||||
|
/*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;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Inventory::InitializeInventoryChanges()
|
||||||
|
{
|
||||||
|
netActor->getNetCreature()->inventoryChanges.items.clear();
|
||||||
|
netActor->getNetCreature()->inventoryChanges.action = mwmp::InventoryChanges::SET;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
netActor->getNetCreature()->equipmentItems[slot].refId = refId;
|
||||||
|
netActor->getNetCreature()->equipmentItems[slot].count = count;
|
||||||
|
netActor->getNetCreature()->equipmentItems[slot].charge = charge;
|
||||||
|
|
||||||
|
if (!Utils::vectorContains(&netActor->getNetCreature()->equipmentIndexChanges, slot))
|
||||||
|
netActor->getNetCreature()->equipmentIndexChanges.push_back(slot);
|
||||||
|
|
||||||
|
equipmentChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Inventory::unequipItem( unsigned short slot)
|
||||||
|
{
|
||||||
|
equipItem(slot, "", 0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Inventory::addItem(const std::string &refId, unsigned int count, int charge)
|
||||||
|
{
|
||||||
|
if (inventoryChanged == mwmp::InventoryChanges::REMOVE)
|
||||||
|
return;
|
||||||
|
if (inventoryChanged == 0)
|
||||||
|
InitializeInventoryChanges();
|
||||||
|
|
||||||
|
mwmp::Item item;
|
||||||
|
item.refId = refId;
|
||||||
|
item.count = count;
|
||||||
|
item.charge = charge;
|
||||||
|
|
||||||
|
netActor->getNetCreature()->inventoryChanges.items.push_back(item);
|
||||||
|
netActor->getNetCreature()->inventoryChanges.action = mwmp::InventoryChanges::ADD;
|
||||||
|
inventoryChanged = netActor->getNetCreature()->inventoryChanges.action;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Inventory::removeItem(const std::string &refId, unsigned short count)
|
||||||
|
{
|
||||||
|
if (inventoryChanged == mwmp::InventoryChanges::ADD)
|
||||||
|
return;
|
||||||
|
if (inventoryChanged == 0)
|
||||||
|
InitializeInventoryChanges();
|
||||||
|
|
||||||
|
mwmp::Item item;
|
||||||
|
item.refId = refId;
|
||||||
|
item.count = count;
|
||||||
|
|
||||||
|
netActor->getNetCreature()->inventoryChanges.items.push_back(item);
|
||||||
|
netActor->getNetCreature()->inventoryChanges.action = mwmp::InventoryChanges::REMOVE;
|
||||||
|
inventoryChanged = netActor->getNetCreature()->inventoryChanges.action;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Inventory::hasItemEquipped(const std::string &refId) const
|
||||||
|
{
|
||||||
|
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; slot++)
|
||||||
|
if (Misc::StringUtils::ciEqual(netActor->getNetCreature()->equipmentItems[slot].refId, refId))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<std::string, int, int> Inventory::getEquipmentItem(unsigned short slot) const
|
||||||
|
{
|
||||||
|
const auto &item = netActor->getNetCreature()->equipmentItems[slot];
|
||||||
|
return make_tuple(item.refId, item.count, item.charge);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<std::string, int, int> Inventory::getInventoryItem(unsigned int slot) const
|
||||||
|
{
|
||||||
|
const auto &item = netActor->getNetCreature()->inventoryChanges.items.at(slot);
|
||||||
|
return make_tuple(item.refId, item.count, item.charge);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Inventory::resetEquipmentFlag()
|
||||||
|
{
|
||||||
|
equipmentChanged = false;
|
||||||
|
|
||||||
|
netActor->getNetCreature()->equipmentIndexChanges.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Inventory::isEquipmentChanged()
|
||||||
|
{
|
||||||
|
return equipmentChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Inventory::resetInventoryFlag()
|
||||||
|
{
|
||||||
|
inventoryChanged = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Inventory::inventoryChangeType()
|
||||||
|
{
|
||||||
|
return inventoryChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
63
apps/openmw-mp/Inventory.hpp
Normal file
63
apps/openmw-mp/Inventory.hpp
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
//
|
||||||
|
// 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();
|
||||||
|
int inventoryChangeType();
|
||||||
|
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);
|
||||||
|
void removeItem(const std::string& refId, unsigned short count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param slot
|
||||||
|
* @return refid, count, charge
|
||||||
|
*/
|
||||||
|
std::tuple<std::string,int, int> getInventoryItem(unsigned int slot) const;
|
||||||
|
|
||||||
|
|
||||||
|
// equipment
|
||||||
|
void equipItem(unsigned short slot, const std::string& refId, unsigned int count, int charge);
|
||||||
|
void unequipItem(unsigned short slot);
|
||||||
|
|
||||||
|
bool hasItemEquipped(const std::string& refId) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param slot
|
||||||
|
* @return refid, count, charge
|
||||||
|
*/
|
||||||
|
std::tuple<std::string,int, int> getEquipmentItem(unsigned short slot) const;
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
void InitializeInventoryChanges();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// not controlled pointer
|
||||||
|
NetActor *netActor;
|
||||||
|
bool equipmentChanged;
|
||||||
|
int inventoryChanged;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,18 +2,23 @@
|
||||||
// Created by koncord on 14.08.16.
|
// Created by koncord on 14.08.16.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <RakSleep.h>
|
|
||||||
#include <Getche.h>
|
|
||||||
#include <sstream>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
#include <Getche.h>
|
||||||
#include <RakPeerInterface.h>
|
#include <RakPeerInterface.h>
|
||||||
#include "MasterClient.hpp"
|
#include <RakSleep.h>
|
||||||
|
|
||||||
#include <components/openmw-mp/Log.hpp>
|
#include <components/openmw-mp/Log.hpp>
|
||||||
#include <components/openmw-mp/Version.hpp>
|
#include <components/openmw-mp/Version.hpp>
|
||||||
#include <components/openmw-mp/Master/PacketMasterAnnounce.hpp>
|
#include <components/openmw-mp/Master/PacketMasterAnnounce.hpp>
|
||||||
|
|
||||||
|
#include "MasterClient.hpp"
|
||||||
#include "Networking.hpp"
|
#include "Networking.hpp"
|
||||||
|
|
||||||
|
#include "Players.hpp"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace mwmp;
|
using namespace mwmp;
|
||||||
using namespace RakNet;
|
using namespace RakNet;
|
||||||
|
@ -32,7 +37,7 @@ MasterClient::MasterClient(RakNet::RakPeerInterface *peer, std::string queryAddr
|
||||||
void MasterClient::SetPlayers(unsigned pl)
|
void MasterClient::SetPlayers(unsigned pl)
|
||||||
{
|
{
|
||||||
mutexData.lock();
|
mutexData.lock();
|
||||||
if (queryData.GetPlayers() != pl)
|
if ((unsigned) queryData.GetPlayers() != pl)
|
||||||
{
|
{
|
||||||
queryData.SetPlayers(pl);
|
queryData.SetPlayers(pl);
|
||||||
updated = true;
|
updated = true;
|
||||||
|
@ -43,7 +48,7 @@ void MasterClient::SetPlayers(unsigned pl)
|
||||||
void MasterClient::SetMaxPlayers(unsigned pl)
|
void MasterClient::SetMaxPlayers(unsigned pl)
|
||||||
{
|
{
|
||||||
mutexData.lock();
|
mutexData.lock();
|
||||||
if (queryData.GetMaxPlayers() != pl)
|
if ((unsigned) queryData.GetMaxPlayers() != pl)
|
||||||
{
|
{
|
||||||
queryData.SetMaxPlayers(pl);
|
queryData.SetMaxPlayers(pl);
|
||||||
updated = true;
|
updated = true;
|
||||||
|
@ -108,7 +113,7 @@ void MasterClient::SetRuleValue(std::string key, double value)
|
||||||
void MasterClient::PushPlugin(Plugin plugin)
|
void MasterClient::PushPlugin(Plugin plugin)
|
||||||
{
|
{
|
||||||
mutexData.lock();
|
mutexData.lock();
|
||||||
queryData.plugins.push_back(plugin);
|
queryData.plugins.push_back(move(plugin));
|
||||||
updated = true;
|
updated = true;
|
||||||
mutexData.unlock();
|
mutexData.unlock();
|
||||||
}
|
}
|
||||||
|
@ -128,6 +133,10 @@ bool MasterClient::Process(RakNet::Packet *packet)
|
||||||
case ID_CONNECTION_REQUEST_ACCEPTED:
|
case ID_CONNECTION_REQUEST_ACCEPTED:
|
||||||
case ID_DISCONNECTION_NOTIFICATION:
|
case ID_DISCONNECTION_NOTIFICATION:
|
||||||
break;
|
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:
|
case ID_MASTER_QUERY:
|
||||||
break;
|
break;
|
||||||
case ID_MASTER_ANNOUNCE:
|
case ID_MASTER_ANNOUNCE:
|
||||||
|
@ -148,7 +157,7 @@ bool MasterClient::Process(RakNet::Packet *packet)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
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 false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -195,22 +204,22 @@ void MasterClient::Thread()
|
||||||
queryData.SetPassword((int) Networking::get().isPassworded());
|
queryData.SetPassword((int) Networking::get().isPassworded());
|
||||||
queryData.SetVersion(TES3MP_VERSION);
|
queryData.SetVersion(TES3MP_VERSION);
|
||||||
|
|
||||||
auto *players = Players::getPlayers();
|
//auto *players = Players::getPlayers();
|
||||||
while (sRun)
|
while (sRun)
|
||||||
{
|
{
|
||||||
SetPlayers((int) players->size());
|
SetPlayers((unsigned) Players::size());
|
||||||
|
|
||||||
auto pIt = players->begin();
|
auto pIt = Players::begin();
|
||||||
if (queryData.players.size() != players->size())
|
if (queryData.players.size() != Players::size())
|
||||||
{
|
{
|
||||||
queryData.players.clear();
|
queryData.players.clear();
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
else
|
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();
|
queryData.players.clear();
|
||||||
updated = true;
|
updated = true;
|
||||||
|
@ -222,13 +231,12 @@ void MasterClient::Thread()
|
||||||
if (updated)
|
if (updated)
|
||||||
{
|
{
|
||||||
updated = false;
|
updated = false;
|
||||||
if (pIt != players->end())
|
if (pIt != Players::end())
|
||||||
{
|
{
|
||||||
for (auto player : *players)
|
Players::for_each([this](auto player) {
|
||||||
{
|
if (!player->npc.mName.empty())
|
||||||
if (!player.second->npc.mName.empty())
|
queryData.players.push_back(player->npc.mName);
|
||||||
queryData.players.push_back(player.second->npc.mName);
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Send(PacketMasterAnnounce::FUNCTION_ANNOUNCE);
|
Send(PacketMasterAnnounce::FUNCTION_ANNOUNCE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <components/openmw-mp/Master/MasterData.hpp>
|
#include <components/openmw-mp/Master/MasterData.hpp>
|
||||||
#include <RakString.h>
|
#include <RakString.h>
|
||||||
#include <components/openmw-mp/Master/PacketMasterAnnounce.hpp>
|
#include <components/openmw-mp/Master/PacketMasterAnnounce.hpp>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
class MasterClient
|
class MasterClient
|
||||||
{
|
{
|
||||||
|
@ -46,7 +47,7 @@ private:
|
||||||
std::thread thrQuery;
|
std::thread thrQuery;
|
||||||
mwmp::PacketMasterAnnounce pma;
|
mwmp::PacketMasterAnnounce pma;
|
||||||
RakNet::BitStream writeStream;
|
RakNet::BitStream writeStream;
|
||||||
bool updated;
|
std::atomic<bool> updated;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
111
apps/openmw-mp/NetActor.cpp
Normal file
111
apps/openmw-mp/NetActor.cpp
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
//
|
||||||
|
// 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"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
NetActor::NetActor() : inventory(this), cellAPI(this)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetActor::resetUpdateFlags()
|
||||||
|
{
|
||||||
|
baseInfoChanged = 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;
|
||||||
|
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;
|
||||||
|
positionChanged = 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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
statsChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Inventory &NetActor::getInventory()
|
||||||
|
{
|
||||||
|
return inventory;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cells &NetActor::getCell()
|
||||||
|
{
|
||||||
|
return cellAPI;
|
||||||
|
}
|
75
apps/openmw-mp/NetActor.hpp
Normal file
75
apps/openmw-mp/NetActor.hpp
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
//
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @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; }
|
||||||
|
protected:
|
||||||
|
bool baseInfoChanged, levelChanged, statsChanged, positionChanged, attributesChanged, skillsChanged;
|
||||||
|
mwmp::BasePlayer *basePlayer;
|
||||||
|
mwmp::BaseNetCreature *netCreature;
|
||||||
|
|
||||||
|
Inventory inventory;
|
||||||
|
Cells cellAPI;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,12 @@
|
||||||
// Created by koncord on 12.01.16.
|
// Created by koncord on 12.01.16.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "Player.hpp"
|
#include <chrono>
|
||||||
#include "processors/ProcessorInitializer.hpp"
|
#include <iostream>
|
||||||
#include <RakPeer.h>
|
#include <thread>
|
||||||
|
|
||||||
#include <Kbhit.h>
|
#include <Kbhit.h>
|
||||||
|
#include <RakPeer.h>
|
||||||
|
|
||||||
#include <components/misc/stringops.hpp>
|
#include <components/misc/stringops.hpp>
|
||||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||||
|
@ -13,24 +15,23 @@
|
||||||
#include <components/openmw-mp/Version.hpp>
|
#include <components/openmw-mp/Version.hpp>
|
||||||
#include <components/openmw-mp/Packets/PacketPreInit.hpp>
|
#include <components/openmw-mp/Packets/PacketPreInit.hpp>
|
||||||
|
|
||||||
#include <iostream>
|
#include <Script/EventController.hpp>
|
||||||
#include <Script/Script.hpp>
|
#include "processors/ProcessorInitializer.hpp"
|
||||||
#include <Script/API/TimerAPI.hpp>
|
|
||||||
#include <chrono>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#include "Networking.hpp"
|
|
||||||
#include "MasterClient.hpp"
|
|
||||||
#include "Cell.hpp"
|
|
||||||
#include "CellController.hpp"
|
|
||||||
#include "processors/PlayerProcessor.hpp"
|
#include "processors/PlayerProcessor.hpp"
|
||||||
#include "processors/ActorProcessor.hpp"
|
#include "processors/ActorProcessor.hpp"
|
||||||
#include "processors/WorldProcessor.hpp"
|
#include "processors/WorldProcessor.hpp"
|
||||||
|
|
||||||
|
#include "Networking.hpp"
|
||||||
|
#include "MasterClient.hpp"
|
||||||
|
|
||||||
|
#include "Cell.hpp"
|
||||||
|
#include "CellController.hpp"
|
||||||
|
#include "Players.hpp"
|
||||||
|
|
||||||
using namespace mwmp;
|
using namespace mwmp;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
Networking *Networking::sThis = 0;
|
Networking *Networking::sThis = nullptr;
|
||||||
|
|
||||||
static int currentMpNum = 0;
|
static int currentMpNum = 0;
|
||||||
|
|
||||||
|
@ -38,7 +39,6 @@ Networking::Networking(RakNet::RakPeerInterface *peer) : mclient(nullptr)
|
||||||
{
|
{
|
||||||
sThis = this;
|
sThis = this;
|
||||||
this->peer = peer;
|
this->peer = peer;
|
||||||
players = Players::getPlayers();
|
|
||||||
|
|
||||||
CellController::create();
|
CellController::create();
|
||||||
|
|
||||||
|
@ -47,15 +47,13 @@ Networking::Networking(RakNet::RakPeerInterface *peer) : mclient(nullptr)
|
||||||
worldPacketController = new WorldPacketController(peer);
|
worldPacketController = new WorldPacketController(peer);
|
||||||
|
|
||||||
// Set send stream
|
// Set send stream
|
||||||
playerPacketController->SetStream(0, &bsOut);
|
playerPacketController->SetStream(nullptr, &bsOut);
|
||||||
actorPacketController->SetStream(0, &bsOut);
|
actorPacketController->SetStream(nullptr, &bsOut);
|
||||||
worldPacketController->SetStream(0, &bsOut);
|
worldPacketController->SetStream(nullptr, &bsOut);
|
||||||
|
|
||||||
running = true;
|
running = true;
|
||||||
exitCode = 0;
|
exitCode = 0;
|
||||||
|
|
||||||
Script::Call<Script::CallbackIdentity("OnServerInit")>();
|
|
||||||
|
|
||||||
serverPassword = TES3MP_DEFAULT_PASSW;
|
serverPassword = TES3MP_DEFAULT_PASSW;
|
||||||
|
|
||||||
ProcessorInitializer();
|
ProcessorInitializer();
|
||||||
|
@ -63,11 +61,11 @@ Networking::Networking(RakNet::RakPeerInterface *peer) : mclient(nullptr)
|
||||||
|
|
||||||
Networking::~Networking()
|
Networking::~Networking()
|
||||||
{
|
{
|
||||||
Script::Call<Script::CallbackIdentity("OnServerExit")>(false);
|
luaState.getEventCtrl().Call<CoreEvent::ON_EXIT>(false);
|
||||||
|
|
||||||
CellController::destroy();
|
CellController::destroy();
|
||||||
|
|
||||||
sThis = 0;
|
sThis = nullptr;
|
||||||
delete playerPacketController;
|
delete playerPacketController;
|
||||||
delete actorPacketController;
|
delete actorPacketController;
|
||||||
delete worldPacketController;
|
delete worldPacketController;
|
||||||
|
@ -85,13 +83,13 @@ bool Networking::isPassworded() const
|
||||||
|
|
||||||
void Networking::processPlayerPacket(RakNet::Packet *packet)
|
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]);
|
PlayerPacket *myPacket = playerPacketController->GetPacket(packet->data[0]);
|
||||||
|
|
||||||
if (packet->data[0] == ID_HANDSHAKE)
|
if (packet->data[0] == ID_HANDSHAKE)
|
||||||
{
|
{
|
||||||
myPacket->setPlayer(player);
|
myPacket->setPlayer(player.get());
|
||||||
myPacket->Read();
|
myPacket->Read();
|
||||||
|
|
||||||
if (player->isHandshaked())
|
if (player->isHandshaked())
|
||||||
|
@ -116,7 +114,8 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
|
||||||
if (!player->isHandshaked())
|
if (!player->isHandshaked())
|
||||||
{
|
{
|
||||||
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Have not completed handshake with player %d", player->getId());
|
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Have not completed handshake with player %d", player->getId());
|
||||||
//KickPlayer(player->guid);
|
if (player->handshakeAttempts() > 5)
|
||||||
|
kickPlayer(player->guid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,15 +123,14 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
|
||||||
{
|
{
|
||||||
player->setLoadState(Player::LOADED);
|
player->setLoadState(Player::LOADED);
|
||||||
|
|
||||||
static constexpr unsigned int ident = Script::CallbackIdentity("OnPlayerConnect");
|
bool result = luaState.getEventCtrl().Call<CoreEvent::ON_PLAYER_CONNECT, bool>(player);
|
||||||
Script::CallBackReturn<ident> result = true;
|
|
||||||
Script::Call<ident>(result, Players::getPlayer(packet->guid)->getId());
|
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
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);
|
playerPacketController->GetPacket(ID_USER_DISCONNECTED)->Send(false);
|
||||||
Players::deletePlayer(packet->guid);
|
Players::deletePlayerByGUID(packet->guid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,7 +138,7 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
|
||||||
{
|
{
|
||||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Received ID_PLAYER_BASEINFO about %s", player->npc.mName.c_str());
|
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->Read();
|
||||||
myPacket->Send(true);
|
myPacket->Send(true);
|
||||||
}
|
}
|
||||||
|
@ -156,43 +154,43 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
|
||||||
|
|
||||||
|
|
||||||
if (!PlayerProcessor::Process(*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)
|
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)
|
if (!player->isHandshaked() || player->getLoadState() != Player::POSTLOADED)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!ActorProcessor::Process(*packet, baseActorList))
|
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::processWorldPacket(RakNet::Packet *packet)
|
void Networking::processWorldPacket(RakNet::Packet *packet)
|
||||||
{
|
{
|
||||||
Player *player = Players::getPlayer(packet->guid);
|
auto player = Players::getPlayerByGUID(packet->guid);
|
||||||
|
|
||||||
if (!player->isHandshaked() || player->getLoadState() != Player::POSTLOADED)
|
if (!player->isHandshaked() || player->getLoadState() != Player::POSTLOADED)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!WorldProcessor::Process(*packet, baseEvent))
|
if (!WorldProcessor::Process(*packet, baseEvent))
|
||||||
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled WorldPacket with identifier %i has arrived", packet->data[0]);
|
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled WorldPacket with identifier %i has arrived", (int) packet->data[0]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
RakNet::BitStream bsIn(&packet->data[1], packet->length, false);
|
||||||
|
|
||||||
bsIn.IgnoreBytes((unsigned int) RakNet::RakNetGUID::size()); // Ignore GUID from received packet
|
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)
|
if (packet->data[0] == ID_GAME_PREINIT)
|
||||||
{
|
{
|
||||||
|
@ -222,7 +220,6 @@ void Networking::update(RakNet::Packet *packet)
|
||||||
// the server
|
// the server
|
||||||
if (it == hashList.end())
|
if (it == hashList.end())
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
else // name is incorrect
|
else // name is incorrect
|
||||||
break;
|
break;
|
||||||
|
@ -244,33 +241,39 @@ void Networking::update(RakNet::Packet *packet)
|
||||||
packetPreInit.setChecksums(&tmp);
|
packetPreInit.setChecksums(&tmp);
|
||||||
packetPreInit.Send(packet->systemAddress);
|
packetPreInit.Send(packet->systemAddress);
|
||||||
}
|
}
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
playerPacketController->SetStream(&bsIn, 0);
|
playerPacketController->SetStream(&bsIn, nullptr);
|
||||||
|
|
||||||
playerPacketController->GetPacket(ID_HANDSHAKE)->RequestData(packet->guid);
|
playerPacketController->GetPacket(ID_HANDSHAKE)->RequestData(packet->guid);
|
||||||
Players::newPlayer(packet->guid);
|
player = Players::addPlayer(packet->guid);
|
||||||
player = Players::getPlayer(packet->guid);
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
else if (playerPacketController->ContainsPacket(packet->data[0]))
|
|
||||||
|
if (playerPacketController->ContainsPacket(packet->data[0]))
|
||||||
{
|
{
|
||||||
playerPacketController->SetStream(&bsIn, 0);
|
playerPacketController->SetStream(&bsIn, nullptr);
|
||||||
processPlayerPacket(packet);
|
processPlayerPacket(packet);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
else if (actorPacketController->ContainsPacket(packet->data[0]))
|
|
||||||
|
if (actorPacketController->ContainsPacket(packet->data[0]))
|
||||||
{
|
{
|
||||||
actorPacketController->SetStream(&bsIn, 0);
|
actorPacketController->SetStream(&bsIn, nullptr);
|
||||||
processActorPacket(packet);
|
processActorPacket(packet);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
else if (worldPacketController->ContainsPacket(packet->data[0]))
|
|
||||||
|
if (worldPacketController->ContainsPacket(packet->data[0]))
|
||||||
{
|
{
|
||||||
worldPacketController->SetStream(&bsIn, 0);
|
worldPacketController->SetStream(&bsIn, nullptr);
|
||||||
processWorldPacket(packet);
|
processWorldPacket(packet);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Networking::newPlayer(RakNet::RakNetGUID guid)
|
void Networking::newPlayer(RakNet::RakNetGUID guid)
|
||||||
|
@ -283,27 +286,24 @@ void Networking::newPlayer(RakNet::RakNetGUID guid)
|
||||||
|
|
||||||
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Sending info about other players to %lu", guid.g);
|
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 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
|
// If an invalid key makes it into the Players map, ignore it
|
||||||
else if (pl->first == RakNet::UNASSIGNED_RAKNET_GUID) continue;
|
else if (pl->guid == RakNet::UNASSIGNED_RAKNET_GUID) return;
|
||||||
|
|
||||||
// if player not fully connected
|
|
||||||
else if (pl->second == nullptr) continue;
|
|
||||||
|
|
||||||
// If we are iterating over a player who has inputted their name, proceed
|
// 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_BASEINFO)->setPlayer(pl.get());
|
||||||
playerPacketController->GetPacket(ID_PLAYER_STATS_DYNAMIC)->setPlayer(pl->second);
|
playerPacketController->GetPacket(ID_PLAYER_STATS_DYNAMIC)->setPlayer(pl.get());
|
||||||
playerPacketController->GetPacket(ID_PLAYER_ATTRIBUTE)->setPlayer(pl->second);
|
playerPacketController->GetPacket(ID_PLAYER_ATTRIBUTE)->setPlayer(pl.get());
|
||||||
playerPacketController->GetPacket(ID_PLAYER_SKILL)->setPlayer(pl->second);
|
playerPacketController->GetPacket(ID_PLAYER_SKILL)->setPlayer(pl.get());
|
||||||
playerPacketController->GetPacket(ID_PLAYER_POSITION)->setPlayer(pl->second);
|
playerPacketController->GetPacket(ID_PLAYER_POSITION)->setPlayer(pl.get());
|
||||||
playerPacketController->GetPacket(ID_PLAYER_CELL_CHANGE)->setPlayer(pl->second);
|
playerPacketController->GetPacket(ID_PLAYER_CELL_CHANGE)->setPlayer(pl.get());
|
||||||
playerPacketController->GetPacket(ID_PLAYER_EQUIPMENT)->setPlayer(pl->second);
|
playerPacketController->GetPacket(ID_PLAYER_EQUIPMENT)->setPlayer(pl.get());
|
||||||
|
|
||||||
playerPacketController->GetPacket(ID_PLAYER_BASEINFO)->Send(guid);
|
playerPacketController->GetPacket(ID_PLAYER_BASEINFO)->Send(guid);
|
||||||
playerPacketController->GetPacket(ID_PLAYER_STATS_DYNAMIC)->Send(guid);
|
playerPacketController->GetPacket(ID_PLAYER_STATS_DYNAMIC)->Send(guid);
|
||||||
|
@ -313,7 +313,7 @@ void Networking::newPlayer(RakNet::RakNetGUID guid)
|
||||||
playerPacketController->GetPacket(ID_PLAYER_CELL_CHANGE)->Send(guid);
|
playerPacketController->GetPacket(ID_PLAYER_CELL_CHANGE)->Send(guid);
|
||||||
playerPacketController->GetPacket(ID_PLAYER_EQUIPMENT)->Send(guid);
|
playerPacketController->GetPacket(ID_PLAYER_EQUIPMENT)->Send(guid);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
LOG_APPEND(Log::LOG_WARN, "- Done");
|
LOG_APPEND(Log::LOG_WARN, "- Done");
|
||||||
|
|
||||||
|
@ -321,14 +321,15 @@ void Networking::newPlayer(RakNet::RakNetGUID guid)
|
||||||
|
|
||||||
void Networking::disconnectPlayer(RakNet::RakNetGUID guid)
|
void Networking::disconnectPlayer(RakNet::RakNetGUID guid)
|
||||||
{
|
{
|
||||||
Player *player = Players::getPlayer(guid);
|
auto player = Players::getPlayerByGUID(guid);
|
||||||
if (!player)
|
if (player == nullptr)
|
||||||
return;
|
return;
|
||||||
Script::Call<Script::CallbackIdentity("OnPlayerDisconnect")>(player->getId());
|
|
||||||
|
|
||||||
playerPacketController->GetPacket(ID_USER_DISCONNECTED)->setPlayer(player);
|
luaState.getEventCtrl().Call<CoreEvent::ON_PLAYER_DISCONNECT>(player);
|
||||||
|
|
||||||
|
playerPacketController->GetPacket(ID_USER_DISCONNECTED)->setPlayer(player.get());
|
||||||
playerPacketController->GetPacket(ID_USER_DISCONNECTED)->Send(true);
|
playerPacketController->GetPacket(ID_USER_DISCONNECTED)->Send(true);
|
||||||
Players::deletePlayer(guid);
|
Players::deletePlayerByGUID(guid);
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerPacketController *Networking::getPlayerPacketController() const
|
PlayerPacketController *Networking::getPlayerPacketController() const
|
||||||
|
@ -369,11 +370,11 @@ void Networking::setCurrentMpNum(int value)
|
||||||
int Networking::incrementMpNum()
|
int Networking::incrementMpNum()
|
||||||
{
|
{
|
||||||
currentMpNum++;
|
currentMpNum++;
|
||||||
Script::Call<Script::CallbackIdentity("OnMpNumIncrement")>(currentMpNum);
|
luaState.getEventCtrl().Call<CoreEvent::ON_MP_REFNUM>(currentMpNum);
|
||||||
return currentMpNum;
|
return currentMpNum;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Networking &Networking::get()
|
Networking &Networking::get()
|
||||||
{
|
{
|
||||||
return *sThis;
|
return *sThis;
|
||||||
}
|
}
|
||||||
|
@ -396,20 +397,18 @@ PacketPreInit::PluginContainer Networking::getPluginListSample()
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
unsigned field = 0;
|
unsigned field = 0;
|
||||||
auto name = "";
|
auto name = luaState.getEventCtrl().Call<CoreEvent::ON_REQUEST_PLUGIN_LIST, string>(id, field++);
|
||||||
Script::Call<Script::CallbackIdentity("OnRequestPluginList")>(name, id, field++);
|
if (name.empty())
|
||||||
if (strlen(name) == 0)
|
|
||||||
break;
|
break;
|
||||||
PacketPreInit::HashList hashList;
|
PacketPreInit::HashList hashList;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
auto hash = "";
|
auto hash = luaState.getEventCtrl().Call<CoreEvent::ON_REQUEST_PLUGIN_LIST, string>(id, field++);
|
||||||
Script::Call<Script::CallbackIdentity("OnRequestPluginList")>(hash, id, field++);
|
if (hash.empty())
|
||||||
if (strlen(hash) == 0)
|
|
||||||
break;
|
break;
|
||||||
hashList.push_back((unsigned)stoul(hash));
|
hashList.push_back((unsigned)stoul(hash));
|
||||||
}
|
}
|
||||||
pls.push_back({name, hashList});
|
pls.emplace_back(name, hashList);
|
||||||
id++;
|
id++;
|
||||||
}
|
}
|
||||||
return pls;
|
return pls;
|
||||||
|
@ -425,8 +424,11 @@ int Networking::mainLoop()
|
||||||
{
|
{
|
||||||
RakNet::Packet *packet;
|
RakNet::Packet *packet;
|
||||||
|
|
||||||
|
auto &timerCtrl = luaState.getTimerCtrl();
|
||||||
|
|
||||||
while (running)
|
while (running)
|
||||||
{
|
{
|
||||||
|
bool updated = false;
|
||||||
if (kbhit() && getch() == '\n')
|
if (kbhit() && getch() == '\n')
|
||||||
break;
|
break;
|
||||||
for (packet=peer->Receive(); packet; peer->DeallocatePacket(packet), packet=peer->Receive())
|
for (packet=peer->Receive(); packet; peer->DeallocatePacket(packet), packet=peer->Receive())
|
||||||
|
@ -469,15 +471,22 @@ int Networking::mainLoop()
|
||||||
case ID_UNCONNECTED_PING:
|
case ID_UNCONNECTED_PING:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
update(packet);
|
updated = update(packet);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TimerAPI::Tick();
|
timerCtrl.tick();
|
||||||
|
if (updated)
|
||||||
|
{
|
||||||
|
// fixme: not good to call for_each every frame. Maybe for_each_Updated will be better
|
||||||
|
Players::for_each([](shared_ptr<Player> pl) {
|
||||||
|
pl->update();
|
||||||
|
});
|
||||||
|
}
|
||||||
this_thread::sleep_for (chrono::milliseconds(1));
|
this_thread::sleep_for (chrono::milliseconds(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
TimerAPI::Terminate();
|
timerCtrl.terminate();
|
||||||
return exitCode;
|
return exitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -516,14 +525,14 @@ MasterClient *Networking::getMasterClient()
|
||||||
return mclient;
|
return mclient;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = new MasterClient(peer, queryAddr, queryPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Networking::postInit()
|
void Networking::postInit()
|
||||||
{
|
{
|
||||||
Script::Call<Script::CallbackIdentity("OnServerPostInit")>();
|
luaState.getEventCtrl().Call<CoreEvent::ON_POST_INIT>();
|
||||||
samples = getPluginListSample();
|
samples = getPluginListSample();
|
||||||
if (mclient)
|
if (mclient)
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include <components/openmw-mp/Controllers/ActorPacketController.hpp>
|
#include <components/openmw-mp/Controllers/ActorPacketController.hpp>
|
||||||
#include <components/openmw-mp/Controllers/WorldPacketController.hpp>
|
#include <components/openmw-mp/Controllers/WorldPacketController.hpp>
|
||||||
#include <components/openmw-mp/Packets/PacketPreInit.hpp>
|
#include <components/openmw-mp/Packets/PacketPreInit.hpp>
|
||||||
#include "Player.hpp"
|
#include <apps/openmw-mp/Script/LuaState.hpp>
|
||||||
|
|
||||||
class MasterClient;
|
class MasterClient;
|
||||||
namespace mwmp
|
namespace mwmp
|
||||||
|
@ -31,7 +31,7 @@ namespace mwmp
|
||||||
void processPlayerPacket(RakNet::Packet *packet);
|
void processPlayerPacket(RakNet::Packet *packet);
|
||||||
void processActorPacket(RakNet::Packet *packet);
|
void processActorPacket(RakNet::Packet *packet);
|
||||||
void processWorldPacket(RakNet::Packet *packet);
|
void processWorldPacket(RakNet::Packet *packet);
|
||||||
void update(RakNet::Packet *packet);
|
bool update(RakNet::Packet *packet);
|
||||||
|
|
||||||
unsigned short numberOfConnections() const;
|
unsigned short numberOfConnections() const;
|
||||||
unsigned int maxConnections() const;
|
unsigned int maxConnections() const;
|
||||||
|
@ -45,6 +45,8 @@ namespace mwmp
|
||||||
ActorPacketController *getActorPacketController() const;
|
ActorPacketController *getActorPacketController() const;
|
||||||
WorldPacketController *getWorldPacketController() const;
|
WorldPacketController *getWorldPacketController() const;
|
||||||
|
|
||||||
|
LuaState &getState() {return luaState;}
|
||||||
|
|
||||||
BaseActorList *getLastActorList();
|
BaseActorList *getLastActorList();
|
||||||
BaseEvent *getLastEvent();
|
BaseEvent *getLastEvent();
|
||||||
|
|
||||||
|
@ -53,22 +55,22 @@ namespace mwmp
|
||||||
int incrementMpNum();
|
int incrementMpNum();
|
||||||
|
|
||||||
MasterClient *getMasterClient();
|
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;
|
void setServerPassword(std::string passw) noexcept;
|
||||||
bool isPassworded() const;
|
bool isPassworded() const;
|
||||||
|
|
||||||
static const Networking &get();
|
static Networking &get();
|
||||||
static Networking *getPtr();
|
static Networking *getPtr();
|
||||||
|
|
||||||
void postInit();
|
void postInit();
|
||||||
private:
|
private:
|
||||||
|
LuaState luaState;
|
||||||
PacketPreInit::PluginContainer getPluginListSample();
|
PacketPreInit::PluginContainer getPluginListSample();
|
||||||
std::string serverPassword;
|
std::string serverPassword;
|
||||||
static Networking *sThis;
|
static Networking *sThis;
|
||||||
|
|
||||||
RakNet::RakPeerInterface *peer;
|
RakNet::RakPeerInterface *peer;
|
||||||
RakNet::BitStream bsOut;
|
RakNet::BitStream bsOut;
|
||||||
TPlayers *players;
|
|
||||||
MasterClient *mclient;
|
MasterClient *mclient;
|
||||||
|
|
||||||
BaseActorList baseActorList;
|
BaseActorList baseActorList;
|
||||||
|
|
470
apps/openmw-mp/Object.cpp
Normal file
470
apps/openmw-mp/Object.cpp
Normal file
|
@ -0,0 +1,470 @@
|
||||||
|
//
|
||||||
|
// 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),
|
||||||
|
"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),
|
||||||
|
"doorState", sol::property(&Object::getDoorState, &Object::setDoorState),
|
||||||
|
"lockLevel", sol::property(&Object::getLockLevel, &Object::setLockLevel),
|
||||||
|
"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;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int BaseObject::getRefNum() const
|
||||||
|
{
|
||||||
|
return object.refNumIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseObject::setRefNum(int refNum)
|
||||||
|
{
|
||||||
|
changedBase = true;
|
||||||
|
object.refNumIndex = refNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
int BaseObject::getMpNum() const
|
||||||
|
{
|
||||||
|
return object.mpNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseObject::setMpNum(int mpNum)
|
||||||
|
{
|
||||||
|
changedBase = true;
|
||||||
|
object.mpNum = mpNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Object::setDisarmState(bool state)
|
||||||
|
{
|
||||||
|
changedObjectTrap = true;
|
||||||
|
object.isDisarmed = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Object::setMasterState(bool state)
|
||||||
|
{
|
||||||
|
changedObjectSpawn = true;
|
||||||
|
object.hasMaster = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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> Container::getItem(int i) const
|
||||||
|
{
|
||||||
|
auto &item = object.containerItems.at(i);
|
||||||
|
return make_tuple(item.refId, item.count, item.charge);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Container::setItem(int i, const string &refId, int count, int charge)
|
||||||
|
{
|
||||||
|
auto &item = object.containerItems.at(i);
|
||||||
|
item.refId = refId;
|
||||||
|
item.count = count;
|
||||||
|
item.charge = charge;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Container::addItem(const string &refId, int count, int charge)
|
||||||
|
{
|
||||||
|
mwmp::ContainerItem item;
|
||||||
|
item.refId = refId;
|
||||||
|
item.count = count;
|
||||||
|
item.charge = charge;
|
||||||
|
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("requestContainers", [&lua](shared_ptr<Player> player) {
|
||||||
|
lua.getObjectCtrl().requestContainers(player);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
shared_ptr<vector<shared_ptr<Object>>> ObjectController::copyObjects(mwmp::BaseEvent &event)
|
||||||
|
{
|
||||||
|
auto objects = make_shared<vector<shared_ptr<Object>>>();
|
||||||
|
|
||||||
|
for (auto &obj : event.worldObjects)
|
||||||
|
{
|
||||||
|
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::BaseEvent &event)
|
||||||
|
{
|
||||||
|
auto containers = make_shared<vector<shared_ptr<Container>>>();
|
||||||
|
|
||||||
|
for (auto &obj : event.worldObjects)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
enum Type
|
||||||
|
{
|
||||||
|
DOOR_STATE = 0,
|
||||||
|
OBJECT_STATE,
|
||||||
|
OBJECT_SCALE,
|
||||||
|
OBJECT_TRAP,
|
||||||
|
OBJECT_LOCK,
|
||||||
|
OBJECT_DELETE,
|
||||||
|
OBJECT_SPAWN,
|
||||||
|
OBJECT_PLACE,
|
||||||
|
LAST
|
||||||
|
};
|
||||||
|
mwmp::BaseEvent events[Type::LAST];
|
||||||
|
bool changed[Type::LAST];
|
||||||
|
|
||||||
|
for (auto &e : events)
|
||||||
|
{
|
||||||
|
e.action = mwmp::BaseEvent::SET;
|
||||||
|
e.guid = player->guid;
|
||||||
|
e.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;
|
||||||
|
events[Type::DOOR_STATE].worldObjects.push_back(object->object);
|
||||||
|
}
|
||||||
|
if (object->changedObjectState && validNewObjOrCopy)
|
||||||
|
{
|
||||||
|
changed[Type::OBJECT_STATE] = true;
|
||||||
|
events[Type::OBJECT_STATE].worldObjects.push_back(object->object);
|
||||||
|
}
|
||||||
|
if (object->changedObjectScale && validNewObjOrCopy)
|
||||||
|
{
|
||||||
|
changed[Type::OBJECT_SCALE] = true;
|
||||||
|
events[Type::OBJECT_SCALE].worldObjects.push_back(object->object);
|
||||||
|
}
|
||||||
|
if (object->changedObjectTrap && validNewObjOrCopy)
|
||||||
|
{
|
||||||
|
changed[Type::OBJECT_TRAP] = true;
|
||||||
|
events[Type::OBJECT_TRAP].worldObjects.push_back(object->object);
|
||||||
|
}
|
||||||
|
if (object->changedObjectLock && validNewObjOrCopy)
|
||||||
|
{
|
||||||
|
changed[Type::OBJECT_LOCK] = true;
|
||||||
|
events[Type::OBJECT_LOCK].worldObjects.push_back(object->object);
|
||||||
|
}
|
||||||
|
if (object->changedObjectDelete && validNewObjOrCopy)
|
||||||
|
{
|
||||||
|
changed[Type::OBJECT_DELETE] = true;
|
||||||
|
events[Type::OBJECT_DELETE].worldObjects.push_back(object->object);
|
||||||
|
}
|
||||||
|
if (object->changedObjectSpawn && validNewObjOrCopy)
|
||||||
|
{
|
||||||
|
changed[Type::OBJECT_SPAWN] = true;
|
||||||
|
events[Type::OBJECT_SPAWN].worldObjects.push_back(object->object);
|
||||||
|
}
|
||||||
|
if (object->changedObjectPlace && validNewObjOrCopy)
|
||||||
|
{
|
||||||
|
changed[Type::OBJECT_PLACE] = true;
|
||||||
|
events[Type::OBJECT_PLACE].worldObjects.push_back(object->object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto worldCtrl = mwmp::Networking::get().getWorldPacketController();
|
||||||
|
|
||||||
|
if (changed[Type::DOOR_STATE])
|
||||||
|
{
|
||||||
|
auto packet = worldCtrl->GetPacket(ID_DOOR_STATE);
|
||||||
|
auto &event = events[Type::DOOR_STATE];
|
||||||
|
packet->setEvent(&event);
|
||||||
|
packet->Send(event.guid);
|
||||||
|
}
|
||||||
|
if (changed[Type::OBJECT_STATE])
|
||||||
|
{
|
||||||
|
auto packet = worldCtrl->GetPacket(ID_OBJECT_STATE);
|
||||||
|
auto &event = events[Type::OBJECT_STATE];
|
||||||
|
packet->setEvent(&event);
|
||||||
|
packet->Send(event.guid);
|
||||||
|
}
|
||||||
|
if (changed[Type::OBJECT_SCALE])
|
||||||
|
{
|
||||||
|
auto packet = worldCtrl->GetPacket(ID_OBJECT_SCALE);
|
||||||
|
auto &event = events[Type::OBJECT_SCALE];
|
||||||
|
packet->setEvent(&event);
|
||||||
|
packet->Send(event.guid);
|
||||||
|
}
|
||||||
|
if (changed[Type::OBJECT_TRAP])
|
||||||
|
{
|
||||||
|
auto packet = worldCtrl->GetPacket(ID_OBJECT_TRAP);
|
||||||
|
auto &event = events[Type::OBJECT_TRAP];
|
||||||
|
packet->setEvent(&event);
|
||||||
|
packet->Send(event.guid);
|
||||||
|
}
|
||||||
|
if (changed[Type::OBJECT_LOCK])
|
||||||
|
{
|
||||||
|
auto packet = worldCtrl->GetPacket(ID_OBJECT_LOCK);
|
||||||
|
auto &event = events[Type::OBJECT_LOCK];
|
||||||
|
packet->setEvent(&event);
|
||||||
|
packet->Send(event.guid);
|
||||||
|
}
|
||||||
|
if (changed[Type::OBJECT_DELETE])
|
||||||
|
{
|
||||||
|
auto packet = worldCtrl->GetPacket(ID_OBJECT_DELETE);
|
||||||
|
auto &event = events[Type::OBJECT_DELETE];
|
||||||
|
packet->setEvent(&event);
|
||||||
|
packet->Send(event.guid);
|
||||||
|
}
|
||||||
|
if (changed[Type::OBJECT_SCALE])
|
||||||
|
{
|
||||||
|
auto packet = worldCtrl->GetPacket(ID_OBJECT_SPAWN);
|
||||||
|
auto &event = events[Type::OBJECT_SCALE];
|
||||||
|
packet->setEvent(&event);
|
||||||
|
packet->Send(event.guid);
|
||||||
|
}
|
||||||
|
if (changed[Type::OBJECT_PLACE])
|
||||||
|
{
|
||||||
|
auto packet = worldCtrl->GetPacket(ID_OBJECT_PLACE);
|
||||||
|
auto &event = events[Type::OBJECT_PLACE];
|
||||||
|
packet->setEvent(&event);
|
||||||
|
packet->Send(event.guid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectController::sendContainers(shared_ptr<Player> player, shared_ptr<vector<shared_ptr<Container>>> objects, const ESM::Cell &cell)
|
||||||
|
{
|
||||||
|
|
||||||
|
mwmp::BaseEvent event;
|
||||||
|
event.cell = cell;
|
||||||
|
event.action = mwmp::BaseEvent::SET;
|
||||||
|
event.guid = player->guid;
|
||||||
|
|
||||||
|
for (auto &object : *objects)
|
||||||
|
{
|
||||||
|
bool validNewObjOrCopy = (!object->copied && object->changedBase) || object->copied;
|
||||||
|
if (object->changed && validNewObjOrCopy)
|
||||||
|
event.worldObjects.push_back(object->object);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto packet = mwmp::Networking::get().getWorldPacketController()->GetPacket(ID_CONTAINER);
|
||||||
|
packet->setEvent(&event);
|
||||||
|
packet->Send(event.guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectController::requestContainers(shared_ptr<Player> player)
|
||||||
|
{
|
||||||
|
mwmp::BaseEvent event;
|
||||||
|
event.action = mwmp::BaseEvent::REQUEST;
|
||||||
|
event.guid = player->guid;
|
||||||
|
event.cell = player->cell;
|
||||||
|
|
||||||
|
auto packet = mwmp::Networking::get().getWorldPacketController()->GetPacket(ID_CONTAINER);
|
||||||
|
packet->setEvent(&event);
|
||||||
|
packet->Send(event.guid);
|
||||||
|
}
|
119
apps/openmw-mp/Object.hpp
Normal file
119
apps/openmw-mp/Object.hpp
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
//
|
||||||
|
// Created by koncord on 26.08.17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
|
#include <components/openmw-mp/Base/BaseEvent.hpp>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class LuaState;
|
||||||
|
class Player;
|
||||||
|
|
||||||
|
class BaseObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BaseObject();
|
||||||
|
std::string getRefId() const;
|
||||||
|
void setRefId(const std::string &refId);
|
||||||
|
|
||||||
|
int getRefNum() const;
|
||||||
|
void setRefNum(int refNum);
|
||||||
|
|
||||||
|
int getMpNum() const;
|
||||||
|
void setMpNum(int mpNum);
|
||||||
|
|
||||||
|
//void setEventCell(const std::string &cellDescription);
|
||||||
|
|
||||||
|
|
||||||
|
mwmp::WorldObject 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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
void setDisarmState(bool state);
|
||||||
|
void setMasterState(bool state);
|
||||||
|
|
||||||
|
bool changedDoorState, 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> getItem(int i) const;
|
||||||
|
void addItem(const std::string &refId, int count, int charge);
|
||||||
|
|
||||||
|
void setItem(int i, const std::string &refId, int count, int charge);
|
||||||
|
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::BaseEvent &event);
|
||||||
|
std::shared_ptr<std::vector<std::shared_ptr<Container>>> copyContainers(mwmp::BaseEvent &event);
|
||||||
|
|
||||||
|
void sendObjects(std::shared_ptr<Player> player, std::shared_ptr<std::vector<std::shared_ptr<Object>>> objects, const ESM::Cell &cell);
|
||||||
|
void sendContainers(std::shared_ptr<Player> player, std::shared_ptr<std::vector<std::shared_ptr<Container>>> objects, const ESM::Cell &cell);
|
||||||
|
|
||||||
|
void requestContainers(std::shared_ptr<Player> player);
|
||||||
|
};
|
|
@ -2,79 +2,218 @@
|
||||||
// Created by koncord on 05.01.16.
|
// Created by koncord on 05.01.16.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "Player.hpp"
|
#include <components/openmw-mp/NetworkMessages.hpp>
|
||||||
|
|
||||||
#include "Networking.hpp"
|
#include "Networking.hpp"
|
||||||
|
|
||||||
TPlayers Players::players;
|
#include "Player.hpp"
|
||||||
TSlots Players::slots;
|
#include "Inventory.hpp"
|
||||||
|
#include "Settings.hpp"
|
||||||
|
|
||||||
void Players::deletePlayer(RakNet::RakNetGUID guid)
|
using namespace std;
|
||||||
|
|
||||||
|
void Player::Init(LuaState &lua)
|
||||||
{
|
{
|
||||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Deleting player with guid %lu", guid.g);
|
lua.getState()->new_usertype<Player>("Player",
|
||||||
|
"getPosition", &NetActor::getPosition,
|
||||||
|
"setPosition", &NetActor::setPosition,
|
||||||
|
"getRotation", &NetActor::getRotation,
|
||||||
|
"setRotation", &NetActor::setRotation,
|
||||||
|
|
||||||
if (players[guid] != 0)
|
"getHealth", &NetActor::getHealth,
|
||||||
{
|
"setHealth", &NetActor::setHealth,
|
||||||
CellController::get()->deletePlayer(players[guid]);
|
"getMagicka", &NetActor::getMagicka,
|
||||||
|
"setMagicka", &NetActor::setMagicka,
|
||||||
|
"getFatigue", &NetActor::getFatigue,
|
||||||
|
"setFatigue", &NetActor::setFatigue,
|
||||||
|
|
||||||
LOG_APPEND(Log::LOG_INFO, "- Emptying slot %i", players[guid]->getId());
|
"getCell", &NetActor::getCell,
|
||||||
|
"getInventory", &NetActor::getInventory,
|
||||||
|
|
||||||
slots[players[guid]->getId()] = 0;
|
"getPreviousCellPos", &Player::getPreviousCellPos,
|
||||||
delete players[guid];
|
|
||||||
players.erase(guid);
|
"kick", &Player::kick,
|
||||||
}
|
"ban", &Player::ban,
|
||||||
|
"address", sol::property(&Player::getIP),
|
||||||
|
|
||||||
|
"getAvgPing", &Player::getAvgPing,
|
||||||
|
|
||||||
|
"message", &Player::message,
|
||||||
|
"cleanChat", &Player::cleanChat,
|
||||||
|
|
||||||
|
"pid", sol::readonly_property(&Player::id),
|
||||||
|
"guid", sol::readonly_property(&Player::getGUID),
|
||||||
|
|
||||||
|
"name", sol::property(&Player::getName, &Player::setName),
|
||||||
|
"setCharGenStages", &Player::setCharGenStages,
|
||||||
|
"level", sol::property(&Player::getLevel, &Player::setLevel),
|
||||||
|
"gender", sol::property(&Player::getGender, &Player::setGender),
|
||||||
|
"race", sol::property(&Player::getRace, &Player::setRace),
|
||||||
|
"head", sol::property(&Player::getHead, &Player::setHead),
|
||||||
|
"hair", sol::property(&Player::getHair, &Player::setHair),
|
||||||
|
"birthsign", sol::property(&Player::getBirthsign, &Player::setBirthsign),
|
||||||
|
"bounty", sol::property(&Player::getBounty, &Player::setBounty),
|
||||||
|
"levelProgress", sol::property(&Player::getLevelProgress, &Player::setLevelProgress),
|
||||||
|
"creatureModel", sol::property(&Player::getCreatureModel, &Player::setCreatureModel),
|
||||||
|
"isCreatureName", sol::property(&Player::isCreatureName, &Player::creatureName),
|
||||||
|
|
||||||
|
"resurrect", &Player::resurrect,
|
||||||
|
"jail", &Player::jail,
|
||||||
|
"werewolf", sol::property(&Player::getWerewolfState, &Player::setWerewolfState),
|
||||||
|
|
||||||
|
"getAttribute", &Player::getAttribute,
|
||||||
|
"setAttribute", &Player::setAttribute,
|
||||||
|
|
||||||
|
"getSkill", &Player::getSkill,
|
||||||
|
"setSkill", &Player::setSkill,
|
||||||
|
|
||||||
|
"getSkillIncrease", &Player::getSkillIncrease,
|
||||||
|
"setSkillIncrease", &Player::setSkillIncrease,
|
||||||
|
|
||||||
|
"getClass", &Player::getCharClass,
|
||||||
|
"getSettings", &Player::getSettings,
|
||||||
|
"getBooks", &Player::getBooks,
|
||||||
|
"getGUI", &Player::getGUI,
|
||||||
|
"getDialogue", &Player::getDialogue,
|
||||||
|
"getFactions", &Player::getFactions,
|
||||||
|
"getQuests", &Player::getQuests,
|
||||||
|
"getSpells", &Player::getSpells,
|
||||||
|
|
||||||
|
"getCellState", &Player::getCellState,
|
||||||
|
"cellStateSize", &Player::cellStateSize,
|
||||||
|
"addCellExplored", &Player::addCellExplored,
|
||||||
|
"setAuthority", &Player::setAuthority,
|
||||||
|
"customData", &Player::customData
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Players::newPlayer(RakNet::RakNetGUID guid)
|
Player::Player(RakNet::RakNetGUID guid) : BasePlayer(guid), NetActor(), changedMap(false), cClass(this),
|
||||||
|
settings(this), books(this), gui(this), dialogue(this), factions(this),
|
||||||
|
quests(this), spells(this)
|
||||||
{
|
{
|
||||||
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Creating new player with guid %lu", guid.g);
|
basePlayer = this;
|
||||||
|
netCreature = this;
|
||||||
players[guid] = new Player(guid);
|
printf("Player::Player()\n");
|
||||||
players[guid]->cell.blank();
|
handshakeCounter = 0;
|
||||||
players[guid]->npc.blank();
|
|
||||||
players[guid]->npcStats.blank();
|
|
||||||
players[guid]->creatureStats.blank();
|
|
||||||
players[guid]->charClass.blank();
|
|
||||||
players[guid]->isWerewolf = false;
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < mwmp::Networking::get().maxConnections(); i++)
|
|
||||||
{
|
|
||||||
if (slots[i] == 0)
|
|
||||||
{
|
|
||||||
LOG_APPEND(Log::LOG_INFO, "- Storing in slot %i", i);
|
|
||||||
|
|
||||||
slots[i] = players[guid];
|
|
||||||
slots[i]->setId(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Player *Players::getPlayer(RakNet::RakNetGUID guid)
|
|
||||||
{
|
|
||||||
if (players.count(guid) == 0)
|
|
||||||
return nullptr;
|
|
||||||
return players[guid];
|
|
||||||
}
|
|
||||||
|
|
||||||
TPlayers *Players::getPlayers()
|
|
||||||
{
|
|
||||||
return &players;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned short Players::getLastPlayerId()
|
|
||||||
{
|
|
||||||
return slots.rbegin()->first;
|
|
||||||
}
|
|
||||||
|
|
||||||
Player::Player(RakNet::RakNetGUID guid) : BasePlayer(guid)
|
|
||||||
{
|
|
||||||
handshakeState = false;
|
|
||||||
loadState = NOTLOADED;
|
loadState = NOTLOADED;
|
||||||
|
resetUpdateFlags();
|
||||||
|
cell.blank();
|
||||||
|
npc.blank();
|
||||||
|
npcStats.blank();
|
||||||
|
creatureStats.blank();
|
||||||
|
charClass.blank();
|
||||||
|
customData = mwmp::Networking::get().getState().getState()->create_table();
|
||||||
}
|
}
|
||||||
|
|
||||||
Player::~Player()
|
Player::~Player()
|
||||||
{
|
{
|
||||||
|
printf("Player::~Player()\n");
|
||||||
|
CellController::get()->deletePlayer(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::update()
|
||||||
|
{
|
||||||
|
auto plPCtrl = mwmp::Networking::get().getPlayerPacketController();
|
||||||
|
|
||||||
|
if (baseInfoChanged)
|
||||||
|
{
|
||||||
|
auto packet = plPCtrl->GetPacket(ID_PLAYER_BASEINFO);
|
||||||
|
packet->setPlayer(basePlayer);
|
||||||
|
packet->Send(false);
|
||||||
|
packet->Send(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we send a cell change before we send the position so the position isn't overridden
|
||||||
|
if (cellAPI.isChangedCell())
|
||||||
|
{
|
||||||
|
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_CELL_CHANGE);
|
||||||
|
packet->setPlayer(this);
|
||||||
|
packet->Send(/*toOthers*/ false);
|
||||||
|
cellAPI.resetChangedCell();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (positionChanged)
|
||||||
|
{
|
||||||
|
auto packet = plPCtrl->GetPacket(ID_PLAYER_POSITION);
|
||||||
|
packet->setPlayer(basePlayer);
|
||||||
|
packet->Send(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The character class can override values from below on the client, so send it first
|
||||||
|
cClass.update();
|
||||||
|
|
||||||
|
if (levelChanged)
|
||||||
|
{
|
||||||
|
auto packet = plPCtrl->GetPacket(ID_PLAYER_LEVEL);
|
||||||
|
packet->setPlayer(basePlayer);
|
||||||
|
packet->Send(false);
|
||||||
|
packet->Send(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statsChanged)
|
||||||
|
{
|
||||||
|
auto packet = plPCtrl->GetPacket(ID_PLAYER_STATS_DYNAMIC);
|
||||||
|
packet->setPlayer(basePlayer);
|
||||||
|
packet->Send(false);
|
||||||
|
packet->Send(true);
|
||||||
|
|
||||||
|
statsDynamicIndexChanges.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attributesChanged)
|
||||||
|
{
|
||||||
|
auto packet = plPCtrl->GetPacket(ID_PLAYER_ATTRIBUTE);
|
||||||
|
packet->setPlayer(basePlayer);
|
||||||
|
packet->Send(false);
|
||||||
|
packet->Send(true);
|
||||||
|
|
||||||
|
attributeIndexChanges.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skillsChanged)
|
||||||
|
{
|
||||||
|
auto packet = plPCtrl->GetPacket(ID_PLAYER_SKILL);
|
||||||
|
packet->setPlayer(basePlayer);
|
||||||
|
packet->Send(false);
|
||||||
|
packet->Send(true);
|
||||||
|
|
||||||
|
skillIndexChanges.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inventory.isEquipmentChanged())
|
||||||
|
{
|
||||||
|
auto packet = plPCtrl->GetPacket(ID_PLAYER_EQUIPMENT);
|
||||||
|
packet->setPlayer(this);
|
||||||
|
packet->Send(false);
|
||||||
|
packet->Send(true);
|
||||||
|
inventory.resetEquipmentFlag();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inventory.inventoryChangeType() != 0)
|
||||||
|
{
|
||||||
|
auto packet = plPCtrl->GetPacket(ID_PLAYER_INVENTORY);
|
||||||
|
packet->setPlayer(this);
|
||||||
|
packet->Send(/*toOthers*/ false);
|
||||||
|
inventory.resetInventoryFlag();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changedMap)
|
||||||
|
{
|
||||||
|
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_MAP);
|
||||||
|
packet->setPlayer(this);
|
||||||
|
packet->Send(/*toOthers*/ false);
|
||||||
|
changedMap = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.update();
|
||||||
|
books.update();
|
||||||
|
gui.update();
|
||||||
|
dialogue.update();
|
||||||
|
factions.update();
|
||||||
|
quests.update();
|
||||||
|
spells.update();
|
||||||
|
|
||||||
|
resetUpdateFlags();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned short Player::getId()
|
unsigned short Player::getId()
|
||||||
|
@ -82,24 +221,24 @@ unsigned short Player::getId()
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::setId(unsigned short id)
|
void Player::setId(unsigned short newId)
|
||||||
{
|
{
|
||||||
this->id = id;
|
this->id = newId;
|
||||||
}
|
|
||||||
|
|
||||||
void Player::setHandshake()
|
|
||||||
{
|
|
||||||
handshakeState = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Player::isHandshaked()
|
bool Player::isHandshaked()
|
||||||
{
|
{
|
||||||
return handshakeState;
|
return handshakeCounter == numeric_limits<int>::max();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::setLoadState(int state)
|
void Player::setHandshake()
|
||||||
{
|
{
|
||||||
loadState = state;
|
handshakeCounter = numeric_limits<int>::max();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Player::handshakeAttempts()
|
||||||
|
{
|
||||||
|
return handshakeCounter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Player::getLoadState()
|
int Player::getLoadState()
|
||||||
|
@ -107,11 +246,9 @@ int Player::getLoadState()
|
||||||
return loadState;
|
return loadState;
|
||||||
}
|
}
|
||||||
|
|
||||||
Player *Players::getPlayer(unsigned short id)
|
void Player::setLoadState(int state)
|
||||||
{
|
{
|
||||||
if (slots.find(id) == slots.end())
|
loadState = state;
|
||||||
return nullptr;
|
|
||||||
return slots[id];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CellController::TContainer *Player::getCells()
|
CellController::TContainer *Player::getCells()
|
||||||
|
@ -119,6 +256,29 @@ CellController::TContainer *Player::getCells()
|
||||||
return &cells;
|
return &cells;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Player::forEachLoaded(std::function<void(Player *pl, Player *other)> func)
|
||||||
|
{
|
||||||
|
std::list <Player*> plList;
|
||||||
|
|
||||||
|
for (auto &&cell : cells)
|
||||||
|
{
|
||||||
|
for (auto &&pl : *cell)
|
||||||
|
{
|
||||||
|
if (pl != nullptr && !pl->npc.mName.empty())
|
||||||
|
plList.push_back(pl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plList.sort();
|
||||||
|
plList.unique();
|
||||||
|
|
||||||
|
for (auto &&pl : plList)
|
||||||
|
{
|
||||||
|
if (pl == this) continue;
|
||||||
|
func(this, pl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Player::sendToLoaded(mwmp::PlayerPacket *myPacket)
|
void Player::sendToLoaded(mwmp::PlayerPacket *myPacket)
|
||||||
{
|
{
|
||||||
std::list <Player*> plList;
|
std::list <Player*> plList;
|
||||||
|
@ -138,25 +298,370 @@ void Player::sendToLoaded(mwmp::PlayerPacket *myPacket)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::forEachLoaded(std::function<void(Player *pl, Player *other)> func)
|
void Player::kick() const
|
||||||
{
|
{
|
||||||
std::list <Player*> plList;
|
mwmp::Networking::getPtr()->kickPlayer(guid);
|
||||||
|
|
||||||
for (auto cell : cells)
|
|
||||||
{
|
|
||||||
for (auto pl : *cell)
|
|
||||||
{
|
|
||||||
if (pl != nullptr && !pl->npc.mName.empty())
|
|
||||||
plList.push_back(pl);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
plList.sort();
|
void Player::ban() const
|
||||||
plList.unique();
|
|
||||||
|
|
||||||
for (auto pl : plList)
|
|
||||||
{
|
{
|
||||||
if (pl == this) continue;
|
auto netCtrl = mwmp::Networking::getPtr();
|
||||||
func(this, pl);
|
RakNet::SystemAddress addr = netCtrl->getSystemAddress(guid);
|
||||||
|
netCtrl->banAddress(addr.ToString(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::cleanChat()
|
||||||
|
{
|
||||||
|
chatMessage.clear();
|
||||||
|
|
||||||
|
auto packet = mwmp::Networking::get().getPlayerPacketController();
|
||||||
|
packet->GetPacket(ID_CHAT_MESSAGE)->setPlayer(this);
|
||||||
|
packet->GetPacket(ID_CHAT_MESSAGE)->Send(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Player::getIP() const
|
||||||
|
{
|
||||||
|
RakNet::SystemAddress addr = mwmp::Networking::getPtr()->getSystemAddress(guid);
|
||||||
|
return addr.ToString(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Player::getAvgPing()
|
||||||
|
{
|
||||||
|
return mwmp::Networking::get().getAvgPing(guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Player::getName()
|
||||||
|
{
|
||||||
|
return npc.mName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setName(const std::string &name)
|
||||||
|
{
|
||||||
|
npc.mName = name;
|
||||||
|
baseInfoChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setCharGenStages(int currentStage, int endStage)
|
||||||
|
{
|
||||||
|
charGenState.currentStage = currentStage;
|
||||||
|
charGenState.endStage = endStage;
|
||||||
|
charGenState.isFinished = false;
|
||||||
|
|
||||||
|
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_CHARGEN);
|
||||||
|
packet->setPlayer(this);
|
||||||
|
packet->Send(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::message(const std::string &message, bool toAll)
|
||||||
|
{
|
||||||
|
chatMessage = message;
|
||||||
|
|
||||||
|
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_CHAT_MESSAGE);
|
||||||
|
packet->setPlayer(this);
|
||||||
|
|
||||||
|
packet->Send(false);
|
||||||
|
if (toAll)
|
||||||
|
packet->Send(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Player::getLevel() const
|
||||||
|
{
|
||||||
|
return creatureStats.mLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setLevel(int level)
|
||||||
|
{
|
||||||
|
creatureStats.mLevel = level;
|
||||||
|
levelChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Player::getGender() const
|
||||||
|
{
|
||||||
|
return npc.isMale();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setGender(int gender)
|
||||||
|
{
|
||||||
|
npc.setIsMale(gender);
|
||||||
|
baseInfoChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Player::getRace() const
|
||||||
|
{
|
||||||
|
return npc.mRace;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setRace(const std::string &race)
|
||||||
|
{
|
||||||
|
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Setting race for %s: %s -> %s", npc.mName.c_str(), npc.mRace.c_str(), race.c_str());
|
||||||
|
|
||||||
|
npc.mRace = race;
|
||||||
|
baseInfoChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Player::getHead() const
|
||||||
|
{
|
||||||
|
return npc.mHead;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setHead(const std::string &head)
|
||||||
|
{
|
||||||
|
npc.mHead = head;
|
||||||
|
baseInfoChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Player::getHair() const
|
||||||
|
{
|
||||||
|
return npc.mHair;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setHair(const std::string &hair)
|
||||||
|
{
|
||||||
|
npc.mHair = hair;
|
||||||
|
baseInfoChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Player::getBirthsign() const
|
||||||
|
{
|
||||||
|
return birthsign;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setBirthsign(const std::string &sign)
|
||||||
|
{
|
||||||
|
birthsign = sign;
|
||||||
|
baseInfoChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Player::getBounty() const
|
||||||
|
{
|
||||||
|
return npcStats.mBounty;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setBounty(int bounty)
|
||||||
|
{
|
||||||
|
npcStats.mBounty = bounty;
|
||||||
|
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_BOUNTY);
|
||||||
|
packet->setPlayer(this);
|
||||||
|
packet->Send(false);
|
||||||
|
packet->Send(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Player::getLevelProgress() const
|
||||||
|
{
|
||||||
|
return npcStats.mLevelProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setLevelProgress(int progress)
|
||||||
|
{
|
||||||
|
npcStats.mLevelProgress = progress;
|
||||||
|
levelChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Player::getCreatureModel() const
|
||||||
|
{
|
||||||
|
return creatureModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setCreatureModel(const std::string &model)
|
||||||
|
{
|
||||||
|
creatureModel = model;
|
||||||
|
baseInfoChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::creatureName(bool useName)
|
||||||
|
{
|
||||||
|
useCreatureName = useName;
|
||||||
|
baseInfoChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Player::isCreatureName() const
|
||||||
|
{
|
||||||
|
return useCreatureName;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<int, int> Player::getAttribute(unsigned short attributeId) const
|
||||||
|
{
|
||||||
|
if (attributeId >= ESM::Attribute::Length)
|
||||||
|
return make_tuple(0, 0);
|
||||||
|
|
||||||
|
return make_tuple(creatureStats.mAttributes[attributeId].mBase, creatureStats.mAttributes[attributeId].mCurrent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setAttribute(unsigned short attributeId, int base, int current)
|
||||||
|
{
|
||||||
|
if (attributeId >= ESM::Attribute::Length)
|
||||||
|
return;
|
||||||
|
|
||||||
|
creatureStats.mAttributes[attributeId].mBase = base;
|
||||||
|
creatureStats.mAttributes[attributeId].mCurrent = current;
|
||||||
|
|
||||||
|
if (!Utils::vectorContains(&attributeIndexChanges, attributeId))
|
||||||
|
attributeIndexChanges.push_back(attributeId);
|
||||||
|
|
||||||
|
attributesChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<int, int, float> Player::getSkill(unsigned short skillId) const
|
||||||
|
{
|
||||||
|
if (skillId >= ESM::Skill::Length)
|
||||||
|
return make_tuple(0, 0, 0.0f);
|
||||||
|
|
||||||
|
const auto &skill = npcStats.mSkills[skillId];
|
||||||
|
|
||||||
|
return make_tuple(skill.mBase, skill.mCurrent, skill.mProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setSkill(unsigned short skillId, int base, int current, float progress)
|
||||||
|
{
|
||||||
|
if (skillId >= ESM::Skill::Length)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto &skill = npcStats.mSkills[skillId];
|
||||||
|
skill.mBase = base;
|
||||||
|
skill.mCurrent = current;
|
||||||
|
skill.mProgress = progress;
|
||||||
|
|
||||||
|
if (!Utils::vectorContains(&skillIndexChanges, skillId))
|
||||||
|
skillIndexChanges.push_back(skillId);
|
||||||
|
|
||||||
|
skillsChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Player::getSkillIncrease(unsigned short attributeId) const
|
||||||
|
{
|
||||||
|
return npcStats.mSkillIncrease[attributeId];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setSkillIncrease(unsigned short attributeId, int increase)
|
||||||
|
{
|
||||||
|
if (attributeId >= ESM::Attribute::Length)
|
||||||
|
return;
|
||||||
|
|
||||||
|
npcStats.mSkillIncrease[attributeId] = increase;
|
||||||
|
|
||||||
|
if (!Utils::vectorContains(&attributeIndexChanges, attributeId))
|
||||||
|
attributeIndexChanges.push_back(attributeId);
|
||||||
|
|
||||||
|
attributesChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CharClass &Player::getCharClass(sol::this_state thisState)
|
||||||
|
{
|
||||||
|
return cClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameSettings &Player::getSettings()
|
||||||
|
{
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
Books &Player::getBooks()
|
||||||
|
{
|
||||||
|
return books;
|
||||||
|
}
|
||||||
|
|
||||||
|
GUI &Player::getGUI()
|
||||||
|
{
|
||||||
|
return gui;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dialogue &Player::getDialogue()
|
||||||
|
{
|
||||||
|
return dialogue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Factions &Player::getFactions()
|
||||||
|
{
|
||||||
|
return factions;
|
||||||
|
}
|
||||||
|
|
||||||
|
Quests &Player::getQuests()
|
||||||
|
{
|
||||||
|
return quests;
|
||||||
|
}
|
||||||
|
|
||||||
|
Spells &Player::getSpells()
|
||||||
|
{
|
||||||
|
return spells;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<float, float, float> Player::getPreviousCellPos() const
|
||||||
|
{
|
||||||
|
return make_tuple(previousCellPosition.pos[0], previousCellPosition.pos[1], previousCellPosition.pos[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::resurrect(unsigned int type)
|
||||||
|
{
|
||||||
|
resurrectType = type;
|
||||||
|
|
||||||
|
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_RESURRECT);
|
||||||
|
packet->setPlayer(this);
|
||||||
|
packet->Send(false);
|
||||||
|
packet->Send(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::jail(int jailDays, bool ignoreJailTeleportation, bool ignoreJailSkillIncreases,
|
||||||
|
const std::string &jailProgressText, const std::string &jailEndText)
|
||||||
|
{
|
||||||
|
this->jailDays = jailDays;
|
||||||
|
this->ignoreJailTeleportation = ignoreJailTeleportation;
|
||||||
|
this->ignoreJailSkillIncreases = ignoreJailSkillIncreases;
|
||||||
|
this->jailProgressText = jailProgressText;
|
||||||
|
this->jailEndText = jailEndText;
|
||||||
|
|
||||||
|
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_JAIL);
|
||||||
|
packet->setPlayer(this);
|
||||||
|
packet->Send(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Player::getWerewolfState() const
|
||||||
|
{
|
||||||
|
return isWerewolf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setWerewolfState(bool state)
|
||||||
|
{
|
||||||
|
isWerewolf = state;
|
||||||
|
|
||||||
|
auto packet = mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_SHAPESHIFT);
|
||||||
|
packet->setPlayer(this);
|
||||||
|
packet->Send(false);
|
||||||
|
packet->Send(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Player::cellStateSize() const
|
||||||
|
{
|
||||||
|
return cellStateChanges.cellStates.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::addCellExplored(const std::string &cellDescription)
|
||||||
|
{
|
||||||
|
auto cellExplored = Utils::getCellFromDescription(cellDescription);
|
||||||
|
mapChanges.cellsExplored.push_back(cellExplored);
|
||||||
|
changedMap = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CellState Player::getCellState(int i)
|
||||||
|
{
|
||||||
|
return CellState(cellStateChanges.cellStates.at(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setAuthority()
|
||||||
|
{
|
||||||
|
mwmp::BaseActorList writeActorList;
|
||||||
|
writeActorList.cell = cell;
|
||||||
|
writeActorList.guid = guid;
|
||||||
|
|
||||||
|
Cell *serverCell = CellController::get()->getCell(&cell);
|
||||||
|
if (serverCell != nullptr)
|
||||||
|
{
|
||||||
|
serverCell->setAuthority(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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <memory>
|
||||||
#include <RakNetTypes.h>
|
#include <RakNetTypes.h>
|
||||||
|
|
||||||
#include <components/esm/npcstats.hpp>
|
#include <components/esm/npcstats.hpp>
|
||||||
|
@ -18,32 +19,30 @@
|
||||||
#include <components/openmw-mp/Log.hpp>
|
#include <components/openmw-mp/Log.hpp>
|
||||||
#include <components/openmw-mp/Base/BasePlayer.hpp>
|
#include <components/openmw-mp/Base/BasePlayer.hpp>
|
||||||
#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>
|
#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>
|
||||||
|
#include <apps/openmw-mp/Script/LuaState.hpp>
|
||||||
#include "Cell.hpp"
|
#include "Cell.hpp"
|
||||||
#include "CellController.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"
|
||||||
|
|
||||||
struct Player;
|
class Player : public mwmp::BasePlayer, public NetActor
|
||||||
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
|
|
||||||
{
|
{
|
||||||
friend class Cell;
|
friend class Cell;
|
||||||
|
friend class Players;
|
||||||
unsigned short id;
|
unsigned short id;
|
||||||
|
uint64_t getGUID() const {return guid.g;}
|
||||||
|
public:
|
||||||
|
static void Init(LuaState &lua);
|
||||||
public:
|
public:
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
@ -52,12 +51,13 @@ public:
|
||||||
LOADED,
|
LOADED,
|
||||||
POSTLOADED
|
POSTLOADED
|
||||||
};
|
};
|
||||||
Player(RakNet::RakNetGUID guid);
|
explicit Player(RakNet::RakNetGUID guid);
|
||||||
|
|
||||||
unsigned short getId();
|
unsigned short getId();
|
||||||
void setId(unsigned short id);
|
void setId(unsigned short id);
|
||||||
|
|
||||||
bool isHandshaked();
|
bool isHandshaked();
|
||||||
|
int handshakeAttempts();
|
||||||
void setHandshake();
|
void setHandshake();
|
||||||
|
|
||||||
void setLoadState(int state);
|
void setLoadState(int state);
|
||||||
|
@ -70,11 +70,129 @@ public:
|
||||||
|
|
||||||
void forEachLoaded(std::function<void(Player *pl, Player *other)> func);
|
void forEachLoaded(std::function<void(Player *pl, Player *other)> func);
|
||||||
|
|
||||||
|
void update();
|
||||||
|
|
||||||
|
public:
|
||||||
|
void kick() const;
|
||||||
|
void ban() const;
|
||||||
|
void cleanChat();
|
||||||
|
|
||||||
|
int getAvgPing();
|
||||||
|
|
||||||
|
std::string getName();
|
||||||
|
void setName(const std::string &name);
|
||||||
|
void setCharGenStages(int currentStage, int endStage);
|
||||||
|
void message(const std::string &message, bool toAll = false);
|
||||||
|
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);
|
||||||
|
|
||||||
|
int getBounty() const;
|
||||||
|
void setBounty(int bounty);
|
||||||
|
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);
|
||||||
|
|
||||||
|
std::string getCreatureModel() const;
|
||||||
|
void setCreatureModel(const std::string &model);
|
||||||
|
void creatureName(bool useName);
|
||||||
|
bool isCreatureName() const;
|
||||||
|
|
||||||
|
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, int current);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return base, current, progress, increase
|
||||||
|
*/
|
||||||
|
std::tuple<int, int, float> getSkill(unsigned short id) const;
|
||||||
|
void setSkill(unsigned short id, int base, int current, float progress);
|
||||||
|
|
||||||
|
int getSkillIncrease(unsigned short attributeId) const;
|
||||||
|
void setSkillIncrease(unsigned short attributeId, int increase);
|
||||||
|
|
||||||
|
CellState getCellState(int i);
|
||||||
|
size_t cellStateSize() const;
|
||||||
|
void addCellExplored(const std::string &cellDescription);
|
||||||
|
|
||||||
|
CharClass &getCharClass(sol::this_state thisState);
|
||||||
|
GameSettings &getSettings();
|
||||||
|
Books &getBooks();
|
||||||
|
GUI &getGUI();
|
||||||
|
Dialogue &getDialogue();
|
||||||
|
Factions &getFactions();
|
||||||
|
Quests &getQuests();
|
||||||
|
Spells &getSpells();
|
||||||
|
|
||||||
|
void setAuthority();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CellController::TContainer cells;
|
CellController::TContainer cells;
|
||||||
bool handshakeState;
|
int handshakeCounter;
|
||||||
int loadState;
|
int loadState;
|
||||||
|
bool /*statsChanged, attributesChanged, skillsChanged, baseInfoChanged, positionChanged,*/ changedMap;
|
||||||
|
CharClass cClass;
|
||||||
|
GameSettings settings;
|
||||||
|
Books books;
|
||||||
|
GUI gui;
|
||||||
|
Dialogue dialogue;
|
||||||
|
Factions factions;
|
||||||
|
Quests quests;
|
||||||
|
Spells spells;
|
||||||
|
sol::table customData;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //OPENMW_PLAYER_HPP
|
#endif //OPENMW_PLAYER_HPP
|
||||||
|
|
115
apps/openmw-mp/Players.cpp
Normal file
115
apps/openmw-mp/Players.cpp
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
//
|
||||||
|
// Created by koncord on 12.08.17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Players.hpp"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
Players::Store Players::store;
|
||||||
|
|
||||||
|
void Players::Init(LuaState &lua)
|
||||||
|
{
|
||||||
|
sol::table playersTable = lua.getState()->create_named_table("Players");
|
||||||
|
playersTable.set_function("getByPID", &Players::getPlayerByPID);
|
||||||
|
playersTable.set_function("getByGUID", &Players::getPlayerByGUID);
|
||||||
|
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())
|
||||||
|
return *it;
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Player> Players::addPlayer(RakNet::RakNetGUID guid)
|
||||||
|
{
|
||||||
|
const int maxConnections = 65535;
|
||||||
|
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Creating new player with guid %lu", guid.g);
|
||||||
|
|
||||||
|
auto player = make_shared<Player>(guid);
|
||||||
|
|
||||||
|
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_INFO, "Marking player (pid %i) for deletion", pid);
|
||||||
|
auto &ls = store.get<ByID>();
|
||||||
|
auto it = ls.find(pid);
|
||||||
|
if (it != ls.end())
|
||||||
|
{
|
||||||
|
ls.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Players::deletePlayerByGUID(RakNet::RakNetGUID guid)
|
||||||
|
{
|
||||||
|
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Marking player (guid %lu) for deletion", guid.g);
|
||||||
|
auto &ls = store.get<ByGUID>();
|
||||||
|
auto it = ls.find(guid.g);
|
||||||
|
if (it != ls.end())
|
||||||
|
{
|
||||||
|
LOG_APPEND(Log::LOG_INFO, "- references: %d", it->use_count());
|
||||||
|
ls.erase(it);
|
||||||
|
LOG_APPEND(Log::LOG_INFO, "- references: %d", it->use_count());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Players::for_each(std::function<void (std::shared_ptr<Player>)> func)
|
||||||
|
{
|
||||||
|
for (auto &player : store)
|
||||||
|
func(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
53
apps/openmw-mp/Players.hpp
Normal file
53
apps/openmw-mp/Players.hpp
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
//
|
||||||
|
// 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 "Player.hpp"
|
||||||
|
|
||||||
|
class LuaState;
|
||||||
|
|
||||||
|
|
||||||
|
class Players
|
||||||
|
{
|
||||||
|
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(std::shared_ptr<Player>)> func);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static Store store;
|
||||||
|
};
|
||||||
|
|
157
apps/openmw-mp/Quests.cpp
Normal file
157
apps/openmw-mp/Quests.cpp
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
//
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int JournalItem::getType() const
|
||||||
|
{
|
||||||
|
return item.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JournalItem::setType(int 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) : player(player), changedKills(false), changedJournal(false)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Quests::~Quests()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Quests::update()
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Quests::setJournalItem(unsigned int id, JournalItem item)
|
||||||
|
{
|
||||||
|
player->journalChanges.journalItems.at(id) = item.item;
|
||||||
|
changedJournal = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
62
apps/openmw-mp/Quests.hpp
Normal file
62
apps/openmw-mp/Quests.hpp
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
//
|
||||||
|
// Created by koncord on 25.08.17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <components/openmw-mp/Base/BasePlayer.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);
|
||||||
|
|
||||||
|
int getType() const;
|
||||||
|
void setType(int type);
|
||||||
|
|
||||||
|
std::string getActorRefId() const;
|
||||||
|
void setActorRefId(const std::string &refid);
|
||||||
|
|
||||||
|
|
||||||
|
mwmp::JournalItem item;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Quests
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void Init(LuaState &lua);
|
||||||
|
public:
|
||||||
|
explicit Quests(Player *player);
|
||||||
|
~Quests();
|
||||||
|
|
||||||
|
void update();
|
||||||
|
|
||||||
|
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:
|
||||||
|
Player *player;
|
||||||
|
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
|
|
102
apps/openmw-mp/Script/CommandController.cpp
Normal file
102
apps/openmw-mp/Script/CommandController.cpp
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
//
|
||||||
|
// 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(std::shared_ptr<Player> player, const std::string &message)
|
||||||
|
{
|
||||||
|
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(tokens));
|
||||||
|
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))
|
||||||
|
throw runtime_error("failed to parse message: "+ message);*/
|
||||||
|
|
||||||
|
if (!x3::parse(message.cbegin(), message.cend(), arguments, ret))
|
||||||
|
throw runtime_error("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(std::shared_ptr<Player> player, const std::string &message);
|
||||||
|
private:
|
||||||
|
std::deque<std::string> cmdParser(const std::string &message);
|
||||||
|
Container commands;
|
||||||
|
};
|
152
apps/openmw-mp/Script/EventController.cpp
Normal file
152
apps/openmw-mp/Script/EventController.cpp
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
//
|
||||||
|
// 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);
|
||||||
|
};
|
||||||
|
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_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_TOPIC),
|
||||||
|
ADD_CORE_EVENT(ON_PLAYER_DISPOSITION),
|
||||||
|
ADD_CORE_EVENT(ON_PLAYER_BOOK),
|
||||||
|
ADD_CORE_EVENT(ON_PLAYER_MAP),
|
||||||
|
ADD_CORE_EVENT(ON_PLAYER_REST),
|
||||||
|
ADD_CORE_EVENT(ON_PLAYER_SENDMESSAGE),
|
||||||
|
ADD_CORE_EVENT(ON_PLAYER_ENDCHARGEN),
|
||||||
|
ADD_CORE_EVENT(ON_GUI_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)
|
||||||
|
);
|
||||||
|
|
||||||
|
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)
|
||||||
|
throw runtime_error("Event " + to_string(i) + " is not registered. Dear developer, please fix me :D");
|
||||||
|
#endif
|
||||||
|
events[i]; // create core event
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventController::registerEvent(int event, sol::environment &env, sol::function& func)
|
||||||
|
{
|
||||||
|
auto iter = events.find(event);
|
||||||
|
if (iter != events.end())
|
||||||
|
iter->second.push(env, func);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.first["ModuleName"]["name"] == moduleName;
|
||||||
|
});
|
||||||
|
if (f != iter->second.end())
|
||||||
|
f->second.call(data); // call only specified mod
|
||||||
|
}
|
||||||
|
iter->second.call(CallbackCollection::type_tag<void>(), data); // call all registered events with this id
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cerr << "Event with id: " << id << " is not registered" << endl;
|
||||||
|
}
|
155
apps/openmw-mp/Script/EventController.hpp
Normal file
155
apps/openmw-mp/Script/EventController.hpp
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
//
|
||||||
|
// 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_EQUIPMENT,
|
||||||
|
ON_PLAYER_INVENTORY,
|
||||||
|
ON_PLAYER_JOURNAL,
|
||||||
|
ON_PLAYER_FACTION,
|
||||||
|
ON_PLAYER_SHAPESHIFT,
|
||||||
|
ON_PLAYER_SPELLBOOK,
|
||||||
|
ON_PLAYER_TOPIC,
|
||||||
|
ON_PLAYER_DISPOSITION,
|
||||||
|
ON_PLAYER_BOOK,
|
||||||
|
ON_PLAYER_MAP,
|
||||||
|
ON_PLAYER_REST,
|
||||||
|
ON_PLAYER_SENDMESSAGE,
|
||||||
|
ON_PLAYER_ENDCHARGEN,
|
||||||
|
|
||||||
|
ON_GUI_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,
|
||||||
|
|
||||||
|
LAST,
|
||||||
|
};
|
||||||
|
const int FIRST = ON_EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CallbackCollection // todo: add sort by dependencies
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef std::vector<std::pair<sol::environment, sol::function>> Container;
|
||||||
|
typedef Container::iterator Iterator;
|
||||||
|
typedef Container::const_iterator CIterator;
|
||||||
|
|
||||||
|
template<typename> struct type_tag {};
|
||||||
|
|
||||||
|
void push(sol::environment &env, sol::function &func) { functions.emplace_back(env, func); }
|
||||||
|
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>
|
||||||
|
void call(type_tag<void>, Args&&... args)
|
||||||
|
{
|
||||||
|
lastCalled = functions.end();
|
||||||
|
_stop = false;
|
||||||
|
for (CIterator iter = functions.begin(); iter != functions.end(); ++iter)
|
||||||
|
{
|
||||||
|
if (!_stop)
|
||||||
|
iter->second.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->second.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);
|
||||||
|
CallbackCollection& GetEvents(Event event);
|
||||||
|
Event createEvent();
|
||||||
|
void raiseEvent(Event id, sol::table data, const std::string &moduleName = "");
|
||||||
|
void stop(int event);
|
||||||
|
|
||||||
|
template<Event event, typename R = void, typename... Args>
|
||||||
|
R Call(Args&&... args)
|
||||||
|
{
|
||||||
|
return events.at(event).call(CallbackCollection::type_tag<R>{}, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
Container events;
|
||||||
|
Event lastEvent = CoreEvent::LAST;
|
||||||
|
LuaState *luaCtrl;
|
||||||
|
};
|
|
@ -1,330 +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).equipedItems[slot].refId.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
int ActorFunctions::GetActorEquipmentItemCount(unsigned int i, unsigned short slot) noexcept
|
|
||||||
{
|
|
||||||
return readActorList->baseActors.at(i).equipedItems[slot].count;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ActorFunctions::GetActorEquipmentItemCharge(unsigned int i, unsigned short slot) noexcept
|
|
||||||
{
|
|
||||||
return readActorList->baseActors.at(i).equipedItems[slot].charge;
|
|
||||||
}
|
|
||||||
|
|
||||||
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::EquipActorItem(unsigned short slot, const char *refId, unsigned int count, int charge) noexcept
|
|
||||||
{
|
|
||||||
tempActor.equipedItems[slot].refId = refId;
|
|
||||||
tempActor.equipedItems[slot].count = count;
|
|
||||||
tempActor.equipedItems[slot].charge = charge;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ActorFunctions::UnequipActorItem(unsigned short slot) noexcept
|
|
||||||
{
|
|
||||||
ActorFunctions::EquipActorItem(slot, "", 0, -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::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,563 +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},\
|
|
||||||
\
|
|
||||||
{"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},\
|
|
||||||
\
|
|
||||||
{"EquipActorItem", ActorFunctions::EquipActorItem},\
|
|
||||||
{"UnequipActorItem", ActorFunctions::UnequipActorItem},\
|
|
||||||
\
|
|
||||||
{"AddActor", ActorFunctions::AddActor},\
|
|
||||||
\
|
|
||||||
{"SendActorList", ActorFunctions::SendActorList},\
|
|
||||||
{"SendActorAuthority", ActorFunctions::SendActorAuthority},\
|
|
||||||
{"SendActorPosition", ActorFunctions::SendActorPosition},\
|
|
||||||
{"SendActorStatsDynamic", ActorFunctions::SendActorStatsDynamic},\
|
|
||||||
{"SendActorEquipment", ActorFunctions::SendActorEquipment},\
|
|
||||||
{"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 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 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.
|
|
||||||
* \return void
|
|
||||||
*/
|
|
||||||
static void EquipActorItem(unsigned short slot, const char* refId, unsigned int count, int charge) 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 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,151 +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;
|
|
||||||
|
|
||||||
void CellFunctions::InitializeMapChanges(unsigned short pid) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, );
|
|
||||||
|
|
||||||
player->mapChanges.cellsExplored.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
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::AddCellExplored(unsigned short pid, const char* cellDescription) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, );
|
|
||||||
|
|
||||||
ESM::Cell cellExplored = Utils::getCellFromDescription(cellDescription);
|
|
||||||
player->mapChanges.cellsExplored.push_back(cellExplored);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CellFunctions::SendMapChanges(unsigned short pid, bool toOthers) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, );
|
|
||||||
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_MAP)->setPlayer(player);
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_MAP)->Send(toOthers);
|
|
||||||
}
|
|
|
@ -1,179 +0,0 @@
|
||||||
#ifndef OPENMW_CELLAPI_HPP
|
|
||||||
#define OPENMW_CELLAPI_HPP
|
|
||||||
|
|
||||||
#include "../Types.hpp"
|
|
||||||
|
|
||||||
#define CELLAPI \
|
|
||||||
{"InitializeMapChanges", CellFunctions::InitializeMapChanges},\
|
|
||||||
\
|
|
||||||
{"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},\
|
|
||||||
\
|
|
||||||
{"AddCellExplored", CellFunctions::AddCellExplored},\
|
|
||||||
\
|
|
||||||
{"SendCell", CellFunctions::SendCell},\
|
|
||||||
{"SendMapChanges", CellFunctions::SendMapChanges}
|
|
||||||
|
|
||||||
|
|
||||||
class CellFunctions
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Clear the last recorded map changes for a player.
|
|
||||||
*
|
|
||||||
* This is used to initialize the sending of new PlayerMap packets.
|
|
||||||
*
|
|
||||||
* \param pid The player ID whose map changes should be used.
|
|
||||||
* \return void
|
|
||||||
*/
|
|
||||||
static void InitializeMapChanges(unsigned short pid) noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \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 Add a new explored cell to the map changes for a player.
|
|
||||||
*
|
|
||||||
* \param pid The player ID whose map changes should be used.
|
|
||||||
* \param cellDescription The cell description of the explored cell.
|
|
||||||
* \return void
|
|
||||||
*/
|
|
||||||
static void AddCellExplored(unsigned short pid, const char* cellDescription) 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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Send a PlayerMap packet with a player's recorded map changes.
|
|
||||||
*
|
|
||||||
* \param pid The player ID whose map 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 SendMapChanges(unsigned short pid, bool toOthers = false) 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,46 +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::CleanChatByPid(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,55 +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);
|
|
||||||
}
|
|
|
@ -1,68 +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}
|
|
||||||
|
|
||||||
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;
|
|
||||||
private:
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //OPENMW_DIALOGUEAPI_HPP
|
|
|
@ -1,118 +0,0 @@
|
||||||
#include "Factions.hpp"
|
|
||||||
|
|
||||||
#include <components/misc/stringops.hpp>
|
|
||||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
|
||||||
|
|
||||||
#include <apps/openmw-mp/Script/ScriptFunctions.hpp>
|
|
||||||
#include <apps/openmw-mp/Networking.hpp>
|
|
||||||
|
|
||||||
using namespace mwmp;
|
|
||||||
|
|
||||||
Faction tempFaction;
|
|
||||||
const Faction emptyFaction = {};
|
|
||||||
|
|
||||||
void FactionFunctions::InitializeFactionChanges(unsigned short pid) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, );
|
|
||||||
|
|
||||||
player->factionChanges.factions.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int FactionFunctions::GetFactionChangesSize(unsigned short pid) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, 0);
|
|
||||||
|
|
||||||
return player->factionChanges.count;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char FactionFunctions::GetFactionChangesAction(unsigned short pid) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, 0);
|
|
||||||
|
|
||||||
return player->factionChanges.action;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *FactionFunctions::GetFactionId(unsigned short pid, unsigned int i) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, "");
|
|
||||||
|
|
||||||
if (i >= player->factionChanges.count)
|
|
||||||
return "invalid";
|
|
||||||
|
|
||||||
return player->factionChanges.factions.at(i).factionId.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
int FactionFunctions::GetFactionRank(unsigned short pid, unsigned int i) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, 0);
|
|
||||||
|
|
||||||
return player->factionChanges.factions.at(i).rank;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FactionFunctions::GetFactionExpulsionState(unsigned short pid, unsigned int i) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, false);
|
|
||||||
|
|
||||||
return player->factionChanges.factions.at(i).isExpelled;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FactionFunctions::GetFactionReputation(unsigned short pid, unsigned int i) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, 0);
|
|
||||||
|
|
||||||
return player->factionChanges.factions.at(i).reputation;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FactionFunctions::SetFactionChangesAction(unsigned short pid, unsigned char action) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, );
|
|
||||||
|
|
||||||
player->factionChanges.action = action;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FactionFunctions::SetFactionId(const char* factionId) noexcept
|
|
||||||
{
|
|
||||||
tempFaction.factionId = factionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FactionFunctions::SetFactionRank(unsigned int rank) noexcept
|
|
||||||
{
|
|
||||||
tempFaction.rank = rank;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FactionFunctions::SetFactionExpulsionState(bool expulsionState) noexcept
|
|
||||||
{
|
|
||||||
tempFaction.isExpelled = expulsionState;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FactionFunctions::SetFactionReputation(int reputation) noexcept
|
|
||||||
{
|
|
||||||
tempFaction.reputation = reputation;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FactionFunctions::AddFaction(unsigned short pid) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, );
|
|
||||||
|
|
||||||
player->factionChanges.factions.push_back(tempFaction);
|
|
||||||
|
|
||||||
tempFaction = emptyFaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FactionFunctions::SendFactionChanges(unsigned short pid, bool toOthers) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, );
|
|
||||||
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_FACTION)->setPlayer(player);
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_FACTION)->Send(toOthers);
|
|
||||||
}
|
|
|
@ -1,156 +0,0 @@
|
||||||
#ifndef OPENMW_FACTIONAPI_HPP
|
|
||||||
#define OPENMW_FACTIONAPI_HPP
|
|
||||||
|
|
||||||
#define FACTIONAPI \
|
|
||||||
{"InitializeFactionChanges", FactionFunctions::InitializeFactionChanges},\
|
|
||||||
\
|
|
||||||
{"GetFactionChangesSize", FactionFunctions::GetFactionChangesSize},\
|
|
||||||
{"GetFactionChangesAction", FactionFunctions::GetFactionChangesAction},\
|
|
||||||
\
|
|
||||||
{"GetFactionId", FactionFunctions::GetFactionId},\
|
|
||||||
{"GetFactionRank", FactionFunctions::GetFactionRank},\
|
|
||||||
{"GetFactionExpulsionState", FactionFunctions::GetFactionExpulsionState},\
|
|
||||||
{"GetFactionReputation", FactionFunctions::GetFactionReputation},\
|
|
||||||
\
|
|
||||||
{"SetFactionChangesAction", FactionFunctions::SetFactionChangesAction},\
|
|
||||||
{"SetFactionId", FactionFunctions::SetFactionId},\
|
|
||||||
{"SetFactionRank", FactionFunctions::SetFactionRank},\
|
|
||||||
{"SetFactionExpulsionState", FactionFunctions::SetFactionExpulsionState},\
|
|
||||||
{"SetFactionReputation", FactionFunctions::SetFactionReputation},\
|
|
||||||
\
|
|
||||||
{"AddFaction", FactionFunctions::AddFaction},\
|
|
||||||
\
|
|
||||||
{"SendFactionChanges", FactionFunctions::SendFactionChanges}
|
|
||||||
|
|
||||||
class FactionFunctions
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Clear the last recorded faction changes for a player.
|
|
||||||
*
|
|
||||||
* This is used to initialize the sending of new PlayerFaction packets.
|
|
||||||
*
|
|
||||||
* \param pid The player ID whose faction changes should be used.
|
|
||||||
* \return void
|
|
||||||
*/
|
|
||||||
static void InitializeFactionChanges(unsigned short pid) noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Get the number of indexes in a player's latest faction changes.
|
|
||||||
*
|
|
||||||
* \param pid The player ID whose faction changes should be used.
|
|
||||||
* \return The number of indexes.
|
|
||||||
*/
|
|
||||||
static unsigned int GetFactionChangesSize(unsigned short pid) noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Get the action type used in a player's latest faction changes.
|
|
||||||
*
|
|
||||||
* \param pid The player ID whose faction changes should be used.
|
|
||||||
* \return The action type (0 for RANK, 1 for EXPULSION, 2 for REPUTATION).
|
|
||||||
*/
|
|
||||||
static unsigned char GetFactionChangesAction(unsigned short pid) noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Get the factionId at a certain index in a player's latest faction changes.
|
|
||||||
*
|
|
||||||
* \param pid The player ID whose faction changes should be used.
|
|
||||||
* \param i The index of the faction.
|
|
||||||
* \return The factionId.
|
|
||||||
*/
|
|
||||||
static const char *GetFactionId(unsigned short pid, unsigned int i) noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Get the rank at a certain index in a player's latest faction changes.
|
|
||||||
*
|
|
||||||
* \param pid The player ID whose faction changes should be used.
|
|
||||||
* \param i The index of the faction.
|
|
||||||
* \return The rank.
|
|
||||||
*/
|
|
||||||
static int GetFactionRank(unsigned short pid, unsigned int i) noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Get the expulsion state at a certain index in a player's latest faction changes.
|
|
||||||
*
|
|
||||||
* \param pid The player ID whose faction changes should be used.
|
|
||||||
* \param i The index of the faction.
|
|
||||||
* \return The expulsion state.
|
|
||||||
*/
|
|
||||||
static bool GetFactionExpulsionState(unsigned short pid, unsigned int i) noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Get the reputation at a certain index in a player's latest faction changes.
|
|
||||||
*
|
|
||||||
* \param pid The player ID whose faction changes should be used.
|
|
||||||
* \param i The index of the faction.
|
|
||||||
* \return The reputation.
|
|
||||||
*/
|
|
||||||
static int GetFactionReputation(unsigned short pid, unsigned int i) noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Set the action type in a player's faction changes.
|
|
||||||
*
|
|
||||||
* \param pid The player ID whose faction changes should be used.
|
|
||||||
* \param action The action (0 for RANK, 1 for EXPULSION, 2 for REPUTATION).
|
|
||||||
* \return void
|
|
||||||
*/
|
|
||||||
static void SetFactionChangesAction(unsigned short pid, unsigned char action) noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Set the factionId of the temporary faction stored on the server.
|
|
||||||
*
|
|
||||||
* \param factionId The factionId.
|
|
||||||
* \return void
|
|
||||||
*/
|
|
||||||
static void SetFactionId(const char* factionId) noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Set the rank of the temporary faction stored on the server.
|
|
||||||
*
|
|
||||||
* \param rank The rank.
|
|
||||||
* \return void
|
|
||||||
*/
|
|
||||||
static void SetFactionRank(unsigned int rank) noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Set the expulsion state of the temporary faction stored on the server.
|
|
||||||
*
|
|
||||||
* \param expulsionState The expulsion state.
|
|
||||||
* \return void
|
|
||||||
*/
|
|
||||||
static void SetFactionExpulsionState(bool expulsionState) noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Set the reputation of the temporary faction stored on the server.
|
|
||||||
*
|
|
||||||
* \param reputation The reputation.
|
|
||||||
* \return void
|
|
||||||
*/
|
|
||||||
static void SetFactionReputation(int reputation) noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Add the server's temporary faction to the faction changes for a player.
|
|
||||||
*
|
|
||||||
* In the process, the server's temporary faction will automatically be cleared so a new one
|
|
||||||
* can be set up.
|
|
||||||
*
|
|
||||||
* \param pid The player ID whose faction changes should be used.
|
|
||||||
* \return void
|
|
||||||
*/
|
|
||||||
static void AddFaction(unsigned short pid) noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Send a PlayerFaction packet with a player's recorded faction changes.
|
|
||||||
*
|
|
||||||
* \param pid The player ID whose faction 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 SendFactionChanges(unsigned short pid, bool toOthers = false) noexcept;
|
|
||||||
private:
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //OPENMW_FACTIONAPI_HPP
|
|
|
@ -1,88 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 23.07.16.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "GUI.hpp"
|
|
||||||
|
|
||||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
|
||||||
|
|
||||||
#include <apps/openmw-mp/Script/ScriptFunctions.hpp>
|
|
||||||
#include <apps/openmw-mp/Networking.hpp>
|
|
||||||
|
|
||||||
void GUIFunctions::_MessageBox(unsigned short pid, int id, const char *label) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player,);
|
|
||||||
|
|
||||||
player->guiMessageBox.id = id;
|
|
||||||
player->guiMessageBox.label = label;
|
|
||||||
player->guiMessageBox.type = Player::GUIMessageBox::MessageBox;
|
|
||||||
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_GUI_MESSAGEBOX)->setPlayer(player);
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_GUI_MESSAGEBOX)->Send(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GUIFunctions::CustomMessageBox(unsigned short pid, int id, const char *label, const char *buttons) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player,);
|
|
||||||
|
|
||||||
player->guiMessageBox.id = id;
|
|
||||||
player->guiMessageBox.label = label;
|
|
||||||
player->guiMessageBox.buttons = buttons;
|
|
||||||
player->guiMessageBox.type = Player::GUIMessageBox::CustomMessageBox;
|
|
||||||
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_GUI_MESSAGEBOX)->setPlayer(player);
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_GUI_MESSAGEBOX)->Send(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GUIFunctions::InputDialog(unsigned short pid, int id, const char *label) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player,);
|
|
||||||
|
|
||||||
player->guiMessageBox.id = id;
|
|
||||||
player->guiMessageBox.label = label;
|
|
||||||
player->guiMessageBox.type = Player::GUIMessageBox::InputDialog;
|
|
||||||
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_GUI_MESSAGEBOX)->setPlayer(player);
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_GUI_MESSAGEBOX)->Send(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GUIFunctions::PasswordDialog(unsigned short pid, int id, const char *label, const char *note) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player,);
|
|
||||||
|
|
||||||
player->guiMessageBox.id = id;
|
|
||||||
player->guiMessageBox.label = label;
|
|
||||||
player->guiMessageBox.note = note;
|
|
||||||
player->guiMessageBox.type = Player::GUIMessageBox::PasswordDialog;
|
|
||||||
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_GUI_MESSAGEBOX)->setPlayer(player);
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_GUI_MESSAGEBOX)->Send(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GUIFunctions::ListBox(unsigned short pid, int id, const char *label, const char *items)
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player,);
|
|
||||||
|
|
||||||
player->guiMessageBox.id = id;
|
|
||||||
player->guiMessageBox.label = label;
|
|
||||||
player->guiMessageBox.data = items;
|
|
||||||
player->guiMessageBox.type = Player::GUIMessageBox::ListBox;
|
|
||||||
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_GUI_MESSAGEBOX)->setPlayer(player);
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_GUI_MESSAGEBOX)->Send(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GUIFunctions::SetMapVisibility(unsigned short targetPID, unsigned short affectedPID, unsigned short state) noexcept
|
|
||||||
{
|
|
||||||
LOG_MESSAGE(Log::LOG_WARN, "stub");
|
|
||||||
}
|
|
||||||
|
|
||||||
void GUIFunctions::SetMapVisibilityAll(unsigned short targetPID, unsigned short state) noexcept
|
|
||||||
{
|
|
||||||
LOG_MESSAGE(Log::LOG_WARN, "stub");
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 30.08.16.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef OPENMW_GUIAPI_HPP
|
|
||||||
#define OPENMW_GUIAPI_HPP
|
|
||||||
|
|
||||||
#define GUIAPI \
|
|
||||||
{"MessageBox", GUIFunctions::_MessageBox},\
|
|
||||||
{"CustomMessageBox", GUIFunctions::CustomMessageBox},\
|
|
||||||
{"InputDialog", GUIFunctions::InputDialog},\
|
|
||||||
{"PasswordDialog", GUIFunctions::PasswordDialog},\
|
|
||||||
{"ListBox", GUIFunctions::ListBox},\
|
|
||||||
{"SetMapVisibility", GUIFunctions::SetMapVisibility},\
|
|
||||||
{"SetMapVisibilityAll", GUIFunctions::SetMapVisibilityAll}
|
|
||||||
|
|
||||||
class GUIFunctions
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/* Do not rename into MessageBox to not conflict with WINAPI's MessageBox */
|
|
||||||
static void _MessageBox(unsigned short pid, int id, const char *label) noexcept;
|
|
||||||
|
|
||||||
static void CustomMessageBox(unsigned short pid, int id, const char *label, const char *buttons) noexcept;
|
|
||||||
static void InputDialog(unsigned short pid, int id, const char *label) noexcept;
|
|
||||||
static void PasswordDialog(unsigned short pid, int id, const char *label, const char *note) noexcept;
|
|
||||||
|
|
||||||
static void ListBox(unsigned short pid, int id, const char *label, const char *items);
|
|
||||||
|
|
||||||
//state 0 - disallow, 1 - allow
|
|
||||||
static void SetMapVisibility(unsigned short targetPID, unsigned short affectedPID, unsigned short state) noexcept;
|
|
||||||
static void SetMapVisibilityAll(unsigned short targetPID, unsigned short state) noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //OPENMW_GUIAPI_HPP
|
|
|
@ -1,162 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 02.03.16.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "Items.hpp"
|
|
||||||
|
|
||||||
#include <components/misc/stringops.hpp>
|
|
||||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
|
||||||
|
|
||||||
#include <apps/openmw-mp/Script/ScriptFunctions.hpp>
|
|
||||||
#include <apps/openmw-mp/Networking.hpp>
|
|
||||||
#include <apps/openmw/mwworld/inventorystore.hpp>
|
|
||||||
|
|
||||||
using namespace mwmp;
|
|
||||||
|
|
||||||
void ItemFunctions::InitializeInventoryChanges(unsigned short pid) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, );
|
|
||||||
|
|
||||||
player->inventoryChanges.items.clear();
|
|
||||||
player->inventoryChanges.action = InventoryChanges::SET;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ItemFunctions::GetEquipmentSize() noexcept
|
|
||||||
{
|
|
||||||
return MWWorld::InventoryStore::Slots;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int ItemFunctions::GetInventoryChangesSize(unsigned short pid) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, 0);
|
|
||||||
|
|
||||||
return player->inventoryChanges.count;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ItemFunctions::EquipItem(unsigned short pid, unsigned short slot, const char *refId, unsigned int count, int charge) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player,);
|
|
||||||
|
|
||||||
player->equipedItems[slot].refId = refId;
|
|
||||||
player->equipedItems[slot].count = count;
|
|
||||||
player->equipedItems[slot].charge = charge;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ItemFunctions::UnequipItem(unsigned short pid, unsigned short slot) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, );
|
|
||||||
|
|
||||||
ItemFunctions::EquipItem(pid, slot, "", 0, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ItemFunctions::AddItem(unsigned short pid, const char* refId, unsigned int count, int charge) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, );
|
|
||||||
|
|
||||||
Item item;
|
|
||||||
item.refId = refId;
|
|
||||||
item.count = count;
|
|
||||||
item.charge = charge;
|
|
||||||
|
|
||||||
player->inventoryChanges.items.push_back(item);
|
|
||||||
player->inventoryChanges.action = InventoryChanges::ADD;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ItemFunctions::RemoveItem(unsigned short pid, const char* refId, unsigned short count) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, );
|
|
||||||
|
|
||||||
Item item;
|
|
||||||
item.refId = refId;
|
|
||||||
item.count = count;
|
|
||||||
|
|
||||||
player->inventoryChanges.items.push_back(item);
|
|
||||||
player->inventoryChanges.action = InventoryChanges::REMOVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ItemFunctions::HasItemEquipped(unsigned short pid, const char* refId)
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, false);
|
|
||||||
|
|
||||||
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; slot++)
|
|
||||||
if (Misc::StringUtils::ciEqual(player->equipedItems[slot].refId, refId))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *ItemFunctions::GetEquipmentItemRefId(unsigned short pid, unsigned short slot) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, 0);
|
|
||||||
|
|
||||||
return player->equipedItems[slot].refId.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
int ItemFunctions::GetEquipmentItemCount(unsigned short pid, unsigned short slot) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, 0);
|
|
||||||
|
|
||||||
return player->equipedItems[slot].count;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ItemFunctions::GetEquipmentItemCharge(unsigned short pid, unsigned short slot) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, 0);
|
|
||||||
|
|
||||||
return player->equipedItems[slot].charge;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *ItemFunctions::GetInventoryItemRefId(unsigned short pid, unsigned int i) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, "");
|
|
||||||
|
|
||||||
if (i >= player->inventoryChanges.count)
|
|
||||||
return "invalid";
|
|
||||||
|
|
||||||
return player->inventoryChanges.items.at(i).refId.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
int ItemFunctions::GetInventoryItemCount(unsigned short pid, unsigned int i) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, 0);
|
|
||||||
|
|
||||||
return player->inventoryChanges.items.at(i).count;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ItemFunctions::GetInventoryItemCharge(unsigned short pid, unsigned int i) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, 0);
|
|
||||||
|
|
||||||
return player->inventoryChanges.items.at(i).charge;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ItemFunctions::SendEquipment(unsigned short pid) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, );
|
|
||||||
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_EQUIPMENT)->setPlayer(player);
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_EQUIPMENT)->Send(false);
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_EQUIPMENT)->Send(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ItemFunctions::SendInventoryChanges(unsigned short pid, bool toOthers) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, );
|
|
||||||
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_INVENTORY)->setPlayer(player);
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_INVENTORY)->Send(toOthers);
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
//
|
|
||||||
// Created by koncord on 30.08.16.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef OPENMW_ITEMAPI_HPP
|
|
||||||
#define OPENMW_ITEMAPI_HPP
|
|
||||||
|
|
||||||
#define ITEMAPI \
|
|
||||||
{"InitializeInventoryChanges", ItemFunctions::InitializeInventoryChanges},\
|
|
||||||
\
|
|
||||||
{"GetEquipmentSize", ItemFunctions::GetEquipmentSize},\
|
|
||||||
{"GetInventoryChangesSize", ItemFunctions::GetInventoryChangesSize},\
|
|
||||||
\
|
|
||||||
{"EquipItem", ItemFunctions::EquipItem},\
|
|
||||||
{"UnequipItem", ItemFunctions::UnequipItem},\
|
|
||||||
\
|
|
||||||
{"AddItem", ItemFunctions::AddItem},\
|
|
||||||
{"RemoveItem", ItemFunctions::RemoveItem},\
|
|
||||||
\
|
|
||||||
{"HasItemEquipped", ItemFunctions::HasItemEquipped},\
|
|
||||||
\
|
|
||||||
{"GetEquipmentItemRefId", ItemFunctions::GetEquipmentItemRefId},\
|
|
||||||
{"GetEquipmentItemCount", ItemFunctions::GetEquipmentItemCount},\
|
|
||||||
{"GetEquipmentItemCharge", ItemFunctions::GetEquipmentItemCharge},\
|
|
||||||
\
|
|
||||||
{"GetInventoryItemRefId", ItemFunctions::GetInventoryItemRefId},\
|
|
||||||
{"GetInventoryItemCount", ItemFunctions::GetInventoryItemCount},\
|
|
||||||
{"GetInventoryItemCharge", ItemFunctions::GetInventoryItemCharge},\
|
|
||||||
\
|
|
||||||
{"SendEquipment", ItemFunctions::SendEquipment},\
|
|
||||||
{"SendInventoryChanges", ItemFunctions::SendInventoryChanges}
|
|
||||||
|
|
||||||
class ItemFunctions
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
static void InitializeInventoryChanges(unsigned short pid) noexcept;
|
|
||||||
|
|
||||||
static int GetEquipmentSize() noexcept;
|
|
||||||
static unsigned int GetInventoryChangesSize(unsigned short pid) noexcept;
|
|
||||||
|
|
||||||
static void EquipItem(unsigned short pid, unsigned short slot, const char* refId, unsigned int count, int charge) noexcept;
|
|
||||||
static void UnequipItem(unsigned short pid, unsigned short slot) noexcept;
|
|
||||||
|
|
||||||
static void AddItem(unsigned short pid, const char* refId, unsigned int count, int charge) noexcept;
|
|
||||||
static void RemoveItem(unsigned short pid, const char* refId, unsigned short count) noexcept;
|
|
||||||
|
|
||||||
static bool HasItemEquipped(unsigned short pid, const char* refId);
|
|
||||||
|
|
||||||
static const char *GetEquipmentItemRefId(unsigned short pid, unsigned short slot) noexcept;
|
|
||||||
static int GetEquipmentItemCount(unsigned short pid, unsigned short slot) noexcept;
|
|
||||||
static int GetEquipmentItemCharge(unsigned short pid, unsigned short slot) noexcept;
|
|
||||||
|
|
||||||
static const char *GetInventoryItemRefId(unsigned short pid, unsigned int i) noexcept;
|
|
||||||
static int GetInventoryItemCount(unsigned short pid, unsigned int i) noexcept;
|
|
||||||
static int GetInventoryItemCharge(unsigned short pid, unsigned int i) noexcept;
|
|
||||||
|
|
||||||
static void SendEquipment(unsigned short pid) noexcept;
|
|
||||||
static void SendInventoryChanges(unsigned short pid, bool toOthers = false) noexcept;
|
|
||||||
private:
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //OPENMW_ITEMAPI_HPP
|
|
|
@ -1,64 +0,0 @@
|
||||||
#include "Mechanics.hpp"
|
|
||||||
|
|
||||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
|
||||||
#include <components/openmw-mp/Log.hpp>
|
|
||||||
|
|
||||||
#include <apps/openmw-mp/Script/ScriptFunctions.hpp>
|
|
||||||
#include <apps/openmw-mp/Networking.hpp>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
bool MechanicsFunctions::IsWerewolf(unsigned short pid) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, 0);
|
|
||||||
|
|
||||||
return player->isWerewolf;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MechanicsFunctions::SetWerewolfState(unsigned short pid, bool isWerewolf)
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, );
|
|
||||||
|
|
||||||
player->isWerewolf = isWerewolf;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MechanicsFunctions::SendShapeshift(unsigned short pid)
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, );
|
|
||||||
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_SHAPESHIFT)->setPlayer(player);
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_SHAPESHIFT)->Send(false);
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_SHAPESHIFT)->Send(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MechanicsFunctions::Jail(unsigned short pid, int jailDays, bool ignoreJailTeleportation, bool ignoreJailSkillIncreases,
|
|
||||||
const char* jailProgressText, const char* jailEndText) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, );
|
|
||||||
|
|
||||||
player->jailDays = jailDays;
|
|
||||||
player->ignoreJailTeleportation = ignoreJailTeleportation;
|
|
||||||
player->ignoreJailSkillIncreases = ignoreJailSkillIncreases;
|
|
||||||
player->jailProgressText = jailProgressText;
|
|
||||||
player->jailEndText = jailEndText;
|
|
||||||
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_JAIL)->setPlayer(player);
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_JAIL)->Send(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MechanicsFunctions::Resurrect(unsigned short pid, unsigned int type) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, );
|
|
||||||
|
|
||||||
player->resurrectType = type;
|
|
||||||
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_RESURRECT)->setPlayer(player);
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_RESURRECT)->Send(false);
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_RESURRECT)->Send(true);
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
#ifndef OPENMW_MECHANICSAPI_HPP
|
|
||||||
#define OPENMW_MECHANICSAPI_HPP
|
|
||||||
|
|
||||||
#include "../Types.hpp"
|
|
||||||
|
|
||||||
#define MECHANICSAPI \
|
|
||||||
{"IsWerewolf", MechanicsFunctions::IsWerewolf},\
|
|
||||||
\
|
|
||||||
{"SetWerewolfState", MechanicsFunctions::SetWerewolfState},\
|
|
||||||
\
|
|
||||||
{"SendShapeshift", MechanicsFunctions::SendShapeshift},\
|
|
||||||
\
|
|
||||||
{"Jail", MechanicsFunctions::Jail},\
|
|
||||||
{"Resurrect", MechanicsFunctions::Resurrect}
|
|
||||||
|
|
||||||
class MechanicsFunctions
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Check whether a player is a werewolf.
|
|
||||||
*
|
|
||||||
* This is based on the last PlayerShapeshift packet received or sent for that player.
|
|
||||||
*
|
|
||||||
* \param pid The player ID.
|
|
||||||
* \return The werewolf state.
|
|
||||||
*/
|
|
||||||
static bool IsWerewolf(unsigned short pid) noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Set the werewolf state of a player.
|
|
||||||
*
|
|
||||||
* This changes the werewolf state recorded for that player in the server memory, but
|
|
||||||
* does not by itself send a packet.
|
|
||||||
*
|
|
||||||
* \param pid The player ID.
|
|
||||||
* \param bool The new werewolf state.
|
|
||||||
* \return void
|
|
||||||
*/
|
|
||||||
static void SetWerewolfState(unsigned short pid, bool isWerewolf);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Send a PlayerShapeshift packet about a player.
|
|
||||||
*
|
|
||||||
* This sends the packet to all players connected to the server. It is currently used
|
|
||||||
* only to communicate werewolf states.
|
|
||||||
*
|
|
||||||
* \param pid The player ID.
|
|
||||||
* \return void
|
|
||||||
*/
|
|
||||||
static void SendShapeshift(unsigned short pid);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \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 pid The player ID.
|
|
||||||
* \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.
|
|
||||||
* \return void
|
|
||||||
*/
|
|
||||||
static void Jail(unsigned short pid, int jailDays, bool ignoreJailTeleportation = false, bool ignoreJailSkillIncreases = false,
|
|
||||||
const char* jailProgressText = "", const char* jailEndText = "") noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Send a PlayerResurrect packet about a player.
|
|
||||||
*
|
|
||||||
* This sends the packet to all players connected to the server.
|
|
||||||
*
|
|
||||||
* \param pid The player ID.
|
|
||||||
* \param type The type of resurrection (0 for REGULAR, 1 for IMPERIAL_SHRINE,
|
|
||||||
* 2 for TRIBUNAL_TEMPLE).
|
|
||||||
* \return void
|
|
||||||
*/
|
|
||||||
static void Resurrect(unsigned short pid, unsigned int type) noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //OPENMW_MECHANICSAPI_HPP
|
|
|
@ -1,54 +0,0 @@
|
||||||
#include "Miscellaneous.hpp"
|
|
||||||
|
|
||||||
#include <components/misc/stringops.hpp>
|
|
||||||
#include <components/openmw-mp/Log.hpp>
|
|
||||||
|
|
||||||
#include <apps/openmw-mp/Script/ScriptFunctions.hpp>
|
|
||||||
#include <apps/openmw-mp/Networking.hpp>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
static std::string tempFilename;
|
|
||||||
|
|
||||||
const char *MiscellaneousFunctions::GetCaseInsensitiveFilename(const char *folderPath, const char *filename) noexcept
|
|
||||||
{
|
|
||||||
if (!boost::filesystem::exists(folderPath)) return "invalid";
|
|
||||||
|
|
||||||
boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end
|
|
||||||
|
|
||||||
for (boost::filesystem::directory_iterator itr(folderPath); itr != end_itr; ++itr)
|
|
||||||
{
|
|
||||||
if (Misc::StringUtils::ciEqual(itr->path().filename().string(), filename))
|
|
||||||
{
|
|
||||||
tempFilename = itr->path().filename().string();
|
|
||||||
return tempFilename.c_str();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "invalid";
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int MiscellaneousFunctions::GetLastPlayerId() noexcept
|
|
||||||
{
|
|
||||||
return Players::getLastPlayerId();
|
|
||||||
}
|
|
||||||
|
|
||||||
int MiscellaneousFunctions::GetCurrentMpNum() noexcept
|
|
||||||
{
|
|
||||||
return mwmp::Networking::getPtr()->getCurrentMpNum();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MiscellaneousFunctions::SetCurrentMpNum(int mpNum) noexcept
|
|
||||||
{
|
|
||||||
mwmp::Networking::getPtr()->setCurrentMpNum(mpNum);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MiscellaneousFunctions::LogMessage(unsigned short level, const char *message) noexcept
|
|
||||||
{
|
|
||||||
LOG_MESSAGE_SIMPLE(level, "[Script]: %s", message);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MiscellaneousFunctions::LogAppend(unsigned short level, const char *message) noexcept
|
|
||||||
{
|
|
||||||
LOG_APPEND(level, "[Script]: %s", message);
|
|
||||||
}
|
|
|
@ -1,92 +0,0 @@
|
||||||
#ifndef OPENMW_MISCELLANEOUSAPI_HPP
|
|
||||||
#define OPENMW_MISCELLANEOUSAPI_HPP
|
|
||||||
|
|
||||||
#include "../Types.hpp"
|
|
||||||
|
|
||||||
#define MISCELLANEOUSAPI \
|
|
||||||
{"GetCaseInsensitiveFilename", MiscellaneousFunctions::GetCaseInsensitiveFilename},\
|
|
||||||
\
|
|
||||||
{"GetLastPlayerId", MiscellaneousFunctions::GetLastPlayerId},\
|
|
||||||
\
|
|
||||||
{"GetCurrentMpNum", MiscellaneousFunctions::GetCurrentMpNum},\
|
|
||||||
{"SetCurrentMpNum", MiscellaneousFunctions::SetCurrentMpNum},\
|
|
||||||
\
|
|
||||||
{"LogMessage", MiscellaneousFunctions::LogMessage},\
|
|
||||||
{"LogAppend", MiscellaneousFunctions::LogAppend}
|
|
||||||
|
|
||||||
class MiscellaneousFunctions
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Get the first filename in a folder that has a case insensitive match with the filename
|
|
||||||
* argument.
|
|
||||||
*
|
|
||||||
* This is used to retain case insensitivity when opening data files on Linux.
|
|
||||||
*
|
|
||||||
* \return The filename that matches.
|
|
||||||
*/
|
|
||||||
static const char *GetCaseInsensitiveFilename(const char *folderPath, const char *filename) noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Get the last player ID currently connected to the server.
|
|
||||||
*
|
|
||||||
* Every player receives a unique numerical index known as their player ID upon joining the
|
|
||||||
* server.
|
|
||||||
*
|
|
||||||
* \return The player ID.
|
|
||||||
*/
|
|
||||||
static unsigned int GetLastPlayerId() noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Get the current (latest) mpNum generated by the server.
|
|
||||||
*
|
|
||||||
* Every object that did not exist in an .ESM or .ESP data file and has instead been placed or
|
|
||||||
* spawned through a server-sent packet has a numerical index known as its mpNum.
|
|
||||||
*
|
|
||||||
* When ObjectPlace and ObjectSpawn packets are received from players, their objects lack mpNums,
|
|
||||||
* so the server assigns them some based on incrementing the server's current mpNum, with the
|
|
||||||
* operation's final mpNum becoming the server's new current mpNum.
|
|
||||||
*
|
|
||||||
* \return The mpNum.
|
|
||||||
*/
|
|
||||||
static int GetCurrentMpNum() noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Set the current (latest) mpNum generated by the server.
|
|
||||||
*
|
|
||||||
* When restarting a server, it is important to revert to the previous current (latest) mpNum
|
|
||||||
* as stored in the server's data, so as to avoid starting over from 0 and ending up assigning
|
|
||||||
* duplicate mpNums to objects.
|
|
||||||
*
|
|
||||||
* \param mpNum The number that should be used as the new current mpNum.
|
|
||||||
* \return void
|
|
||||||
*/
|
|
||||||
static void SetCurrentMpNum(int mpNum) noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Write a log message with its own timestamp.
|
|
||||||
*
|
|
||||||
* It will have "[Script]:" prepended to it so as to mark it as a script-generated log message.
|
|
||||||
*
|
|
||||||
* \param level The logging level used (0 for LOG_VERBOSE, 1 for LOG_INFO, 2 for LOG_WARN,
|
|
||||||
* 3 for LOG_ERROR, 4 for LOG_FATAL).
|
|
||||||
* \param message The message logged.
|
|
||||||
* \return void
|
|
||||||
*/
|
|
||||||
static void LogMessage(unsigned short level, const char *message) noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Write a log message without its own timestamp.
|
|
||||||
*
|
|
||||||
* It will have "[Script]:" prepended to it so as to mark it as a script-generated log message.
|
|
||||||
*
|
|
||||||
* \param level The logging level used (0 for LOG_VERBOSE, 1 for LOG_INFO, 2 for LOG_WARN,
|
|
||||||
* 3 for LOG_ERROR, 4 for LOG_FATAL).
|
|
||||||
* \param message The message logged.
|
|
||||||
* \return void
|
|
||||||
*/
|
|
||||||
static void LogAppend(unsigned short level, const char *message) noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //OPENMW_MISCELLANEOUSAPI_HPP
|
|
|
@ -1,128 +0,0 @@
|
||||||
#include "Positions.hpp"
|
|
||||||
#include <apps/openmw-mp/Script/ScriptFunctions.hpp>
|
|
||||||
#include <components/openmw-mp/NetworkMessages.hpp>
|
|
||||||
#include <apps/openmw-mp/Player.hpp>
|
|
||||||
#include <apps/openmw-mp/Networking.hpp>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
void PositionFunctions::GetPos(unsigned short pid, float *x, float *y, float *z) noexcept
|
|
||||||
{
|
|
||||||
*x = 0.00;
|
|
||||||
*y = 0.00;
|
|
||||||
*z = 0.00;
|
|
||||||
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player,);
|
|
||||||
|
|
||||||
*x = player->position.pos[0];
|
|
||||||
*y = player->position.pos[1];
|
|
||||||
*z = player->position.pos[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
double PositionFunctions::GetPosX(unsigned short pid) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, 0.0f);
|
|
||||||
|
|
||||||
return player->position.pos[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
double PositionFunctions::GetPosY(unsigned short pid) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, 0.0f);
|
|
||||||
|
|
||||||
return player->position.pos[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
double PositionFunctions::GetPosZ(unsigned short pid) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, 0.0f);
|
|
||||||
|
|
||||||
return player->position.pos[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
double PositionFunctions::GetPreviousCellPosX(unsigned short pid) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, 0.0f);
|
|
||||||
|
|
||||||
return player->previousCellPosition.pos[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
double PositionFunctions::GetPreviousCellPosY(unsigned short pid) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, 0.0f);
|
|
||||||
|
|
||||||
return player->previousCellPosition.pos[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
double PositionFunctions::GetPreviousCellPosZ(unsigned short pid) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, 0.0f);
|
|
||||||
|
|
||||||
return player->previousCellPosition.pos[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
void PositionFunctions::GetRot(unsigned short pid, float *x, float *y, float *z) noexcept
|
|
||||||
{
|
|
||||||
*x = 0.00;
|
|
||||||
*y = 0.00;
|
|
||||||
*z = 0.00;
|
|
||||||
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, );
|
|
||||||
|
|
||||||
*x = player->position.rot[0];
|
|
||||||
*y = player->position.rot[1];
|
|
||||||
*z = player->position.rot[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
double PositionFunctions::GetRotX(unsigned short pid) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, 0.0f);
|
|
||||||
|
|
||||||
return player->position.rot[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
double PositionFunctions::GetRotZ(unsigned short pid) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, 0.0f);
|
|
||||||
|
|
||||||
return player->position.rot[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
void PositionFunctions::SetPos(unsigned short pid, double x, double y, double z) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player,);
|
|
||||||
|
|
||||||
player->position.pos[0] = x;
|
|
||||||
player->position.pos[1] = y;
|
|
||||||
player->position.pos[2] = z;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PositionFunctions::SetRot(unsigned short pid, double x, double z) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, );
|
|
||||||
|
|
||||||
player->position.rot[0] = x;
|
|
||||||
player->position.rot[2] = z;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PositionFunctions::SendPos(unsigned short pid) noexcept
|
|
||||||
{
|
|
||||||
Player *player;
|
|
||||||
GET_PLAYER(pid, player, );
|
|
||||||
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_POSITION)->setPlayer(player);
|
|
||||||
mwmp::Networking::get().getPlayerPacketController()->GetPacket(ID_PLAYER_POSITION)->Send(false);
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue