Merge pull request #309 from TES3MP/new-script-api

Add new-script-api commits
sol2-server-rewrite
David Cernat 7 years ago committed by GitHub
commit 2bc79fcdf4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

3
.gitmodules vendored

@ -1,3 +1,6 @@
[submodule "extern/breakpad"]
path = extern/breakpad
url = https://chromium.googlesource.com/breakpad/breakpad
[submodule "extern/sol"]
path = extern/sol
url = https://github.com/ThePhD/sol2

@ -213,7 +213,6 @@ endif()
IF(BUILD_OPENMW OR BUILD_OPENCS)
find_package(OpenSceneGraph 3.3.4 REQUIRED osgDB osgViewer osgText osgGA osgParticle osgUtil osgFX)
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
set(USED_OSG_PLUGINS
osgdb_bmp
@ -257,6 +256,8 @@ IF(BUILD_OPENMW OR BUILD_OPENCS)
ENDIF(BUILD_OPENMW OR BUILD_OPENCS)
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}) # HACK: DON'T TOUCH IT!
set(BOOST_COMPONENTS system filesystem program_options)
if(WIN32)

@ -5,7 +5,7 @@ TES3MP
TES3MP is a project aiming to add multiplayer functionality to [OpenMW](https://github.com/OpenMW/openmw), a free and open source engine recreation of the popular Bethesda Softworks game "The Elder Scrolls III: Morrowind".
* TES3MP version: 0.6.1
* TES3MP version: 0.7-alpha
* OpenMW version: 0.43.0
* 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(cBoxNotFull, SIGNAL(toggled(bool)), this, SLOT(notFullSwitch(bool)));
connect(cBoxWithPlayers, SIGNAL(toggled(bool)), this, SLOT(havePlayersSwitch(bool)));
connect(cBBoxWOPass, SIGNAL(toggled(bool)), this, SLOT(noPasswordSwitch(bool)));
connect(comboLatency, SIGNAL(currentIndexChanged(int)), this, SLOT(maxLatencyChanged(int)));
connect(leGamemode, SIGNAL(textChanged(const QString &)), this, SLOT(gamemodeChanged(const QString &)));
loadFavorites();
@ -65,7 +66,7 @@ MainWindow::~MainWindow()
delete mGameInvoker;
}
void MainWindow::addServerAndUpdate(QString addr)
void MainWindow::addServerAndUpdate(const QString &addr)
{
favorites->insertRow(0);
QModelIndex mi = favorites->index(0, ServerData::ADDR);
@ -142,7 +143,8 @@ void MainWindow::play()
if (sm->myData[sourceId].GetPassword() == 1)
{
bool ok;
QString passw = QInputDialog::getText(this, "Connecting to: " + sm->myData[sourceId].addr, "Password: ", QLineEdit::Password, "", &ok);
QString passw = QInputDialog::getText(this, tr("Connecting to: ") + sm->myData[sourceId].addr, tr("Password: "),
QLineEdit::Password, "", &ok);
if (!ok)
return;
arguments.append(QLatin1String("--password=") + passw.toLatin1());
@ -228,6 +230,11 @@ void MainWindow::havePlayersSwitch(bool state)
proxyModel->filterEmptyServers(state);
}
void MainWindow::noPasswordSwitch(bool state)
{
proxyModel->filterPassworded(state);
}
void MainWindow::maxLatencyChanged(int index)
{
int maxLatency = index * 50;

@ -17,11 +17,11 @@ class MainWindow : public QMainWindow, private Ui::MainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
virtual ~MainWindow();
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow() override;
protected:
void closeEvent(QCloseEvent * event) Q_DECL_OVERRIDE;
void addServerAndUpdate(QString addr);
void addServerAndUpdate(const QString &addr);
protected slots:
void tabSwitched(int index);
void addServer();
@ -31,6 +31,7 @@ protected slots:
void serverSelected();
void notFullSwitch(bool state);
void havePlayersSwitch(bool state);
void noPasswordSwitch(bool state);
void maxLatencyChanged(int index);
void gamemodeChanged(const QString &text);
private:

@ -6,6 +6,7 @@
#include "ServerModel.hpp"
#include <qdebug.h>
#include <apps/browser/netutils/Utils.hpp>
bool MySortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
@ -13,26 +14,49 @@ bool MySortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &
QModelIndex pingIndex = sourceModel()->index(sourceRow, ServerData::PING, sourceParent);
QModelIndex plIndex = sourceModel()->index(sourceRow, ServerData::PLAYERS, sourceParent);
QModelIndex maxPlIndex = sourceModel()->index(sourceRow, ServerData::MAX_PLAYERS, sourceParent);
QModelIndex passwordIndex = sourceModel()->index(sourceRow, ServerData::PASSW, sourceParent);
int ping = sourceModel()->data(pingIndex).toInt();
bool pingOk;
int ping = sourceModel()->data(pingIndex).toInt(&pingOk);
int players = sourceModel()->data(plIndex).toInt();
int maxPlayers = sourceModel()->data(maxPlIndex).toInt();
if (maxPing > 0 && (ping == -1 || ping > maxPing))
if (maxPing > 0 && (ping == -1 || ping > maxPing || !pingOk))
return false;
if (filterEmpty && players == 0)
return false;
if (filterFull && players >= maxPlayers)
return false;
if(filterPasswEnabled && sourceModel()->data(passwordIndex).toString() == "Yes")
return false;
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
}
bool MySortFilterProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
if(sortColumn() == ServerData::PING)
{
bool valid;
int pingright = sourceModel()->data(source_right).toInt(&valid);
pingright = valid ? pingright : PING_UNREACHABLE;
int pingleft = sourceModel()->data(source_left).toInt(&valid);
pingleft = valid ? pingleft : PING_UNREACHABLE;
return pingleft < pingright;
}
else
return QSortFilterProxyModel::lessThan(source_left, source_right);
}
MySortFilterProxyModel::MySortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent)
{
filterEmpty = false;
filterFull = false;
filterPasswEnabled = false;
maxPing = 0;
setSortCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);
}
void MySortFilterProxyModel::filterEmptyServers(bool state)
@ -52,3 +76,9 @@ void MySortFilterProxyModel::pingLessThan(int maxPing)
this->maxPing = maxPing;
invalidateFilter();
}
void MySortFilterProxyModel::filterPassworded(bool state)
{
filterPasswEnabled = state;
invalidateFilter();
}

@ -13,13 +13,15 @@ class MySortFilterProxyModel : public QSortFilterProxyModel
Q_OBJECT
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const Q_DECL_FINAL;
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const Q_DECL_FINAL;
public:
MySortFilterProxyModel(QObject *parent);
explicit MySortFilterProxyModel(QObject *parent);
void filterFullServer(bool state);
void filterEmptyServers(bool state);
void filterPassworded(bool state);
void pingLessThan(int maxPing);
private:
bool filterEmpty, filterFull;
bool filterEmpty, filterFull, filterPasswEnabled;
int maxPing;
};

@ -7,7 +7,7 @@
#include <QDebug>
#include "PingUpdater.hpp"
void PingHelper::Add(int row, AddrPair addrPair)
void PingHelper::Add(int row, const AddrPair &addrPair)
{
pingUpdater->addServer(row, addrPair);
if (!pingThread->isRunning())
@ -35,9 +35,8 @@ PingHelper &PingHelper::Get()
return helper;
}
PingHelper::PingHelper()
PingHelper::PingHelper() : QObject()
{
QObject();
pingThread = new QThread;
pingUpdater = new PingUpdater;
pingUpdater->moveToThread(pingThread);
@ -48,11 +47,4 @@ PingHelper::PingHelper()
connect(this, SIGNAL(stop()), pingUpdater, SLOT(stop()));
//connect(pingUpdater, SIGNAL(finished()), pingUpdater, SLOT(deleteLater()));
connect(pingUpdater, SIGNAL(updateModel(int, unsigned)), this, SLOT(update(int, unsigned)));
}
PingHelper::~PingHelper()
{
}

@ -17,17 +17,16 @@ class PingHelper : public QObject
Q_OBJECT
public:
void Add(int row, AddrPair addrPair);
void Add(int row, const AddrPair &addrPair);
void Stop();
void SetModel(QAbstractTableModel *model);
//void UpdateImmedialy(PingUpdater::AddrPair addrPair);
static PingHelper &Get();
private:
PingHelper();
~PingHelper();
PingHelper(const PingHelper&) = delete;
PingHelper& operator=(const PingHelper&) = delete;
private:
PingHelper();
signals:
void stop();
public slots:

@ -14,7 +14,7 @@ void PingUpdater::stop()
run = false;
}
void PingUpdater::addServer(int row, AddrPair addr)
void PingUpdater::addServer(int row, const AddrPair &addr)
{
servers.push_back({row, addr});
run = true;

@ -14,7 +14,7 @@ class PingUpdater : public QObject
{
Q_OBJECT
public:
void addServer(int row, AddrPair addrPair);
void addServer(int row, const AddrPair &addrPair);
public slots:
void stop();
void process();

@ -19,8 +19,8 @@ QueryHelper::QueryHelper(QAbstractItemModel *model)
connect(queryThread, SIGNAL(started()), queryUpdate, SLOT(process()));
connect(queryUpdate, SIGNAL(finished()), queryThread, SLOT(quit()));
connect(queryUpdate, &QueryUpdate::finished, [this](){emit finished();});
connect(queryUpdate, SIGNAL(updateModel(QString, unsigned short, QueryData)),
this, SLOT(update(QString, unsigned short, QueryData)));
connect(queryUpdate, SIGNAL(updateModel(const QString&, unsigned short, const QueryData&)),
this, SLOT(update(const QString&, unsigned short, const QueryData&)));
queryUpdate->moveToThread(queryThread);
}
@ -39,7 +39,7 @@ void QueryHelper::terminate()
queryThread->terminate();
}
void QueryHelper::update(QString addr, unsigned short port, QueryData data)
void QueryHelper::update(const QString &addr, unsigned short port, const QueryData& data)
{
ServerModel *model = ((ServerModel*)_model);
model->insertRow(model->rowCount());
@ -80,7 +80,7 @@ void QueryUpdate::process()
return;
}
for (auto server : data)
for (const auto &server : data)
emit updateModel(server.first.ToString(false), server.first.GetPort(), server.second);
emit finished();
}

@ -23,7 +23,7 @@ public slots:
void refresh();
void terminate();
private slots:
void update(QString addr, unsigned short port, QueryData data);
void update(const QString &addr, unsigned short port, const QueryData& data);
signals:
void finished();
void started();
@ -38,7 +38,7 @@ class QueryUpdate : public QObject
Q_OBJECT
signals:
void finished();
void updateModel(QString addr, unsigned short port, QueryData data);
void updateModel(const QString &addr, unsigned short port, const QueryData& data);
public slots:
void process();
};

@ -8,6 +8,7 @@
#include "ServerInfoDialog.hpp"
#include <apps/browser/netutils/Utils.hpp>
#include <algorithm>
#include <utility>
using namespace std;
using namespace RakNet;
@ -18,12 +19,7 @@ ServerInfoDialog::ServerInfoDialog(QWidget *parent): QDialog(parent)
connect(btnRefresh, SIGNAL(clicked()), this, SLOT(refresh()));
}
ServerInfoDialog::~ServerInfoDialog()
{
}
void ServerInfoDialog::Server(QString addr)
void ServerInfoDialog::Server(const QString &addr)
{
this->addr = addr;
}
@ -42,7 +38,7 @@ bool ServerInfoDialog::refresh()
listPlayers->clear();
for (auto player : sd.second.players)
for (const auto &player : sd.second.players)
listPlayers->addItem(QString::fromStdString(player));
listPlugins->clear();

@ -11,9 +11,8 @@ class ServerInfoDialog : public QDialog, public Ui::Dialog
{
Q_OBJECT
public:
explicit ServerInfoDialog(QWidget *parent = 0);
virtual ~ServerInfoDialog();
void Server(QString addr);
explicit ServerInfoDialog(QWidget *parent = nullptr);
void Server(const QString &addr);
public slots:
bool refresh();
private:

@ -1,16 +1,11 @@
#include <qmessagebox.h>
#include "ServerModel.hpp"
#include <qdebug.h>
#include <apps/browser/netutils/Utils.hpp>
ServerModel::ServerModel(QObject *parent) : QAbstractTableModel(parent)
{
}
ServerModel::~ServerModel()
{
}
/*QHash<int, QByteArray> ServerModel::roleNames() const
{
return roles;
@ -49,7 +44,7 @@ QVariant ServerModel::data(const QModelIndex &index, int role) const
var = QString(sd.rules.at("name").str.c_str());
break;
case ServerData::PING:
var = sd.ping;
var = sd.ping == PING_UNREACHABLE ? QVariant("Unreachable") : sd.ping;
break;
case ServerData::MODNAME:
if (sd.rules.at("gamemode").str == "")

@ -29,8 +29,7 @@ class ServerModel: public QAbstractTableModel
{
Q_OBJECT
public:
explicit ServerModel(QObject *parent = 0);
~ServerModel();
explicit ServerModel(QObject *parent = nullptr);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_FINAL;
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_FINAL;
int columnCount(const QModelIndex &parent) const Q_DECL_FINAL;

@ -30,7 +30,7 @@ QueryClient::~QueryClient()
RakPeerInterface::DestroyInstance(peer);
}
void QueryClient::SetServer(std::string addr, unsigned short port)
void QueryClient::SetServer(const string &addr, unsigned short port)
{
masterAddr = SystemAddress(addr.c_str(), port);
}
@ -83,7 +83,7 @@ map<SystemAddress, QueryData> QueryClient::Query()
return query;
}
pair<SystemAddress, QueryData> QueryClient::Update(RakNet::SystemAddress addr)
pair<SystemAddress, QueryData> QueryClient::Update(const RakNet::SystemAddress &addr)
{
qDebug() << "Locking mutex in QueryClient::Update(RakNet::SystemAddress addr)";
pair<SystemAddress, QueryData> server;
@ -179,7 +179,7 @@ ConnectionState QueryClient::Connect()
{
ConnectionAttemptResult car = peer->Connect(masterAddr.ToString(false), masterAddr.GetPort(), TES3MP_MASTERSERVER_PASSW,
strlen(TES3MP_MASTERSERVER_PASSW), 0, 0, 5, 500);
strlen(TES3MP_MASTERSERVER_PASSW), nullptr, 0, 5, 500);
while (true)
{

@ -14,16 +14,16 @@
class QueryClient
{
private:
public:
QueryClient(QueryClient const &) = delete;
QueryClient(QueryClient &&) = delete;
QueryClient &operator=(QueryClient const &) = delete;
QueryClient &operator=(QueryClient &&) = delete;
public:
static QueryClient &Get();
void SetServer(std::string addr, unsigned short port);
void SetServer(const std::string &addr, unsigned short port);
std::map<RakNet::SystemAddress, QueryData> Query();
std::pair<RakNet::SystemAddress, QueryData> Update(RakNet::SystemAddress addr);
std::pair<RakNet::SystemAddress, QueryData> Update(const RakNet::SystemAddress &addr);
int Status();
private:
RakNet::ConnectionState Connect();

@ -44,11 +44,15 @@ unsigned int PingRakNetServer(const char *addr, unsigned short port)
break;
case ID_CONNECTED_PING:
case ID_UNCONNECTED_PONG:
{
RakNet::BitStream bsIn(&packet->data[1], packet->length, false);
bsIn.Read(time);
time = now - time;
done = true;
break;
}
default:
break;
}
peer->DeallocatePacket(packet);
}
@ -69,9 +73,9 @@ ServerExtendedData getExtendedData(const char *addr, unsigned short port)
sstr << TES3MP_VERSION;
sstr << TES3MP_PROTO_VERSION;
std::string msg = "";
std::string msg;
if (peer->Connect(addr, port, sstr.str().c_str(), (int)(sstr.str().size()), 0, 0, 3, 500, 0) != RakNet::CONNECTION_ATTEMPT_STARTED)
if (peer->Connect(addr, port, sstr.str().c_str(), (int)(sstr.str().size()), nullptr, 0, 3, 500, 0) != RakNet::CONNECTION_ATTEMPT_STARTED)
msg = "Connection attempt failed.\n";
@ -142,14 +146,14 @@ ServerExtendedData getExtendedData(const char *addr, unsigned short port)
{
RakNet::RakString str;
bs.Read(str);
data.players.push_back(str.C_String());
data.players.emplace_back(str.C_String());
}
bs.Read(length);
for (size_t i = 0; i < length; i++)
{
RakNet::RakString str;
bs.Read(str);
data.plugins.push_back(str.C_String());
data.plugins.emplace_back(str.C_String());
}
done = true;
}

@ -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();
}

@ -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)
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})
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)

@ -12,19 +12,54 @@
#include <components/openmw-mp/Master/PacketMasterUpdate.hpp>
#include <components/openmw-mp/Master/PacketMasterAnnounce.hpp>
#include <components/openmw-mp/Version.hpp>
#include <components/openmw-mp/Utils.hpp>
#include <boost/filesystem.hpp>
using namespace RakNet;
using namespace std;
using namespace mwmp;
using namespace chrono;
MasterServer::MasterServer(unsigned short maxConnections, unsigned short port)
MasterServer::MasterServer(const std::string &luaScript)
{
state.open_libraries();
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, 0);
peer->Startup(maxConnections, &sockdescr, 1, 1000);
sockdescr = SocketDescriptor(port.as<unsigned short>(), nullptr);
peer->Startup(maxConnections.as<unsigned short>(), &sockdescr, 1, 1000);
peer->SetLimitIPConnectionFrequency(true);
peer->SetMaximumIncomingConnections(maxConnections);
peer->SetMaximumIncomingConnections(maxConnections.as<unsigned short>());
peer->SetIncomingPassword(TES3MP_MASTERSERVER_PASSW, (int) strlen(TES3MP_MASTERSERVER_PASSW));
run = false;
}
@ -52,6 +87,13 @@ void MasterServer::Thread()
PacketMasterAnnounce pma(peer);
pma.SetSendStream(&send);
luaStuff([](sol::state &state) {
sol::protected_function func = state["OnInit"];
sol::protected_function_result result = func.call();
if (!result.valid())
cerr << "Error: " << result.get<string>() << endl;
});
while (run)
{
Packet *packet = peer->Receive();
@ -67,9 +109,9 @@ void MasterServer::Thread()
servers.erase(it++);
else ++it;
}
for(auto id = pendingACKs.begin(); id != pendingACKs.end();)
for (auto id = pendingACKs.begin(); id != pendingACKs.end();)
{
if(now - id->second >= 30s)
if (now - id->second >= 30s)
{
cout << "timeout: " << peer->GetSystemAddressFromGuid(id->first).ToString() << endl;
peer->CloseConnection(id->first, true);
@ -113,7 +155,7 @@ void MasterServer::Thread()
SystemAddress addr;
data.Read(addr); // update 1 server
ServerIter it = servers.find(addr);
auto it = servers.find(addr);
if (it != servers.end())
{
pair<SystemAddress, QueryData> pairPtr(it->first, static_cast<QueryData>(it->second));
@ -127,7 +169,7 @@ void MasterServer::Thread()
}
case ID_MASTER_ANNOUNCE:
{
ServerIter iter = servers.find(packet->systemAddress);
auto iter = servers.find(packet->systemAddress);
pma.SetReadStream(&data);
SServer server;
@ -141,6 +183,26 @@ void MasterServer::Thread()
pendingACKs[packet->guid] = steady_clock::now();
};
auto isServerValid = [&](const SServer &sserver) {
bool ret = false;
auto addr = packet->systemAddress.ToString(false);
lock_guard<mutex> lock(banMutex);
if (peer->IsBanned(addr)) // check if address is banned
return false;
luaStuff([&ret, &packet, &sserver, &addr](sol::state &state) {
sol::protected_function func = state["OnServerAnnounce"];
sol::protected_function_result result = func.call(addr, sserver);
if (result.valid())
ret = result.get<bool>();
else
cerr << "Error: " << result.get<string>() << endl;
});
return ret;
};
if (iter != servers.end())
{
if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_DELETE)
@ -152,9 +214,19 @@ void MasterServer::Thread()
}
else if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_ANNOUNCE)
{
cout << "Updated";
iter->second = server;
keepAliveFunc();
if (isServerValid(server))
{
cout << "Updated";
iter->second = server;
keepAliveFunc();
}
else
{
cout << "Update rejected";
servers.erase(iter);
pendingACKs.erase(packet->guid);
}
}
else
{
@ -164,9 +236,14 @@ void MasterServer::Thread()
}
else if (pma.GetFunc() == PacketMasterAnnounce::FUNCTION_ANNOUNCE)
{
cout << "Added";
iter = servers.insert({packet->systemAddress, server}).first;
keepAliveFunc();
if (isServerValid(server))
{
cout << "Added";
iter = servers.insert({packet->systemAddress, server}).first;
keepAliveFunc();
}
else
cout << "Adding rejected";
}
else
{
@ -186,7 +263,8 @@ void MasterServer::Thread()
peer->CloseConnection(packet->systemAddress, true);
break;
default:
cout << "Wrong packet. id " << (unsigned) packet->data[0] << " packet length " << packet->length << " from " << packet->systemAddress.ToString() << endl;
cout << "Wrong packet. id " << (unsigned) packet->data[0] << " packet length "
<< packet->length << " from " << packet->systemAddress.ToString() << endl;
peer->CloseConnection(packet->systemAddress, true);
}
}
@ -234,3 +312,22 @@ MasterServer::ServerMap *MasterServer::GetServers()
{
return &servers;
}
void MasterServer::luaStuff(std::function<void(sol::state &)> f)
{
lock_guard<mutex> lock(luaMutex);
f(state);
}
void MasterServer::ban(const std::string &addr)
{
lock_guard<mutex> lock(banMutex);
peer->AddToBanList(addr.c_str());
}
void MasterServer::unban(const std::string &addr)
{
lock_guard<mutex> lock(banMutex);
peer->RemoveFromBanList(addr.c_str());
}

@ -9,18 +9,12 @@
#include <chrono>
#include <RakPeerInterface.h>
#include <components/openmw-mp/Master/MasterData.hpp>
#include <extern/sol/sol.hpp>
#include <mutex>
class MasterServer
{
public:
struct Ban
{
RakNet::SystemAddress sa;
bool permanent;
struct Date
{
} date;
};
struct SServer : QueryData
{
std::chrono::steady_clock::time_point lastUpdate;
@ -29,7 +23,7 @@ public:
//typedef ServerMap::const_iterator ServerCIter;
typedef ServerMap::iterator ServerIter;
MasterServer(unsigned short maxConnections, unsigned short port);
explicit MasterServer(const std::string &luaScript);
~MasterServer();
void Start();
@ -38,6 +32,10 @@ public:
void Wait();
ServerMap* GetServers();
void luaStuff(std::function<void(sol::state &)> f);
void ban(const std::string &addr);
void unban(const std::string &addr);
private:
void Thread();
@ -49,6 +47,9 @@ private:
ServerMap servers;
bool run;
std::map<RakNet::RakNetGUID, std::chrono::steady_clock::time_point> pendingACKs;
sol::state state;
std::mutex luaMutex;
std::mutex banMutex;
};

@ -7,22 +7,12 @@
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include "RestUtils.hpp"
using namespace std;
using namespace chrono;
using namespace boost::property_tree;
static string response201 = "HTTP/1.1 201 Created\r\nContent-Length: 7\r\n\r\nCreated";
static string response202 = "HTTP/1.1 202 Accepted\r\nContent-Length: 8\r\n\r\nAccepted";
static string response400 = "HTTP/1.1 400 Bad Request\r\nContent-Length: 11\r\n\r\nbad request";
inline void ResponseStr(HttpServer::Response &response, string content, string type = "", string code = "200 OK")
{
response << "HTTP/1.1 " << code << "\r\n";
if (!type.empty())
response << "Content-Type: " << type <<"\r\n";
response << "Content-Length: " << content.length() << "\r\n\r\n" << content;
}
inline void ptreeToServer(boost::property_tree::ptree &pt, MasterServer::SServer &server)
{
server.SetName(pt.get<string>("hostname").c_str());
@ -70,7 +60,7 @@ void RestServer::start()
auto port = (unsigned short)stoi(&(addr[addr.find(':')+1]));
queryToStringStream(ss, "server", serverMap->at(RakNet::SystemAddress(addr.c_str(), port)));
ss << "}";
ResponseStr(*response, ss.str(), "application/json");
ResponseStr<SimpleWeb::HTTP>(response, ss.str(), "application/json");
}
catch(out_of_range e)
{
@ -93,7 +83,7 @@ void RestServer::start()
ss << ", ";
}
ss << "}}";
ResponseStr(*response, ss.str(), "application/json");
ResponseStr<SimpleWeb::HTTP>(response, ss.str(), "application/json");
updatedCache = false;
}
*response << str;
@ -110,7 +100,7 @@ void RestServer::start()
MasterServer::SServer server;
ptreeToServer(pt, server);
unsigned short port = pt.get<unsigned short>("port");
auto port = pt.get<unsigned short>("port");
server.lastUpdate = steady_clock::now();
serverMap->insert({RakNet::SystemAddress(request->remote_endpoint_address.c_str(), port), server});
updatedCache = true;
@ -137,7 +127,6 @@ void RestServer::start()
*response << response400;
return;
}
if (request->content.size() != 0)
{
try
@ -171,14 +160,14 @@ void RestServer::start()
ss << ", \"players\": " << players;
ss << "}";
ResponseStr(*response, ss.str(), "application/json");
ResponseStr<SimpleWeb::HTTP>(response, ss.str(), "application/json");
};
httpServer.default_resource["GET"]=[](auto response, auto /*request*/) {
*response << response400;
};
httpServer.start();
thr = thread([this](){httpServer.start();});
}
void RestServer::cacheUpdated()
@ -189,4 +178,6 @@ void RestServer::cacheUpdated()
void RestServer::stop()
{
httpServer.stop();
if(thr.joinable())
thr.join();
}

@ -24,6 +24,7 @@ private:
HttpServer httpServer;
MasterServer::ServerMap *serverMap;
bool updatedCache = true;
std::thread thr;
};

@ -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;
}

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014-2016 Ole Christian Eidheim
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -1,72 +1,137 @@
#ifndef BASE_SERVER_HPP
#define BASE_SERVER_HPP
#pragma once
#include <boost/asio.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/functional/hash.hpp>
#include <map>
#include <unordered_map>
#include <thread>
#include "utility.hpp"
#include <condition_variable>
#include <functional>
#include <iostream>
#include <map>
#include <sstream>
#include <thread>
#include <unordered_set>
#include <regex>
#ifndef CASE_INSENSITIVE_EQUALS_AND_HASH
#define CASE_INSENSITIVE_EQUALS_AND_HASH
//Based on http://www.boost.org/doc/libs/1_60_0/doc/html/unordered/hash_equality.html
struct case_insensitive_equals
{
bool operator()(const std::string &key1, const std::string &key2) const
{
return boost::algorithm::iequals(key1, key2);
}
};
struct case_insensitive_hash
{
size_t operator()(const std::string &key) const
{
std::size_t seed = 0;
for (auto &c: key)
boost::hash_combine(seed, std::tolower(c));
return seed;
}
};
#ifdef USE_STANDALONE_ASIO
#include <asio.hpp>
#include <asio/steady_timer.hpp>
namespace SimpleWeb {
using error_code = std::error_code;
using errc = std::errc;
namespace make_error_code = std;
} // namespace SimpleWeb
#else
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
namespace SimpleWeb {
namespace asio = boost::asio;
using error_code = boost::system::error_code;
namespace errc = boost::system::errc;
namespace make_error_code = boost::system::errc;
} // namespace SimpleWeb
#endif
namespace SimpleWeb
{
template<class socket_type>
namespace SimpleWeb {
template <class socket_type>
class Server;
template<class socket_type>
class ServerBase
{
public:
virtual ~ServerBase()
{}
template <class socket_type>
class ServerBase {
protected:
class Session;
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 Server<socket_type>;
asio::streambuf streambuf;
boost::asio::streambuf streambuf;
std::shared_ptr<Session> session;
long timeout_content;
std::shared_ptr<socket_type> socket;
Response(std::shared_ptr<Session> session, long timeout_content) noexcept : std::ostream(&streambuf), session(std::move(session)), timeout_content(timeout_content) {}
Response(const std::shared_ptr<socket_type> &socket) : std::ostream(&streambuf), socket(socket)
{}
template <typename size_type>
void write_header(const CaseInsensitiveMultimap &header, size_type size) {
bool content_length_written = false;
bool chunked_transfer_encoding = false;
for(auto &field : header) {
if(!content_length_written && case_insensitive_equal(field.first, "content-length"))
content_length_written = true;
else if(!chunked_transfer_encoding && case_insensitive_equal(field.first, "transfer-encoding") && case_insensitive_equal(field.second, "chunked"))
chunked_transfer_encoding = true;
*this << field.first << ": " << field.second << "\r\n";
}
if(!content_length_written && !chunked_transfer_encoding && !close_connection_after_response)
*this << "Content-Length: " << size << "\r\n\r\n";
else
*this << "\r\n";
}
public:
size_t size()
{
size_t size() noexcept {
return streambuf.size();
}
/// Use this function if you need to recursively send parts of a longer message
void send(const std::function<void(const error_code &)> &callback = nullptr) noexcept {
session->connection->set_timeout(timeout_content);
auto self = this->shared_from_this(); // Keep Response instance alive through the following async_write
asio::async_write(*session->connection->socket, streambuf, [self, callback](const error_code &ec, size_t /*bytes_transferred*/) {
self->session->connection->cancel_timeout();
auto lock = self->session->connection->handler_runner->continue_lock();
if(!lock)
return;
if(callback)
callback(ec);
});
}
/// Write directly to stream buffer using std::ostream::write
void write(const char_type *ptr, std::streamsize n) {
std::ostream::write(ptr, n);
}
/// Convenience function for writing status line, potential header fields, and empty content
void write(StatusCode status_code = StatusCode::success_ok, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) {
*this << "HTTP/1.1 " << SimpleWeb::status_code(status_code) << "\r\n";
write_header(header, 0);
}
/// Convenience function for writing status line, header fields, and content
void write(StatusCode status_code, const std::string &content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) {
*this << "HTTP/1.1 " << SimpleWeb::status_code(status_code) << "\r\n";
write_header(header, content.size());
if(!content.empty())
*this << content;
}
/// Convenience function for writing status line, header fields, and content
void write(StatusCode status_code, std::istream &content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) {
*this << "HTTP/1.1 " << SimpleWeb::status_code(status_code) << "\r\n";
content.seekg(0, std::ios::end);
auto size = content.tellg();
content.seekg(0, std::ios::beg);
write_header(header, size);
if(size)
*this << content.rdbuf();
}
/// Convenience function for writing success status line, header fields, and content
void write(const std::string &content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) {
write(StatusCode::success_ok, content, header);
}
/// Convenience function for writing success status line, header fields, and content
void write(std::istream &content, const CaseInsensitiveMultimap &header = CaseInsensitiveMultimap()) {
write(StatusCode::success_ok, content, header);
}
/// Convenience function for writing success status line, and header fields
void write(const CaseInsensitiveMultimap &header) {
write(StatusCode::success_ok, std::string(), header);
}
/// If true, force server to close the connection after the response have been sent.
///
/// This is useful when implementing a HTTP/1.0-server sending content
@ -74,438 +139,399 @@ namespace SimpleWeb
bool close_connection_after_response = false;
};
class Content : public std::istream
{
class Content : public std::istream {
friend class ServerBase<socket_type>;
public:
size_t size()
{
size_t size() noexcept {
return streambuf.size();
}
std::string string()
{
std::stringstream ss;
ss << rdbuf();
return ss.str();
/// Convenience function to return std::string. The stream buffer is consumed.
std::string string() noexcept {
try {
std::stringstream ss;
ss << rdbuf();
return ss.str();
}
catch(...) {
return std::string();
}
}
private:
boost::asio::streambuf &streambuf;
Content(boost::asio::streambuf &streambuf) : std::istream(&streambuf), streambuf(streambuf)
{}
asio::streambuf &streambuf;
Content(asio::streambuf &streambuf) noexcept : std::istream(&streambuf), streambuf(streambuf) {}
};
class Request
{
class Request {
friend class ServerBase<socket_type>;
friend class Server<socket_type>;
friend class Session;
public:
std::string method, path, http_version;
std::string method, path, query_string, http_version;
Content content;
std::unordered_multimap<std::string, std::string, case_insensitive_hash, case_insensitive_equals> header;
CaseInsensitiveMultimap header;
std::smatch path_match;
std::string remote_endpoint_address;
unsigned short remote_endpoint_port;
/// Returns query keys with percent-decoded values.
CaseInsensitiveMultimap parse_query_string() noexcept {
return SimpleWeb::QueryString::parse(query_string);
}
private:
Request(const socket_type &socket) : content(streambuf)
{
try
{
remote_endpoint_address = socket.lowest_layer().remote_endpoint().address().to_string();
remote_endpoint_port = socket.lowest_layer().remote_endpoint().port();
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) {}
};
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());
}
catch (...)
{}
}
boost::asio::streambuf streambuf;
std::shared_ptr<Connection> connection;
std::shared_ptr<Request> request;
};
class Config
{
public:
class Config {
friend class ServerBase<socket_type>;
Config(unsigned short port) : port(port)
{}
Config(unsigned short port) noexcept : port(port) {}
public:
/// Port number to use. Defaults to 80 for HTTP and 443 for HTTPS.
unsigned short port;
/// Number of threads that the server will use when start() is called. Defaults to 1 thread.
/// If io_service is not set, number of threads that the server will use when start() is called.
/// Defaults to 1 thread.
size_t thread_pool_size = 1;
/// Timeout on request handling. Defaults to 5 seconds.
size_t timeout_request = 5;
long timeout_request = 5;
/// Timeout on content handling. Defaults to 300 seconds.
size_t timeout_content = 300;
long timeout_content = 300;
/// IPv4 address in dotted decimal form or IPv6 address in hexadecimal notation.
/// If empty, the address will be any address.
std::string address;
/// Set to false to avoid binding the socket to an address that is already in use. Defaults to true.
bool reuse_address = true;
};
///Set before calling start().
/// Set before calling start().
Config config;
private:
class regex_orderable : public std::regex
{
class regex_orderable : public std::regex {
std::string str;
public:
regex_orderable(const char *regex_cstr) : std::regex(regex_cstr), str(regex_cstr)
{}
regex_orderable(const std::string &regex_str) : std::regex(regex_str), str(regex_str)
{}
bool operator<(const regex_orderable &rhs) const
{
public:
regex_orderable(const char *regex_cstr) : std::regex(regex_cstr), str(regex_cstr) {}
regex_orderable(std::string regex_str) : std::regex(regex_str), str(std::move(regex_str)) {}
bool operator<(const regex_orderable &rhs) const noexcept {
return str < rhs.str;
}
};
public:
/// Warning: do not add or remove resources after start() is called
std::map<regex_orderable, std::map<std::string,
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>,
std::shared_ptr<typename ServerBase<socket_type>::Request>)>>>
resource;
std::map<regex_orderable, std::map<std::string, std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>, std::shared_ptr<typename ServerBase<socket_type>::Request>)>>> resource;
std::map<std::string, std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>, std::shared_ptr<typename ServerBase<socket_type>::Request>)>> default_resource;
std::map<std::string,
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>,
std::shared_ptr<typename ServerBase<socket_type>::Request>)>> default_resource;
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Request>, const error_code &)> on_error;
std::function<
void(std::shared_ptr<typename ServerBase<socket_type>::Request>,
const boost::system::error_code &)>
on_error;
std::function<void(std::unique_ptr<socket_type> &, std::shared_ptr<typename ServerBase<socket_type>::Request>)> on_upgrade;
std::function<void(std::shared_ptr<socket_type> socket,
std::shared_ptr<typename ServerBase<socket_type>::Request>)> on_upgrade;
/// If you have your own asio::io_service, store its pointer here before running start().
std::shared_ptr<asio::io_service> io_service;
virtual void start()
{
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();
boost::asio::ip::tcp::endpoint endpoint;
if (config.address.size() > 0)
endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(config.address),
config.port);
asio::ip::tcp::endpoint endpoint;
if(config.address.size() > 0)
endpoint = asio::ip::tcp::endpoint(asio::ip::address::from_string(config.address), config.port);
else
endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), config.port);
endpoint = asio::ip::tcp::endpoint(asio::ip::tcp::v4(), config.port);
if (!acceptor)
acceptor = std::unique_ptr<boost::asio::ip::tcp::acceptor>(
new boost::asio::ip::tcp::acceptor(*io_service));
if(!acceptor)
acceptor = std::unique_ptr<asio::ip::tcp::acceptor>(new asio::ip::tcp::acceptor(*io_service));
acceptor->open(endpoint.protocol());
acceptor->set_option(boost::asio::socket_base::reuse_address(config.reuse_address));
acceptor->set_option(asio::socket_base::reuse_address(config.reuse_address));
acceptor->bind(endpoint);
acceptor->listen();
accept();
//If thread_pool_size>1, start m_io_service.run() in (thread_pool_size-1) threads for thread-pooling
threads.clear();
for (size_t c = 1; c < config.thread_pool_size; c++)
{
threads.emplace_back([this]()
{
io_service->run();
});
}
if(internal_io_service) {
// If thread_pool_size>1, start m_io_service.run() in (thread_pool_size-1) threads for thread-pooling
threads.clear();
for(size_t c = 1; c < config.thread_pool_size; c++) {
threads.emplace_back([this]() {
this->io_service->run();
});
}
//Main thread
if (config.thread_pool_size > 0)
io_service->run();
// Main thread
if(config.thread_pool_size > 0)
io_service->run();
//Wait for the rest of the threads, if any, to finish as well
for (auto &t: threads)
{
t.join();
// Wait for the rest of the threads, if any, to finish as well
for(auto &t : threads)
t.join();
}
}
void stop()
{
acceptor->close();
if (config.thread_pool_size > 0)
io_service->stop();
/// Stop accepting new requests, and close current connections.
void stop() noexcept {
if(acceptor) {
error_code ec;
acceptor->close(ec);
{
std::unique_lock<std::mutex> lock(*connections_mutex);
for(auto &connection : *connections)
connection->close();
connections->clear();
}
if(internal_io_service)
io_service->stop();
}
}
///Use this function if you need to recursively send parts of a longer message
void send(const std::shared_ptr<Response> &response,
const std::function<void(const boost::system::error_code &)> &callback = nullptr) const
{
boost::asio::async_write(*response->socket, response->streambuf, [this, response, callback]
(const boost::system::error_code &ec, size_t /*bytes_transferred*/)
{
if (callback)
callback(ec);
});
virtual ~ServerBase() noexcept {
handler_runner->stop();
stop();
}
/// If you have your own boost::asio::io_service, store its pointer here before running start().
/// You might also want to set config.thread_pool_size to 0.
std::shared_ptr<boost::asio::io_service> io_service;
protected:
std::unique_ptr<boost::asio::ip::tcp::acceptor> acceptor;
bool internal_io_service = false;
std::unique_ptr<asio::ip::tcp::acceptor> acceptor;
std::vector<std::thread> threads;
ServerBase(unsigned short port) : config(port)
{}
virtual void accept()=0;
std::shared_ptr<boost::asio::deadline_timer>
get_timeout_timer(const std::shared_ptr<socket_type> &socket, long seconds)
{
if (seconds == 0)
return nullptr;
auto timer = std::make_shared<boost::asio::deadline_timer>(*io_service);
timer->expires_from_now(boost::posix_time::seconds(seconds));
timer->async_wait([socket](const boost::system::error_code &ec)
{
if (!ec)
{
boost::system::error_code ec;
socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
socket->lowest_layer().close();
}
});
return timer;
}
std::shared_ptr<std::unordered_set<Connection *>> connections;
std::shared_ptr<std::mutex> connections_mutex;
void read_request_and_content(const std::shared_ptr<socket_type> &socket)
{
//Create new streambuf (Request::streambuf) for async_read_until()
//shared_ptr is used to pass temporary objects to the asynchronous functions
std::shared_ptr<Request> request(new Request(*socket));
std::shared_ptr<ScopeRunner> handler_runner;
//Set timeout on the following boost::asio::async-read or write function
auto timer = this->get_timeout_timer(socket, config.timeout_request);
ServerBase(unsigned short port) noexcept : config(port), connections(new std::unordered_set<Connection *>()), connections_mutex(new std::mutex()), handler_runner(new ScopeRunner()) {}
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)
virtual void accept() = 0;
template <typename... Args>
std::shared_ptr<Connection> create_connection(Args &&... args) noexcept {
auto connections = this->connections;
auto connections_mutex = this->connections_mutex;
auto connection = std::shared_ptr<Connection>(new Connection(handler_runner, std::forward<Args>(args)...), [connections, connections_mutex](Connection *connection) {
{
//request->streambuf.size() is not necessarily the same as bytes_transferred, from Boost-docs:
//"After a successful async_read_until operation, the streambuf may contain additional data beyond the delimiter"
//The chosen solution is to extract lines from the stream directly when parsing the header. What is left of the
//streambuf (maybe some bytes of the content) is appended to in the async_read-function below (for retrieving content).
size_t num_additional_bytes =
request->streambuf.size() - bytes_transferred;
if (!this->parse_request(request))
std::unique_lock<std::mutex> lock(*connections_mutex);
auto it = connections->find(connection);
if(it != connections->end())
connections->erase(it);
}
delete connection;
});
{
std::unique_lock<std::mutex> lock(*connections_mutex);
connections->emplace(connection.get());
}
return connection;
}
void read_request_and_content(const std::shared_ptr<Session> &session) {
session->connection->set_timeout(config.timeout_request);
asio::async_read_until(*session->connection->socket, session->request->streambuf, "\r\n\r\n", [this, session](const error_code &ec, size_t bytes_transferred) {
session->connection->cancel_timeout();
auto lock = session->connection->handler_runner->continue_lock();
if(!lock)
return;
if(!ec) {
// request->streambuf.size() is not necessarily the same as bytes_transferred, from Boost-docs:
// "After a successful async_read_until operation, the streambuf may contain additional data beyond the delimiter"
// The chosen solution is to extract lines from the stream directly when parsing the header. What is left of the
// streambuf (maybe some bytes of the content) is appended to in the async_read-function below (for retrieving content).
size_t num_additional_bytes = session->request->streambuf.size() - bytes_transferred;
if(!RequestMessage::parse(session->request->content, session->request->method, session->request->path,
session->request->query_string, session->request->http_version, session->request->header)) {
if(this->on_error)
this->on_error(session->request, make_error_code::make_error_code(errc::protocol_error));
return;
}
//If content, read that as well
auto it = request->header.find("Content-Length");
if (it != request->header.end())
{
unsigned long long content_length;
try
{
// If content, read that as well
auto it = session->request->header.find("Content-Length");
if(it != session->request->header.end()) {
unsigned long long content_length = 0;
try {
content_length = stoull(it->second);
}
catch (const std::exception &e)
{
if (on_error)
on_error(request, boost::system::error_code(
boost::system::errc::protocol_error,
boost::system::generic_category()));
catch(const std::exception &e) {
if(this->on_error)
this->on_error(session->request, make_error_code::make_error_code(errc::protocol_error));
return;
}
if (content_length > num_additional_bytes)
{
//Set timeout on the following boost::asio::async-read or write function
auto timer = this->get_timeout_timer(socket,
config.timeout_content);
boost::asio::async_read(*socket, request->streambuf,
boost::asio::transfer_exactly(
content_length -
num_additional_bytes),
[this, socket, request, timer]
(const boost::system::error_code &ec,
size_t /*bytes_transferred*/)
{
if (timer)
timer->cancel();
if (!ec)
this->find_resource(socket,
request);
else if (on_error)
on_error(request, ec);
});
if(content_length > num_additional_bytes) {
session->connection->set_timeout(config.timeout_content);
asio::async_read(*session->connection->socket, session->request->streambuf, asio::transfer_exactly(content_length - num_additional_bytes), [this, session](const error_code &ec, size_t /*bytes_transferred*/) {
session->connection->cancel_timeout();
auto lock = session->connection->handler_runner->continue_lock();
if(!lock)
return;
if(!ec)
this->find_resource(session);
else if(this->on_error)
this->on_error(session->request, ec);
});
}
else
this->find_resource(socket, request);
this->find_resource(session);
}
else
this->find_resource(socket, request);
this->find_resource(session);
}
else if (on_error)
on_error(request, ec);
else if(this->on_error)
this->on_error(session->request, ec);
});
}
bool parse_request(const std::shared_ptr<Request> &request) const
{
std::string line;
getline(request->content, line);
size_t method_end;
if ((method_end = line.find(' ')) != std::string::npos)
{
size_t path_end;
if ((path_end = line.find(' ', method_end + 1)) != std::string::npos)
{
request->method = line.substr(0, method_end);
request->path = line.substr(method_end + 1, path_end - method_end - 1);
size_t protocol_end;
if ((protocol_end = line.find('/', path_end + 1)) != std::string::npos)
void find_resource(const std::shared_ptr<Session> &session) {
// Upgrade connection
if(on_upgrade) {
auto it = session->request->header.find("Upgrade");
if(it != session->request->header.end()) {
// remove connection from connections
{
if (line.compare(path_end + 1, protocol_end - path_end - 1, "HTTP") != 0)
return false;
request->http_version = line.substr(protocol_end + 1, line.size() - protocol_end - 2);
std::unique_lock<std::mutex> lock(*connections_mutex);
auto it = connections->find(session->connection.get());
if(it != connections->end())
connections->erase(it);
}
else
return false;
getline(request->content, line);
size_t param_end;
while ((param_end = line.find(':')) != std::string::npos)
{
size_t value_start = param_end + 1;
if ((value_start) < line.size())
{
if (line[value_start] == ' ')
value_start++;
if (value_start < line.size())
request->header.emplace(line.substr(0, param_end),
line.substr(value_start, line.size() - value_start - 1));
}
getline(request->content, line);
}
}
else
return false;
}
else
return false;
return true;
}
void find_resource(const std::shared_ptr<socket_type> &socket, const std::shared_ptr<Request> &request)
{
//Upgrade connection
if (on_upgrade)
{
auto it = request->header.find("Upgrade");
if (it != request->header.end())
{
on_upgrade(socket, request);
on_upgrade(session->connection->socket, session->request);
return;
}
}
//Find path- and method-match, and call write_response
for (auto &regex_method: resource)
{
auto it = regex_method.second.find(request->method);
if (it != regex_method.second.end())
{
// Find path- and method-match, and call write_response
for(auto &regex_method : resource) {
auto it = regex_method.second.find(session->request->method);
if(it != regex_method.second.end()) {
std::smatch sm_res;
if (std::regex_match(request->path, sm_res, regex_method.first))
{
request->path_match = std::move(sm_res);
write_response(socket, request, it->second);
if(std::regex_match(session->request->path, sm_res, regex_method.first)) {
session->request->path_match = std::move(sm_res);
write_response(session, it->second);
return;
}
}
}
auto it = default_resource.find(request->method);
if (it != default_resource.end())
{
write_response(socket, request, it->second);
}
auto it = default_resource.find(session->request->method);
if(it != default_resource.end())
write_response(session, it->second);
}
void write_response(const std::shared_ptr<socket_type> &socket, const std::shared_ptr<Request> &request,
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>,
std::shared_ptr<
typename ServerBase<socket_type>::Request>)> &resource_function)
{
//Set timeout on the following boost::asio::async-read or write function
auto timer = this->get_timeout_timer(socket, config.timeout_content);
auto response = std::shared_ptr<Response>(new Response(socket), [this, request, timer]
(Response *response_ptr)
{
void write_response(const std::shared_ptr<Session> &session,
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>, std::shared_ptr<typename ServerBase<socket_type>::Request>)> &resource_function) {
session->connection->set_timeout(config.timeout_content);
auto response = std::shared_ptr<Response>(new Response(session, config.timeout_content), [this](Response *response_ptr) {
auto response = std::shared_ptr<Response>(response_ptr);
this->send(response, [this, response, request, timer](
const boost::system::error_code &ec)
{
if (timer)
timer->cancel();
if (!ec)
{
if (response->close_connection_after_response)
response->send([this, response](const error_code &ec) {
if(!ec) {
if(response->close_connection_after_response)
return;
auto range = request->header.equal_range(
"Connection");
for (auto it = range.first; it != range.second; it++)
{
if (boost::iequals(it->second, "close"))
{
auto range = response->session->request->header.equal_range("Connection");
for(auto it = range.first; it != range.second; it++) {
if(case_insensitive_equal(it->second, "close"))
return;
}
else if (boost::iequals(it->second, "keep-alive"))
{
this->read_request_and_content(
response->socket);
else if(case_insensitive_equal(it->second, "keep-alive")) {
auto new_session = std::make_shared<Session>(response->session->connection);
this->read_request_and_content(new_session);
return;
}
}
if (request->http_version >= "1.1")
this->read_request_and_content(response->socket);
if(response->session->request->http_version >= "1.1") {
auto new_session = std::make_shared<Session>(response->session->connection);
this->read_request_and_content(new_session);
return;
}
}
else if (on_error)
on_error(request, ec);
else if(this->on_error)
this->on_error(response->session->request, ec);
});
});
try
{
resource_function(response, request);
try {
resource_function(response, session->request);
}
catch (const std::exception &e)
{
if (on_error)
on_error(request, boost::system::error_code(boost::system::errc::operation_canceled,
boost::system::generic_category()));
catch(const std::exception &e) {
if(on_error)
on_error(session->request, make_error_code::make_error_code(errc::operation_canceled));
return;
}
}
};
}
#endif //BASE_SERVER_HPP
}

@ -1,55 +1,42 @@
/*
* https://github.com/eidheim/Simple-Web-Server/
*
* The MIT License (MIT)
* Copyright (c) 2014-2016 Ole Christian Eidheim
*/
#ifndef SERVER_HTTP_HPP
#define SERVER_HTTP_HPP
#pragma once
#include "base_server.hpp"
namespace SimpleWeb
{
namespace SimpleWeb {
template<class socket_type>
template <class socket_type>
class Server : public ServerBase<socket_type> {};
typedef boost::asio::ip::tcp::socket HTTP;
using HTTP = asio::ip::tcp::socket;
template<>
class Server<HTTP> : public ServerBase<HTTP>
{
template <>
class Server<HTTP> : public ServerBase<HTTP> {
public:
Server() : ServerBase<HTTP>::ServerBase(80)
{}
Server() noexcept : ServerBase<HTTP>::ServerBase(80) {}
protected:
virtual void accept()
{
//Create new socket for this connection
//Shared_ptr is used to pass temporary objects to the asynchronous functions
auto socket = std::make_shared<HTTP>(*io_service);
acceptor->async_accept(*socket, [this, socket](const boost::system::error_code &ec)
{
//Immediately start accepting a new connection (if io_service hasn't been stopped)
if (ec != boost::asio::error::operation_aborted)
accept();
if (!ec)
{
boost::asio::ip::tcp::no_delay option(true);
socket->set_option(option);
this->read_request_and_content(socket);
void accept() override {
auto session = std::make_shared<Session>(create_connection(*io_service));
acceptor->async_accept(*session->connection->socket, [this, session](const error_code &ec) {
auto lock = session->connection->handler_runner->continue_lock();
if(!lock)
return;
// Immediately start accepting a new connection (unless io_service has been stopped)
if(ec != asio::error::operation_aborted)
this->accept();
if(!ec) {
asio::ip::tcp::no_delay option(true);
error_code ec;
session->connection->socket->set_option(option, ec);
this->read_request_and_content(session);
}
else if (on_error)
on_error(std::shared_ptr<Request>(new Request(*socket)), ec);
else if(this->on_error)
this->on_error(session->request, ec);
});
}
};
}
#endif //SERVER_HTTP_HPP
} // namespace SimpleWeb

@ -1,91 +1,82 @@
#ifndef HTTPS_SERVER_HPP
#define HTTPS_SERVER_HPP
#pragma once
#include "base_server.hpp"
#ifdef USE_STANDALONE_ASIO
#include <asio/ssl.hpp>
#else
#include <boost/asio/ssl.hpp>
#include <openssl/ssl.h>
#endif
#include <algorithm>
#include <openssl/ssl.h>
namespace SimpleWeb
{
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> HTTPS;
namespace SimpleWeb {
using HTTPS = asio::ssl::stream<asio::ip::tcp::socket>;
template<>
class Server<HTTPS> : public ServerBase<HTTPS>
{
template <>
class Server<HTTPS> : public ServerBase<HTTPS> {
std::string session_id_context;
bool set_session_id_context = false;
public:
Server(const std::string &cert_file, const std::string &private_key_file,
const std::string &verify_file = std::string()) : ServerBase<HTTPS>::ServerBase(443),
context(boost::asio::ssl::context::tlsv12)
{
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, boost::asio::ssl::context::pem);
context.use_private_key_file(private_key_file, asio::ssl::context::pem);
if (verify_file.size() > 0)
{
if(verify_file.size() > 0) {
context.load_verify_file(verify_file);
context.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert |
boost::asio::ssl::verify_client_once);
context.set_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert | asio::ssl::verify_client_once);
set_session_id_context = true;
}
}
void start()
{
if (set_session_id_context)
{
void start() override {
if(set_session_id_context) {
// Creating session_id_context from address:port but reversed due to small SSL_MAX_SSL_SESSION_ID_LENGTH
session_id_context = std::to_string(config.port) + ':';
session_id_context.append(config.address.rbegin(), config.address.rend());
SSL_CTX_set_session_id_context(context.native_handle(),
reinterpret_cast<const unsigned char *>(session_id_context.data()),
std::min<size_t>(session_id_context.size(),
SSL_MAX_SSL_SESSION_ID_LENGTH));
SSL_CTX_set_session_id_context(context.native_handle(), reinterpret_cast<const unsigned char *>(session_id_context.data()),
std::min<size_t>(session_id_context.size(), SSL_MAX_SSL_SESSION_ID_LENGTH));
}
ServerBase::start();
}
protected:
boost::asio::ssl::context context;
asio::ssl::context context;
virtual void accept()
{
//Create new socket for this connection
//Shared_ptr is used to pass temporary objects to the asynchronous functions
auto socket = std::make_shared<HTTPS>(*io_service, context);
void accept() override {
auto session = std::make_shared<Session>(create_connection(*io_service, context));
acceptor->async_accept((*socket).lowest_layer(), [this, socket](const boost::system::error_code &ec)
{
//Immediately start accepting a new connection (if io_service hasn't been stopped)
if (ec != boost::asio::error::operation_aborted)
accept();
acceptor->async_accept(session->connection->socket->lowest_layer(), [this, session](const error_code &ec) {
auto lock = session->connection->handler_runner->continue_lock();
if(!lock)
return;
if(ec != asio::error::operation_aborted)
this->accept();
if (!ec)
{
boost::asio::ip::tcp::no_delay option(true);
socket->lowest_layer().set_option(option);
if(!ec) {
asio::ip::tcp::no_delay option(true);
error_code ec;
session->connection->socket->lowest_layer().set_option(option, ec);
//Set timeout on the following boost::asio::ssl::stream::async_handshake
auto timer = get_timeout_timer(socket, config.timeout_request);
socket->async_handshake(boost::asio::ssl::stream_base::server, [this, socket, timer]
(const boost::system::error_code &ec)
{
if (timer)
timer->cancel();
if (!ec)
read_request_and_content(socket);
else if (on_error)
on_error(std::shared_ptr<Request>(new Request(*socket)), ec);
session->connection->set_timeout(config.timeout_request);
session->connection->socket->async_handshake(asio::ssl::stream_base::server, [this, session](const error_code &ec) {
session->connection->cancel_timeout();
auto lock = session->connection->handler_runner->continue_lock();
if(!lock)
return;
if(!ec)
this->read_request_and_content(session);
else if(this->on_error)
this->on_error(session->request, ec);
});
}
else if (on_error)
on_error(std::shared_ptr<Request>(new Request(*socket)), ec);
else if(this->on_error)
this->on_error(session->request, ec);
});
}
};
}
#endif //HTTPS_SERVER_HPP
} // namespace SimpleWeb

@ -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

@ -0,0 +1,340 @@
#pragma once
#include "status_code.hpp"
#include <atomic>
#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>
namespace SimpleWeb {
inline bool case_insensitive_equal(const std::string &str1, const std::string &str2) noexcept {
return str1.size() == str2.size() &&
std::equal(str1.begin(), str1.end(), str2.begin(), [](char a, char b) {
return tolower(a) == tolower(b);
});
}
class CaseInsensitiveEqual {
public:
bool operator()(const std::string &str1, const std::string &str2) const noexcept {
return case_insensitive_equal(str1, str2);
}
};
// Based on https://stackoverflow.com/questions/2590677/how-do-i-combine-hash-values-in-c0x/2595226#2595226
class CaseInsensitiveHash {
public:
size_t operator()(const std::string &str) const noexcept {
size_t h = 0;
std::hash<int> hash;
for(auto c : str)
h ^= hash(tolower(c)) + 0x9e3779b9 + (h << 6) + (h >> 2);
return h;
}
};
using CaseInsensitiveMultimap = std::unordered_multimap<std::string, std::string, CaseInsensitiveHash, CaseInsensitiveEqual>;
/// Percent encoding and decoding
class Percent {
public:
/// Returns percent-encoded string
static std::string encode(const std::string &value) noexcept {
static auto hex_chars = "0123456789ABCDEF";
std::string result;
result.reserve(value.size()); // Minimum size of result
for(auto &chr : value) {
if(chr == ' ')
result += '+';
else if(chr == '!' || chr == '#' || chr == '$' || (chr >= '&' && chr <= ',') || (chr >= '/' && chr <= ';') || chr == '=' || chr == '?' || chr == '@' || chr == '[' || chr == ']')
result += std::string("%") + hex_chars[chr >> 4] + hex_chars[chr & 15];
else
result += chr;
}
return result;
}
/// Returns percent-decoded string
static std::string decode(const std::string &value) noexcept {
std::string result;
result.reserve(value.size() / 3 + (value.size() % 3)); // Minimum size of result
for(size_t i = 0; i < value.size(); ++i) {
auto &chr = value[i];
if(chr == '%' && i + 2 < value.size()) {
auto hex = value.substr(i + 1, 2);
auto decoded_chr = static_cast<char>(std::strtol(hex.c_str(), nullptr, 16));
result += decoded_chr;
i += 2;
}
else if(chr == '+')
result += ' ';
else
result += chr;
}
return result;
}
};
/// Query string creation and parsing
class QueryString {
public:
/// Returns query string created from given field names and values
static std::string create(const CaseInsensitiveMultimap &fields) noexcept {
std::string result;
bool first = true;
for(auto &field : fields) {
result += (!first ? "&" : "") + field.first + '=' + Percent::encode(field.second);
first = false;
}
return result;
}
/// Returns query keys with percent-decoded values.
static CaseInsensitiveMultimap parse(const std::string &query_string) noexcept {
CaseInsensitiveMultimap result;
if(query_string.empty())
return result;
size_t name_pos = 0;
auto name_end_pos = std::string::npos;
auto value_pos = std::string::npos;
for(size_t c = 0; c < query_string.size(); ++c) {
if(query_string[c] == '&') {
auto name = query_string.substr(name_pos, (name_end_pos == std::string::npos ? c : name_end_pos) - name_pos);
if(!name.empty()) {
auto value = value_pos == std::string::npos ? std::string() : query_string.substr(value_pos, c - value_pos);
result.emplace(std::move(name), Percent::decode(value));
}
name_pos = c + 1;
name_end_pos = std::string::npos;
value_pos = std::string::npos;
}
else if(query_string[c] == '=') {
name_end_pos = c;
value_pos = c + 1;
}
}
if(name_pos < query_string.size()) {
auto name = query_string.substr(name_pos, name_end_pos - name_pos);
if(!name.empty()) {
auto value = value_pos >= query_string.size() ? std::string() : query_string.substr(value_pos);
result.emplace(std::move(name), Percent::decode(value));
}
}
return result;
}
};
class HttpHeader {
public:
/// Parse header fields
static CaseInsensitiveMultimap parse(std::istream &stream) noexcept {
CaseInsensitiveMultimap result;
std::string line;
getline(stream, line);
size_t param_end;
while((param_end = line.find(':')) != std::string::npos) {
size_t value_start = param_end + 1;
if(value_start < line.size()) {
if(line[value_start] == ' ')
value_start++;
if(value_start < line.size())
result.emplace(line.substr(0, param_end), line.substr(value_start, line.size() - value_start - 1));
}
getline(stream, line);
}
return result;
}
};
class RequestMessage {
public:
/// Parse request line and header fields
static bool parse(std::istream &stream, std::string &method, std::string &path, std::string &query_string, std::string &version, CaseInsensitiveMultimap &header) noexcept {
header.clear();
std::string line;
getline(stream, line);
size_t method_end;
if((method_end = line.find(' ')) != std::string::npos) {
method = line.substr(0, method_end);
size_t query_start = std::string::npos;
size_t path_and_query_string_end = std::string::npos;
for(size_t i = method_end + 1; i < line.size(); ++i) {
if(line[i] == '?' && (i + 1) < line.size())
query_start = i + 1;
else if(line[i] == ' ') {
path_and_query_string_end = i;
break;
}
}
if(path_and_query_string_end != std::string::npos) {
if(query_start != std::string::npos) {
path = line.substr(method_end + 1, query_start - method_end - 2);
query_string = line.substr(query_start, path_and_query_string_end - query_start);
}
else
path = line.substr(method_end + 1, path_and_query_string_end - method_end - 1);
size_t protocol_end;
if((protocol_end = line.find('/', path_and_query_string_end + 1)) != std::string::npos) {
if(line.compare(path_and_query_string_end + 1, protocol_end - path_and_query_string_end - 1, "HTTP") != 0)
return false;
version = line.substr(protocol_end + 1, line.size() - protocol_end - 2);
}
else
return false;
header = HttpHeader::parse(stream);
}
else
return false;
}
else
return false;
return true;
}
};
class ResponseMessage {
public:
/// Parse status line and header fields
static bool parse(std::istream &stream, std::string &version, std::string &status_code, CaseInsensitiveMultimap &header) noexcept {
header.clear();
std::string line;
getline(stream, line);
size_t version_end = line.find(' ');
if(version_end != std::string::npos) {
if(5 < line.size())
version = line.substr(5, version_end - 5);
else
return false;
if((version_end + 1) < line.size())
status_code = line.substr(version_end + 1, line.size() - (version_end + 1) - 1);
else
return false;
header = HttpHeader::parse(stream);
}
else
return false;
return true;
}
};
class ContentDisposition {
public:
/// Can be used to parse the Content-Disposition header field value when
/// clients are posting requests with enctype="multipart/form-data"
static CaseInsensitiveMultimap parse(const std::string &line) {
CaseInsensitiveMultimap result;
size_t para_start_pos = 0;
size_t para_end_pos = std::string::npos;
size_t value_start_pos = std::string::npos;
for(size_t c = 0; c < line.size(); ++c) {
if(para_start_pos != std::string::npos) {
if(para_end_pos == std::string::npos) {
if(line[c] == ';') {
result.emplace(line.substr(para_start_pos, c - para_start_pos), std::string());
para_start_pos = std::string::npos;
}
else if(line[c] == '=')
para_end_pos = c;
}
else {
if(value_start_pos == std::string::npos) {
if(line[c] == '"' && c + 1 < line.size())
value_start_pos = c + 1;
}
else if(line[c] == '"') {
result.emplace(line.substr(para_start_pos, para_end_pos - para_start_pos), line.substr(value_start_pos, c - value_start_pos));
para_start_pos = std::string::npos;
para_end_pos = std::string::npos;
value_start_pos = std::string::npos;
}
}
}
else if(line[c] != ' ' && line[c] != ';')
para_start_pos = c;
}
if(para_start_pos != std::string::npos && para_end_pos == std::string::npos)
result.emplace(line.substr(para_start_pos), std::string());
return result;
}
};
} // namespace SimpleWeb
#ifdef __SSE2__
#include <emmintrin.h>
namespace SimpleWeb {
inline void spin_loop_pause() noexcept { _mm_pause(); }
} // namespace SimpleWeb
// TODO: need verification that the following checks are correct:
#elif defined(_MSC_VER) && _MSC_VER >= 1800 && (defined(_M_X64) || defined(_M_IX86))
#include <intrin.h>
namespace SimpleWeb {
inline void spin_loop_pause() noexcept { _mm_pause(); }
} // namespace SimpleWeb
#else
namespace SimpleWeb {
inline void spin_loop_pause() noexcept {}
} // namespace SimpleWeb
#endif
namespace SimpleWeb {
/// Makes it possible to for instance cancel Asio handlers without stopping asio::io_service
class ScopeRunner {
/// Scope count that is set to -1 if scopes are to be canceled
std::atomic<long> count;
public:
class SharedLock {
friend class ScopeRunner;
std::atomic<long> &count;
SharedLock(std::atomic<long> &count) noexcept : count(count) {}
SharedLock &operator=(const SharedLock &) = delete;
SharedLock(const SharedLock &) = delete;
public:
~SharedLock() noexcept {
count.fetch_sub(1);
}
};
ScopeRunner() noexcept : count(0) {}
/// Returns nullptr if scope should be exited, or a shared lock otherwise
std::unique_ptr<SharedLock> continue_lock() noexcept {
long expected = count;
while(expected >= 0 && !count.compare_exchange_weak(expected, expected + 1))
spin_loop_pause();
if(expected < 0)
return nullptr;
else
return std::unique_ptr<SharedLock>(new SharedLock(count));
}
/// Blocks until all shared locks are released, then prevents future shared locks
void stop() noexcept {
long expected = 0;
while(!count.compare_exchange_weak(expected, -1)) {
if(expected < 0)
return;
expected = 0;
spin_loop_pause();
}
}
};
} // namespace SimpleWeb

@ -1,36 +1,74 @@
#include <iostream>
#include <Kbhit.h>
#include <RakSleep.h>
#include <extern/sol/sol.hpp>
#include "MasterServer.hpp"
#include "RestServer.hpp"
#include "AdminRest.hpp"
using namespace RakNet;
using namespace std;
unique_ptr<RestServer> restServer;
unique_ptr<MasterServer> masterServer;
bool run = true;
shared_ptr<MasterServer> masterServer;
unique_ptr<AdminRest> restAdminServer;
int main()
int main(int argc, char* argv[])
{
masterServer.reset(new MasterServer(2000, 25560));
restServer.reset(new RestServer(8080, masterServer->GetServers()));
if (argc != 2)
return 1;
string luaScript(argv[1]);
masterServer = make_shared<MasterServer>(luaScript);
masterServer->luaStuff([](sol::state &state)
{
sol::table config = state["config"];
sol::object restPort = config["restPort"];
if (restPort.get_type() != sol::type::number)
throw runtime_error("config.restPort is not correct");
restServer = make_unique<RestServer>(restPort.as<unsigned short>(), masterServer->GetServers());
sol::object restAdminCert = config["restAdminCert"];
if (restAdminCert.get_type() != sol::type::string)
throw runtime_error("config.restAdminCert is not correct");
sol::object restAdminKey = config["restAdminKey"];
if (restAdminKey.get_type() != sol::type::string)
throw runtime_error("config.restAdminKey is not correct");
sol::object restAdminVerifyFile = config["restAdminVerifyFile"];
if (restAdminVerifyFile.get_type() != sol::type::string)
throw runtime_error("config.restAdminVerifyFile is not correct");
sol::object restAdminPort = config["restAdminPort"];
if (restAdminPort.get_type() != sol::type::number)
throw runtime_error("config.restAdminPort is not correct");
restAdminServer = make_unique<AdminRest>(restAdminCert.as<string>(), restAdminKey.as<string>(),
restAdminVerifyFile.as<string>(), restAdminPort.as<unsigned short>(), masterServer);
});
auto onExit = [](int /*sig*/){
restServer->stop();
restAdminServer->stop();
masterServer->luaStuff([](sol::state &state) {
sol::protected_function func = state["OnExit"];
if (func.valid())
func.call();
});
masterServer->Stop(false);
masterServer->Wait();
run = false;
};
signal(SIGINT, onExit);
signal(SIGTERM, onExit);
masterServer->Start();
thread server_thread([]() { restServer->start(); });
server_thread.join();
restServer->start();
restAdminServer->start();
masterServer->Wait();
return 0;

@ -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;
}

@ -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;
};

@ -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);
}

@ -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)
if(UNIX) #temporarily disabled for non-unix
if(NOT (${CMAKE_CXX_COMPILER} MATCHES "aarch64" OR ${CMAKE_CXX_COMPILER} MATCHES "arm")) #temporarily disabled for arm
find_package(CallFF REQUIRED)
include_directories(${CallFF_INCLUDES})
endif(NOT (${CMAKE_CXX_COMPILER} MATCHES "aarch64" OR ${CMAKE_CXX_COMPILER} MATCHES "arm"))
endif(UNIX)
option(BUILD_WITH_PAWN "Enable Pawn language" OFF)
option(ENABLE_BREAKPAD "Enable Google Breakpad for Crash reporting" OFF)
@ -22,89 +15,58 @@ if(ENABLE_BREAKPAD)
include_directories(${CMAKE_SOURCE_DIR}/extern/breakpad/src ${Breakpad_Headers})
endif(ENABLE_BREAKPAD)
if(BUILD_WITH_PAWN)
option(BUILD_WITH_LUA "Enable Terra/Lua language" ON)
option(FORCE_LUA "Use Lua instead Terra" OFF)
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(Terra_ROOT ${CMAKE_SOURCE_DIR}/external/terra/)
#if(WIN32 OR FORCE_LUA)
find_package(LuaJit REQUIRED)
#[[else()
find_package(Terra REQUIRED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DENABLE_TERRA")]]
#endif()
set(LuaScript_Headers ${Terra_INCLUDES} ${LUA_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/extern/sol/sol.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)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DENABLE_LUA")
include_directories(${Terra_INCLUDES} ${LUA_INCLUDE_DIR} ${LUAJIT_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/extern/sol)
option(BUILD_WITH_LUA "Enable Terra/Lua language" ON)
option(FORCE_LUA "Use Lua instead Terra" OFF)
if(BUILD_WITH_LUA)
#set(Terra_ROOT ${CMAKE_SOURCE_DIR}/external/terra/)
if(WIN32 OR FORCE_LUA)
find_package(Lua51 REQUIRED)
MESSAGE(STATUS "Found LUA_LIBRARY: ${LUA_LIBRARY}")
MESSAGE(STATUS "Found LUA_INCLUDE_DIR: ${LUA_INCLUDE_DIR}")
else()
find_package(Terra REQUIRED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DENABLE_TERRA")
endif()
set(LuaScript_Sources
Script/LangLua/LangLua.cpp
Script/LangLua/LuaFunc.cpp)
set(LuaScript_Headers ${Terra_INCLUDES} ${LUA_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/extern/LuaBridge ${CMAKE_SOURCE_DIR}/extern/LuaBridge/detail
Script/LangLua/LangLua.hpp)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DENABLE_LUA")
include_directories(${Terra_INCLUDES} ${LUA_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/extern/LuaBridge)
endif(BUILD_WITH_LUA)
set(NativeScript_Sources
Script/LangNative/LangNative.cpp
)
set(NativeScript_Headers
Script/LangNative/LangNative.hpp
)
# local files
set(SERVER
main.cpp
Player.cpp
Player.cpp Players.cpp
Networking.cpp
MasterClient.cpp
Cell.cpp
CellController.cpp
Utils.cpp
Script/Script.cpp Script/ScriptFunction.cpp
Script/ScriptFunctions.cpp
Script/Functions/Actors.cpp Script/Functions/World.cpp Script/Functions/Miscellaneous.cpp
Script/Functions/Books.cpp Script/Functions/Cells.cpp Script/Functions/CharClass.cpp
Script/Functions/Chat.cpp Script/Functions/Dialogue.cpp Script/Functions/Factions.cpp
Script/Functions/GUI.cpp Script/Functions/Items.cpp Script/Functions/Mechanics.cpp
Script/Functions/Positions.cpp Script/Functions/Quests.cpp Script/Functions/Settings.cpp
Script/Functions/Spells.cpp Script/Functions/Stats.cpp Script/Functions/Timer.cpp
Script/API/TimerAPI.cpp Script/API/PublicFnAPI.cpp
${PawnScript_Sources}
${LuaScript_Sources}
${NativeScript_Sources}
CharClass.cpp
Inventory.cpp
Settings.cpp
Timer.cpp
Books.cpp
GUI.cpp
Dialogue.cpp
Factions.cpp
Cells.cpp
Quests.cpp
Spells.cpp
Actors.cpp
NetActor.cpp
CellState.cpp
Object.cpp
stacktrace.cpp
Window.cpp
Script/CommandController.cpp Script/EventController.cpp Script/LuaState.cpp Script/luaUtils.cpp
)
if(WIN32)
list(APPEND SERVER stackwalker/StackWalker.cpp)
endif()
set(SERVER_HEADER
Script/Types.hpp Script/Script.hpp Script/SystemInterface.hpp
Script/ScriptFunction.hpp Script/Platform.hpp Script/Language.hpp
Script/ScriptFunctions.hpp Script/API/TimerAPI.hpp Script/API/PublicFnAPI.hpp
${PawnScript_Headers}
${LuaScript_Headers}
${NativeScript_Headers}
${CallFF_INCLUDES}
)
source_group(tes3mp-server FILES ${SERVER} ${SERVER_HEADER})
@ -134,7 +96,7 @@ set(PROCESSORS_PLAYER
processors/player/ProcessorPlayerResurrect.hpp processors/player/ProcessorPlayerShapeshift.hpp
processors/player/ProcessorPlayerSkill.hpp processors/player/ProcessorPlayerSpeech.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})
@ -171,7 +133,15 @@ add_executable(tes3mp-server
${PROCESSORS_ACTOR} ${PROCESSORS_PLAYER} ${PROCESSORS_WORLD} ${PROCESSORS}
${APPLE_BUNDLE_RESOURCES}
)
add_definitions(-std=gnu++14 -Wno-ignored-qualifiers)
# For Lua debugging
target_compile_definitions(tes3mp-server PRIVATE $<$<CONFIG:Debug>:SOL_SAFE_FUNCTIONS> $<$<CONFIG:RelWithDebInfo>:SOL_SAFE_FUNCTIONS>)
target_compile_definitions(tes3mp-server PRIVATE $<$<CONFIG:Debug>:SOL_SAFE_USERTYPE> $<$<CONFIG:RelWithDebInfo>:SOL_SAFE_USERTYPE>)
target_compile_definitions(tes3mp-server PRIVATE $<$<CONFIG:Debug>:SERVER_DEBUG> $<$<CONFIG:RelWithDebInfo>:SERVER_DEBUG>)
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
#${Boost_SYSTEM_LIBRARY}
@ -182,9 +152,8 @@ target_link_libraries(tes3mp-server
components
${Terra_LIBRARY}
${LUA_LIBRARIES}
${Pawn_LIBRARY}
${LUAJIT_LIBRARY}
${Breakpad_Library}
${CallFF_LIBRARY}
)
if (UNIX)

@ -2,13 +2,14 @@
// Created by koncord on 18.02.17.
//
#include "Cell.hpp"
#include <iostream>
#include <components/openmw-mp/NetworkMessages.hpp>
#include <iostream>
#include "Script/EventController.hpp"
#include "Networking.hpp"
#include "Cell.hpp"
#include "Player.hpp"
#include "Script/Script.hpp"
using namespace std;
@ -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());
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);
}
@ -69,7 +71,8 @@ void Cell::removePlayer(Player *player)
LOG_APPEND(Log::LOG_INFO, "- Removing %s from Cell %s", player->npc.mName.c_str(), getDescription().c_str());
Script::Call<Script::CallbackIdentity("OnCellUnload")>(player->getId(), getDescription().c_str());
mwmp::Networking::get().getState().getEventCtrl().Call<CoreEvent::ON_CELL_UNLOAD>(player, getDescription());
players.erase(it);
return;
@ -81,27 +84,27 @@ void Cell::readActorList(unsigned char packetID, const mwmp::BaseActorList *newA
{
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;
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)
{
case ID_ACTOR_POSITION:
cellActor->hasPositionData = true;
cellActor->position = newActor.position;
cellActor->position = newActor->position;
break;
case ID_ACTOR_STATS_DYNAMIC:
cellActor->hasStatsDynamicData = true;
cellActor->creatureStats.mDynamic[0] = newActor.creatureStats.mDynamic[0];
cellActor->creatureStats.mDynamic[1] = newActor.creatureStats.mDynamic[1];
cellActor->creatureStats.mDynamic[2] = newActor.creatureStats.mDynamic[2];
cellActor->creatureStats.mDynamic[0] = newActor->creatureStats.mDynamic[0];
cellActor->creatureStats.mDynamic[1] = newActor->creatureStats.mDynamic[1];
cellActor->creatureStats.mDynamic[2] = newActor->creatureStats.mDynamic[2];
break;
}
}
@ -116,9 +119,9 @@ bool Cell::containsActor(int refNumIndex, int mpNum)
{
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 false;
@ -128,28 +131,28 @@ mwmp::BaseActor *Cell::getActor(int refNumIndex, int mpNum)
{
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)
return actor;
return actor.get();
}
return 0;
}
void Cell::removeActors(const mwmp::BaseActorList *newActorList)
{
for (std::vector<mwmp::BaseActor>::iterator it = cellActorList.baseActors.begin(); it != cellActorList.baseActors.end();)
for (auto it = cellActorList.baseActors.begin(); it != cellActorList.baseActors.end();)
{
int refNumIndex = (*it).refNumIndex;
int mpNum = (*it).mpNum;
int refNumIndex = (*it)->refNumIndex;
int mpNum = (*it)->mpNum;
bool foundActor = false;
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);
foundActor = true;

@ -1,9 +1,11 @@
#include "CellController.hpp"
#include <iostream>
#include <apps/openmw-mp/Script/EventController.hpp>
#include "Networking.hpp"
#include "CellController.hpp"
#include "Cell.hpp"
#include "Player.hpp"
#include "Script/Script.hpp"
using namespace std;
@ -50,7 +52,7 @@ Cell *CellController::getCell(ESM::Cell *esmCell)
Cell *CellController::getCellByXY(int x, int y)
{
auto it = find_if(cells.begin(), cells.end(), [x, y](const Cell *c)
auto it = find_if (cells.begin(), cells.end(), [x, y](const Cell *c)
{
return c->cell.mData.mX == x && c->cell.mData.mY == y;
});
@ -66,7 +68,7 @@ Cell *CellController::getCellByXY(int x, int y)
Cell *CellController::getCellByName(std::string cellName)
{
auto it = find_if(cells.begin(), cells.end(), [cellName](const Cell *c)
auto it = find_if (cells.begin(), cells.end(), [cellName](const Cell *c)
{
return c->cell.mName == cellName;
});
@ -83,7 +85,7 @@ Cell *CellController::getCellByName(std::string cellName)
Cell *CellController::addCell(ESM::Cell cellData)
{
LOG_APPEND(Log::LOG_INFO, "- Loaded cells: %d", cells.size());
auto it = find_if(cells.begin(), cells.end(), [cellData](const Cell *c) {
auto it = find_if (cells.begin(), cells.end(), [cellData](const Cell *c) {
// Currently we cannot compare because plugin lists can be loaded in different order
//return c->cell.sRecordId == cellData.sRecordId;
if (c->cell.isExterior() && cellData.isExterior())
@ -123,7 +125,8 @@ void CellController::removeCell(Cell *cell)
{
if (*it != nullptr && *it == cell)
{
Script::Call<Script::CallbackIdentity("OnCellDeletion")>(cell->getDescription().c_str());
mwmp::Networking::get().getState().getEventCtrl().Call<CoreEvent::ON_CELL_DELETION>(cell->getDescription());
LOG_APPEND(Log::LOG_INFO, "- Removing %s from CellController", cell->getDescription().c_str());
delete *it;

@ -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();
}

@ -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;
};

@ -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;
}

@ -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;
};

@ -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);
}

@ -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;
};

@ -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();
}

@ -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;
};

@ -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;
}

@ -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;
};

@ -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);
}
}

@ -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;
};

@ -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;
}

@ -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.
//
#include <RakSleep.h>
#include <Getche.h>
#include <sstream>
#include <iostream>
#include <sstream>
#include <thread>
#include <Getche.h>
#include <RakPeerInterface.h>
#include "MasterClient.hpp"
#include <RakSleep.h>
#include <components/openmw-mp/Log.hpp>
#include <components/openmw-mp/Version.hpp>
#include <components/openmw-mp/Master/PacketMasterAnnounce.hpp>
#include "MasterClient.hpp"
#include "Networking.hpp"
#include "Players.hpp"
using namespace std;
using namespace mwmp;
using namespace RakNet;
@ -32,7 +37,7 @@ MasterClient::MasterClient(RakNet::RakPeerInterface *peer, std::string queryAddr
void MasterClient::SetPlayers(unsigned pl)
{
mutexData.lock();
if (queryData.GetPlayers() != pl)
if ((unsigned) queryData.GetPlayers() != pl)
{
queryData.SetPlayers(pl);
updated = true;
@ -43,7 +48,7 @@ void MasterClient::SetPlayers(unsigned pl)
void MasterClient::SetMaxPlayers(unsigned pl)
{
mutexData.lock();
if (queryData.GetMaxPlayers() != pl)
if ((unsigned) queryData.GetMaxPlayers() != pl)
{
queryData.SetMaxPlayers(pl);
updated = true;
@ -108,7 +113,7 @@ void MasterClient::SetRuleValue(std::string key, double value)
void MasterClient::PushPlugin(Plugin plugin)
{
mutexData.lock();
queryData.plugins.push_back(plugin);
queryData.plugins.push_back(move(plugin));
updated = true;
mutexData.unlock();
}
@ -128,6 +133,10 @@ bool MasterClient::Process(RakNet::Packet *packet)
case ID_CONNECTION_REQUEST_ACCEPTED:
case ID_DISCONNECTION_NOTIFICATION:
break;
case ID_CONNECTION_BANNED:
Stop();
LOG_MESSAGE_SIMPLE(Log::LOG_FATAL, "Your server was banned on the master server. Contact the master server administrator for details.");
break;
case ID_MASTER_QUERY:
break;
case ID_MASTER_ANNOUNCE:
@ -148,7 +157,7 @@ bool MasterClient::Process(RakNet::Packet *packet)
}
break;
default:
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Received wrong packet from master server with id: %d", packet->data[0]);
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Received wrong packet from master server with id: %d", (int) packet->data[0]);
return false;
}
return true;
@ -195,22 +204,22 @@ void MasterClient::Thread()
queryData.SetPassword((int) Networking::get().isPassworded());
queryData.SetVersion(TES3MP_VERSION);
auto *players = Players::getPlayers();
//auto *players = Players::getPlayers();
while (sRun)
{
SetPlayers((int) players->size());
SetPlayers((unsigned) Players::size());
auto pIt = players->begin();
if (queryData.players.size() != players->size())
auto pIt = Players::begin();
if (queryData.players.size() != Players::size())
{
queryData.players.clear();
updated = true;
}
else
{
for (int i = 0; pIt != players->end(); i++, pIt++)
for (int i = 0; pIt != Players::end(); i++, pIt++)
{
if (queryData.players[i] != pIt->second->npc.mName)
if (queryData.players[i] != (*pIt)->npc.mName)
{
queryData.players.clear();
updated = true;
@ -222,13 +231,12 @@ void MasterClient::Thread()
if (updated)
{
updated = false;
if (pIt != players->end())
if (pIt != Players::end())
{
for (auto player : *players)
{
if (!player.second->npc.mName.empty())
queryData.players.push_back(player.second->npc.mName);
}
Players::for_each([this](auto player) {
if (!player->npc.mName.empty())
queryData.players.push_back(player->npc.mName);
});
}
Send(PacketMasterAnnounce::FUNCTION_ANNOUNCE);
}

@ -11,6 +11,7 @@
#include <components/openmw-mp/Master/MasterData.hpp>
#include <RakString.h>
#include <components/openmw-mp/Master/PacketMasterAnnounce.hpp>
#include <atomic>
class MasterClient
{
@ -46,7 +47,7 @@ private:
std::thread thrQuery;
mwmp::PacketMasterAnnounce pma;
RakNet::BitStream writeStream;
bool updated;
std::atomic<bool> updated;
};

@ -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;
}

@ -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.
//
#include "Player.hpp"
#include "processors/ProcessorInitializer.hpp"
#include <RakPeer.h>
#include <chrono>
#include <iostream>
#include <thread>
#include <Kbhit.h>
#include <RakPeer.h>
#include <components/misc/stringops.hpp>
#include <components/openmw-mp/NetworkMessages.hpp>
@ -13,24 +15,23 @@
#include <components/openmw-mp/Version.hpp>
#include <components/openmw-mp/Packets/PacketPreInit.hpp>
#include <iostream>
#include <Script/Script.hpp>
#include <Script/API/TimerAPI.hpp>
#include <chrono>
#include <thread>
#include <Script/EventController.hpp>
#include "processors/ProcessorInitializer.hpp"
#include "processors/PlayerProcessor.hpp"
#include "processors/ActorProcessor.hpp"
#include "processors/WorldProcessor.hpp"
#include "Networking.hpp"
#include "MasterClient.hpp"
#include "Cell.hpp"
#include "CellController.hpp"
#include "processors/PlayerProcessor.hpp"
#include "processors/ActorProcessor.hpp"
#include "processors/WorldProcessor.hpp"
#include "Players.hpp"
using namespace mwmp;
using namespace std;
Networking *Networking::sThis = 0;
Networking *Networking::sThis = nullptr;
static int currentMpNum = 0;
@ -38,7 +39,6 @@ Networking::Networking(RakNet::RakPeerInterface *peer) : mclient(nullptr)
{
sThis = this;
this->peer = peer;
players = Players::getPlayers();
CellController::create();
@ -47,15 +47,13 @@ Networking::Networking(RakNet::RakPeerInterface *peer) : mclient(nullptr)
worldPacketController = new WorldPacketController(peer);
// Set send stream
playerPacketController->SetStream(0, &bsOut);
actorPacketController->SetStream(0, &bsOut);
worldPacketController->SetStream(0, &bsOut);
playerPacketController->SetStream(nullptr, &bsOut);
actorPacketController->SetStream(nullptr, &bsOut);
worldPacketController->SetStream(nullptr, &bsOut);
running = true;
exitCode = 0;
Script::Call<Script::CallbackIdentity("OnServerInit")>();
serverPassword = TES3MP_DEFAULT_PASSW;
ProcessorInitializer();
@ -63,11 +61,11 @@ Networking::Networking(RakNet::RakPeerInterface *peer) : mclient(nullptr)
Networking::~Networking()
{
Script::Call<Script::CallbackIdentity("OnServerExit")>(false);
luaState.getEventCtrl().Call<CoreEvent::ON_EXIT>(false);
CellController::destroy();
sThis = 0;
sThis = nullptr;
delete playerPacketController;
delete actorPacketController;
delete worldPacketController;
@ -85,13 +83,13 @@ bool Networking::isPassworded() const
void Networking::processPlayerPacket(RakNet::Packet *packet)
{
Player *player = Players::getPlayer(packet->guid);
auto player = Players::getPlayerByGUID(packet->guid);
PlayerPacket *myPacket = playerPacketController->GetPacket(packet->data[0]);
if (packet->data[0] == ID_HANDSHAKE)
{
myPacket->setPlayer(player);
myPacket->setPlayer(player.get());
myPacket->Read();
if (player->isHandshaked())
@ -116,7 +114,8 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
if (!player->isHandshaked())
{
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;
}
@ -124,15 +123,14 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
{
player->setLoadState(Player::LOADED);
static constexpr unsigned int ident = Script::CallbackIdentity("OnPlayerConnect");
Script::CallBackReturn<ident> result = true;
Script::Call<ident>(result, Players::getPlayer(packet->guid)->getId());
bool result = luaState.getEventCtrl().Call<CoreEvent::ON_PLAYER_CONNECT, bool>(player);
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);
Players::deletePlayer(packet->guid);
Players::deletePlayerByGUID(packet->guid);
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());
myPacket->setPlayer(player);
myPacket->setPlayer(player.get());
myPacket->Read();
myPacket->Send(true);
}
@ -156,43 +154,43 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
if (!PlayerProcessor::Process(*packet))
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled PlayerPacket with identifier %i has arrived", packet->data[0]);
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled PlayerPacket with identifier %i has arrived", (int) packet->data[0]);
}
void Networking::processActorPacket(RakNet::Packet *packet)
{
Player *player = Players::getPlayer(packet->guid);
auto player = Players::getPlayerByGUID(packet->guid);
if (!player->isHandshaked() || player->getLoadState() != Player::POSTLOADED)
return;
if (!ActorProcessor::Process(*packet, baseActorList))
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled ActorPacket with identifier %i has arrived", packet->data[0]);
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Unhandled ActorPacket with identifier %i has arrived", (int) packet->data[0]);
}
void Networking::processWorldPacket(RakNet::Packet *packet)
{
Player *player = Players::getPlayer(packet->guid);
auto player = Players::getPlayerByGUID(packet->guid);
if (!player->isHandshaked() || player->getLoadState() != Player::POSTLOADED)
return;
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);
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)
{
@ -222,7 +220,6 @@ void Networking::update(RakNet::Packet *packet)
// the server
if (it == hashList.end())
break;
}
else // name is incorrect
break;
@ -244,33 +241,39 @@ void Networking::update(RakNet::Packet *packet)
packetPreInit.setChecksums(&tmp);
packetPreInit.Send(packet->systemAddress);
}
return;
return false;
}
playerPacketController->SetStream(&bsIn, 0);
playerPacketController->SetStream(&bsIn, nullptr);
playerPacketController->GetPacket(ID_HANDSHAKE)->RequestData(packet->guid);
Players::newPlayer(packet->guid);
player = Players::getPlayer(packet->guid);
return;
player = Players::addPlayer(packet->guid);
return false;
}
else if (playerPacketController->ContainsPacket(packet->data[0]))
if (playerPacketController->ContainsPacket(packet->data[0]))
{
playerPacketController->SetStream(&bsIn, 0);
playerPacketController->SetStream(&bsIn, nullptr);
processPlayerPacket(packet);
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);
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);
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)
@ -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);
for (TPlayers::iterator pl = players->begin(); pl != players->end(); pl++) //sending other players to new player
Players::for_each([this, &guid](auto pl) //sending other players to new player
{
// If we are iterating over the new player, don't send the packets below
if (pl->first == guid) continue;
if (pl->guid == guid) return;
// If an invalid key makes it into the Players map, ignore it
else if (pl->first == RakNet::UNASSIGNED_RAKNET_GUID) continue;
// if player not fully connected
else if (pl->second == nullptr) continue;
else if (pl->guid == RakNet::UNASSIGNED_RAKNET_GUID) return;
// If we are iterating over a player who has inputted their name, proceed
else if (pl->second->getLoadState() == Player::POSTLOADED)
else if (pl->getLoadState() == Player::POSTLOADED)
{
playerPacketController->GetPacket(ID_PLAYER_BASEINFO)->setPlayer(pl->second);
playerPacketController->GetPacket(ID_PLAYER_STATS_DYNAMIC)->setPlayer(pl->second);
playerPacketController->GetPacket(ID_PLAYER_ATTRIBUTE)->setPlayer(pl->second);
playerPacketController->GetPacket(ID_PLAYER_SKILL)->setPlayer(pl->second);
playerPacketController->GetPacket(ID_PLAYER_POSITION)->setPlayer(pl->second);
playerPacketController->GetPacket(ID_PLAYER_CELL_CHANGE)->setPlayer(pl->second);
playerPacketController->GetPacket(ID_PLAYER_EQUIPMENT)->setPlayer(pl->second);
playerPacketController->GetPacket(ID_PLAYER_BASEINFO)->setPlayer(pl.get());
playerPacketController->GetPacket(ID_PLAYER_STATS_DYNAMIC)->setPlayer(pl.get());
playerPacketController->GetPacket(ID_PLAYER_ATTRIBUTE)->setPlayer(pl.get());
playerPacketController->GetPacket(ID_PLAYER_SKILL)->setPlayer(pl.get());
playerPacketController->GetPacket(ID_PLAYER_POSITION)->setPlayer(pl.get());
playerPacketController->GetPacket(ID_PLAYER_CELL_CHANGE)->setPlayer(pl.get());
playerPacketController->GetPacket(ID_PLAYER_EQUIPMENT)->setPlayer(pl.get());
playerPacketController->GetPacket(ID_PLAYER_BASEINFO)->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_EQUIPMENT)->Send(guid);
}
}
});
LOG_APPEND(Log::LOG_WARN, "- Done");
@ -321,14 +321,15 @@ void Networking::newPlayer(RakNet::RakNetGUID guid)
void Networking::disconnectPlayer(RakNet::RakNetGUID guid)
{
Player *player = Players::getPlayer(guid);
if (!player)
auto player = Players::getPlayerByGUID(guid);
if (player == nullptr)
return;
Script::Call<Script::CallbackIdentity("OnPlayerDisconnect")>(player->getId());
playerPacketController->GetPacket(ID_USER_DISCONNECTED)->setPlayer(player);
luaState.getEventCtrl().Call<CoreEvent::ON_PLAYER_DISCONNECT>(player);
playerPacketController->GetPacket(ID_USER_DISCONNECTED)->setPlayer(player.get());
playerPacketController->GetPacket(ID_USER_DISCONNECTED)->Send(true);
Players::deletePlayer(guid);
Players::deletePlayerByGUID(guid);
}
PlayerPacketController *Networking::getPlayerPacketController() const
@ -369,11 +370,11 @@ void Networking::setCurrentMpNum(int value)
int Networking::incrementMpNum()
{
currentMpNum++;
Script::Call<Script::CallbackIdentity("OnMpNumIncrement")>(currentMpNum);
luaState.getEventCtrl().Call<CoreEvent::ON_MP_REFNUM>(currentMpNum);
return currentMpNum;
}
const Networking &Networking::get()
Networking &Networking::get()
{
return *sThis;
}
@ -396,20 +397,18 @@ PacketPreInit::PluginContainer Networking::getPluginListSample()
while (true)
{
unsigned field = 0;
auto name = "";
Script::Call<Script::CallbackIdentity("OnRequestPluginList")>(name, id, field++);
if (strlen(name) == 0)
auto name = luaState.getEventCtrl().Call<CoreEvent::ON_REQUEST_PLUGIN_LIST, string>(id, field++);
if (name.empty())
break;
PacketPreInit::HashList hashList;
while (true)
{
auto hash = "";
Script::Call<Script::CallbackIdentity("OnRequestPluginList")>(hash, id, field++);
if (strlen(hash) == 0)
auto hash = luaState.getEventCtrl().Call<CoreEvent::ON_REQUEST_PLUGIN_LIST, string>(id, field++);
if (hash.empty())
break;
hashList.push_back((unsigned)stoul(hash));
}
pls.push_back({name, hashList});
pls.emplace_back(name, hashList);
id++;
}
return pls;
@ -425,8 +424,11 @@ int Networking::mainLoop()
{
RakNet::Packet *packet;
auto &timerCtrl = luaState.getTimerCtrl();
while (running)
{
bool updated = false;
if (kbhit() && getch() == '\n')
break;
for (packet=peer->Receive(); packet; peer->DeallocatePacket(packet), packet=peer->Receive())
@ -469,15 +471,22 @@ int Networking::mainLoop()
case ID_UNCONNECTED_PING:
break;
default:
update(packet);
updated = update(packet);
break;
}
}
TimerAPI::Tick();
this_thread::sleep_for(chrono::milliseconds(1));
timerCtrl.tick();
if (updated)
{
// 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));
}
TimerAPI::Terminate();
timerCtrl.terminate();
return exitCode;
}
@ -516,14 +525,14 @@ MasterClient *Networking::getMasterClient()
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);
}
void Networking::postInit()
{
Script::Call<Script::CallbackIdentity("OnServerPostInit")>();
luaState.getEventCtrl().Call<CoreEvent::ON_POST_INIT>();
samples = getPluginListSample();
if (mclient)
{

@ -9,7 +9,7 @@
#include <components/openmw-mp/Controllers/ActorPacketController.hpp>
#include <components/openmw-mp/Controllers/WorldPacketController.hpp>
#include <components/openmw-mp/Packets/PacketPreInit.hpp>
#include "Player.hpp"
#include <apps/openmw-mp/Script/LuaState.hpp>
class MasterClient;
namespace mwmp
@ -31,7 +31,7 @@ namespace mwmp
void processPlayerPacket(RakNet::Packet *packet);
void processActorPacket(RakNet::Packet *packet);
void processWorldPacket(RakNet::Packet *packet);
void update(RakNet::Packet *packet);
bool update(RakNet::Packet *packet);
unsigned short numberOfConnections() const;
unsigned int maxConnections() const;
@ -45,6 +45,8 @@ namespace mwmp
ActorPacketController *getActorPacketController() const;
WorldPacketController *getWorldPacketController() const;
LuaState &getState() {return luaState;}
BaseActorList *getLastActorList();
BaseEvent *getLastEvent();
@ -53,22 +55,22 @@ namespace mwmp
int incrementMpNum();
MasterClient *getMasterClient();
void InitQuery(std::string queryAddr, unsigned short queryPort);
void InitQuery(const std::string &queryAddr, unsigned short queryPort);
void setServerPassword(std::string passw) noexcept;
bool isPassworded() const;
static const Networking &get();
static Networking &get();
static Networking *getPtr();
void postInit();
private:
LuaState luaState;
PacketPreInit::PluginContainer getPluginListSample();
std::string serverPassword;
static Networking *sThis;
RakNet::RakPeerInterface *peer;
RakNet::BitStream bsOut;
TPlayers *players;
MasterClient *mclient;
BaseActorList baseActorList;

@ -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);
}

@ -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.
//
#include "Player.hpp"
#include <components/openmw-mp/NetworkMessages.hpp>
#include "Networking.hpp"
TPlayers Players::players;
TSlots Players::slots;
#include "Player.hpp"
#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,
"getHealth", &NetActor::getHealth,
"setHealth", &NetActor::setHealth,
"getMagicka", &NetActor::getMagicka,
"setMagicka", &NetActor::setMagicka,
"getFatigue", &NetActor::getFatigue,
"setFatigue", &NetActor::setFatigue,
"getCell", &NetActor::getCell,
"getInventory", &NetActor::getInventory,
"getPreviousCellPos", &Player::getPreviousCellPos,
"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
);
}
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)
{
basePlayer = this;
netCreature = this;
printf("Player::Player()\n");
handshakeCounter = 0;
loadState = NOTLOADED;
resetUpdateFlags();
cell.blank();
npc.blank();
npcStats.blank();
creatureStats.blank();
charClass.blank();
customData = mwmp::Networking::get().getState().getState()->create_table();
}
if (players[guid] != 0)
Player::~Player()
{
printf("Player::~Player()\n");
CellController::get()->deletePlayer(this);
}
void Player::update()
{
auto plPCtrl = mwmp::Networking::get().getPlayerPacketController();
if (baseInfoChanged)
{
CellController::get()->deletePlayer(players[guid]);
auto packet = plPCtrl->GetPacket(ID_PLAYER_BASEINFO);
packet->setPlayer(basePlayer);
packet->Send(false);
packet->Send(true);
}
LOG_APPEND(Log::LOG_INFO, "- Emptying slot %i", players[guid]->getId());
// 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();
}
slots[players[guid]->getId()] = 0;
delete players[guid];
players.erase(guid);
if (positionChanged)
{
auto packet = plPCtrl->GetPacket(ID_PLAYER_POSITION);
packet->setPlayer(basePlayer);
packet->Send(false);
}
}
void Players::newPlayer(RakNet::RakNetGUID guid)
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Creating new player with guid %lu", guid.g);
// The character class can override values from below on the client, so send it first
cClass.update();
players[guid] = new Player(guid);
players[guid]->cell.blank();
players[guid]->npc.blank();
players[guid]->npcStats.blank();
players[guid]->creatureStats.blank();
players[guid]->charClass.blank();
players[guid]->isWerewolf = false;
if (levelChanged)
{
auto packet = plPCtrl->GetPacket(ID_PLAYER_LEVEL);
packet->setPlayer(basePlayer);
packet->Send(false);
packet->Send(true);
}
for (unsigned int i = 0; i < mwmp::Networking::get().maxConnections(); i++)
if (statsChanged)
{
if (slots[i] == 0)
{
LOG_APPEND(Log::LOG_INFO, "- Storing in slot %i", i);
auto packet = plPCtrl->GetPacket(ID_PLAYER_STATS_DYNAMIC);
packet->setPlayer(basePlayer);
packet->Send(false);
packet->Send(true);
slots[i] = players[guid];
slots[i]->setId(i);
break;
}
statsDynamicIndexChanges.clear();
}
}
Player *Players::getPlayer(RakNet::RakNetGUID guid)
{
if (players.count(guid) == 0)
return nullptr;
return players[guid];
}
if (attributesChanged)
{
auto packet = plPCtrl->GetPacket(ID_PLAYER_ATTRIBUTE);
packet->setPlayer(basePlayer);
packet->Send(false);
packet->Send(true);
TPlayers *Players::getPlayers()
{
return &players;
}
attributeIndexChanges.clear();
}
unsigned short Players::getLastPlayerId()
{
return slots.rbegin()->first;
}
if (skillsChanged)
{
auto packet = plPCtrl->GetPacket(ID_PLAYER_SKILL);
packet->setPlayer(basePlayer);
packet->Send(false);
packet->Send(true);
Player::Player(RakNet::RakNetGUID guid) : BasePlayer(guid)
{
handshakeState = false;
loadState = NOTLOADED;
}
skillIndexChanges.clear();
}
Player::~Player()
{
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()
@ -82,24 +221,24 @@ unsigned short Player::getId()
return id;
}
void Player::setId(unsigned short id)
void Player::setId(unsigned short newId)
{
this->id = id;
this->id = newId;
}
void Player::setHandshake()
bool Player::isHandshaked()
{
handshakeState = true;
return handshakeCounter == numeric_limits<int>::max();
}
bool Player::isHandshaked()
void Player::setHandshake()
{
return handshakeState;
handshakeCounter = numeric_limits<int>::max();
}
void Player::setLoadState(int state)
int Player::handshakeAttempts()
{
loadState = state;
return handshakeCounter++;
}
int Player::getLoadState()
@ -107,11 +246,9 @@ int Player::getLoadState()
return loadState;
}
Player *Players::getPlayer(unsigned short id)
void Player::setLoadState(int state)
{
if (slots.find(id) == slots.end())
return nullptr;
return slots[id];
loadState = state;
}
CellController::TContainer *Player::getCells()
@ -119,37 +256,36 @@ CellController::TContainer *Player::getCells()
return &cells;
}
void Player::sendToLoaded(mwmp::PlayerPacket *myPacket)
void Player::forEachLoaded(std::function<void(Player *pl, Player *other)> func)
{
std::list <Player*> plList;
for (auto cell : cells)
for (auto pl : *cell)
plList.push_back(pl);
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)
for (auto &&pl : plList)
{
if (pl == this) continue;
myPacket->setPlayer(this);
myPacket->Send(pl->guid);
func(this, pl);
}
}
void Player::forEachLoaded(std::function<void(Player *pl, Player *other)> func)
void Player::sendToLoaded(mwmp::PlayerPacket *myPacket)
{
std::list <Player*> plList;
for (auto cell : cells)
{
for (auto pl : *cell)
{
if (pl != nullptr && !pl->npc.mName.empty())
plList.push_back(pl);
}
}
plList.push_back(pl);
plList.sort();
plList.unique();
@ -157,6 +293,375 @@ void Player::forEachLoaded(std::function<void(Player *pl, Player *other)> func)
for (auto pl : plList)
{
if (pl == this) continue;
func(this, pl);
myPacket->setPlayer(this);
myPacket->Send(pl->guid);
}
}
void Player::kick() const
{
mwmp::Networking::getPtr()->kickPlayer(guid);
}
void Player::ban() const
{
auto netCtrl = mwmp::Networking::getPtr();
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 <string>
#include <chrono>
#include <memory>
#include <RakNetTypes.h>
#include <components/esm/npcstats.hpp>
@ -18,32 +19,30 @@
#include <components/openmw-mp/Log.hpp>
#include <components/openmw-mp/Base/BasePlayer.hpp>
#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>
#include <apps/openmw-mp/Script/LuaState.hpp>
#include "Cell.hpp"
#include "CellController.hpp"
struct Player;
typedef std::map<RakNet::RakNetGUID, Player*> TPlayers;
typedef std::map<unsigned short, Player*> TSlots;
class Players
{
public:
static void newPlayer(RakNet::RakNetGUID guid);
static void deletePlayer(RakNet::RakNetGUID guid);
static Player *getPlayer(RakNet::RakNetGUID guid);
static Player *getPlayer(unsigned short id);
static TPlayers *getPlayers();
static unsigned short getLastPlayerId();
private:
static TPlayers players;
static TSlots slots;
};
class Player : public mwmp::BasePlayer
#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"
class Player : public mwmp::BasePlayer, public NetActor
{
friend class Cell;
friend class Players;
unsigned short id;
uint64_t getGUID() const {return guid.g;}
public:
static void Init(LuaState &lua);
public:
enum
@ -52,12 +51,13 @@ public:
LOADED,
POSTLOADED
};
Player(RakNet::RakNetGUID guid);
explicit Player(RakNet::RakNetGUID guid);
unsigned short getId();
void setId(unsigned short id);
bool isHandshaked();
int handshakeAttempts();
void setHandshake();
void setLoadState(int state);
@ -70,11 +70,129 @@ public:
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:
CellController::TContainer cells;
bool handshakeState;
int handshakeCounter;
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

@ -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();
}

@ -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;
};

@ -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);
}

@ -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

@ -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;
}

@ -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;
};

@ -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;
}

@ -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…
Cancel
Save