Compare commits

...

153 Commits

Author SHA1 Message Date
David Cernat c2f330d4f1
Merge pull request #354 from TES3MP/master
Add master commits up to 4 Dec 2017
7 years ago
David Cernat fc5e883160 [General] Rework PlayerStatsDynamic packets so they are of minimal size 7 years ago
David Cernat 993cc3dfd6 [Server] Rename server "plugins"/"mods" into "modules" for clarity
The terms "plugins" and "mods" were used interchangeably to refer to collections of server scripts, which was bound to cause confusion later on, especially with client data files frequently being referred to as "plugins" and "mods" as well.

Moreover, the server configuration file now starts its manual ordering with "Module1" for consistency with the pluginlist.json (soon to be dataFileList.json) of the CoreScripts.
7 years ago
David Cernat d1ad0c91f8 [General] Rework PlayerEquipment packets so they are of minimal size
Moreover, rename BaseNetCreature's equipedItems into equipmentItems.
7 years ago
David Cernat 7e322f1f8b Merge pull request #352 from TES3MP/master while resolving conflicts
# Conflicts:
#	README.md
7 years ago
David Cernat bd9e8bd10f [General] Simplify storing of attribute and skill index changes 7 years ago
David Cernat 711bdf187a [General] Add BaseNetCreature to CMakeLists in components 7 years ago
David Cernat 720ef5f6c5 [General] Use consistent code style 7 years ago
David Cernat eff3504b05
Merge pull request #350 from TES3MP/master
Add master commits up to 29 Nov 2017
7 years ago
Koncord b55bb445dd [General] Change underlying type of the WidgetType to uint8_t 7 years ago
Koncord a546d99000 [General] Fix type 7 years ago
Koncord 1841562553 [General] Minor cleanup of the World Packets 7 years ago
Koncord 744b8cf168 [General] Minor cleanup of the Actor Packets 7 years ago
Koncord e44fcdc0b3 [General] Cleanup Player packets 7 years ago
David Cernat 901fe72471 [Server] Fix variable shadowing in Player 7 years ago
David Cernat a796f81444 [General] Add and use utility function for int value checks in vectors 7 years ago
Koncord 46d55816b8 [Client] Remove debug code 7 years ago
Koncord 4ebfcc4a21 [Server] Limit handshake attempts 7 years ago
Koncord 729f6e745e [Server] Remove unused get() method from EventController 7 years ago
Koncord fef3764eb1 [General] Add forgotten files to build 7 years ago
Koncord 382f56178c [Client] Add custom windows 7 years ago
Koncord e657934cef [Server] Add custom window API 7 years ago
Koncord c276ff4bd9 [General] Add packet GUIWindow 7 years ago
David Cernat 0a35b897be
Merge pull request #348 from TES3MP/master
Add master commits up to 25 Nov 2017
7 years ago
David Cernat b0965f094a [General] Rework PlayerAttribute packets so they are of minimal size
Previously, whenever a single attribute value changed for a player, that player then sent a PlayerAttribute packet with all values for all 8 attributes.

This did not cause anywhere as much packet spam as PlayerSkill used to, but there was no good reason not to fix it as well.
7 years ago
David Cernat ef79a98544 [General] Rework PlayerSkill packets so they are of minimal size
Previously, whenever a single skill value changed for a player, that player then sent a PlayerSkill packet with all values for all 27 skills, plus the player's progress towards the next level and the bonuses to each attribute on the next level up as the result of sklll increases thus far.

This commit makes PlayerSkill contain only the values of specific skills, moves the player's progress towards the next level to PlayerLevel packets, and moves the bonuses to each attribute on the next level up to PlayerAttribute packets.

Players now also send a PlayerSkill packet whenever their progress towards a new point in a skill changes. This was previously avoided so as to not have massive packet spam.
7 years ago
David Cernat 64b57983f0 [General] Add TRACE log messages in player processors 7 years ago
David Cernat 606ddff813
Merge pull request #346 from TES3MP/master
Add master commits up to 24 Nov 2017
7 years ago
David Cernat cac4684986 [Client] Don't force skill update on cell change
Previously, an attempt by the server to simultaneously change a player's cell and skills (as you'd expect when a player file is loaded) led to:

1) The server sending the cell packet first and the skill packet afterwards

2) The player receiving the cell packet and sending their own skill packet as part of the client's forced skill update

3) The player receiving the skill packet from the server

4) The server receiving the skill packet from the player

The result was that, if the player then left the server without sending another skill packet, the server's memory retained the skills the player had sent instead of the skills it had sent to the player.

This is the first step in a solution to that situation and similar ones.
7 years ago
David Cernat 07d75abdf8 [Server] Use consistent order for includes 7 years ago
David Cernat 1ee460bba8
Merge pull request #344 from TES3MP/master
Add master commits up to 23 Nov 2017
7 years ago
David Cernat d33254f287 [Server] Rename CharClass' isDefault() into isCustom()
It was already using the logic of isCustom() by mistake.
7 years ago
David Cernat 010a80ceca [Server] Place getters and setters in consistent order 7 years ago
David Cernat 947b3f76be [Server] Replace Player's isMale() and setIsMale() with gender property
For simplicity and clarity.
7 years ago
David Cernat 6f822f54aa [Server] Make chat commands case insensitive 7 years ago
David Cernat a3e2ab4d4e [Server] Send correct packet for inventory changes 7 years ago
David Cernat 4cc0216e0a [Server] Send cell changes before position changes, and prioritize both
Previously, a script changing a player's cell and position at the same time would end up sending a position packet first and then a cell change packet that overrode the former, with the player ending up at the center of the destination cell instead of at the correct position.
7 years ago
David Cernat 80be664139 [Server] Fix skill-related script functions
Add getSkillIncrease() and setSkillIncrease() script functions to get and set the attribute bonuses received at the next level up as a result of skill increases.

Previously, getSkill() and setSkill() attempted to return and set the attribute bonuses, respectively. However, they mistakenly used a skill ID as a parameter for the attribute bonuses, when in fact npcStats.mSkillIncrease is an integer array of size 8 where the key stands for an attribute's ID. As a result, setSkill() had the unexpected side effect of messing up a player's major and minor skills because of the invalid values it was setting for npcStats.mSkillIncreases.
7 years ago
David Cernat 57a0415ba3 [Server] Send level packets in Player's update() at the appropriate time
Previously, trying to send a level packet after base info and character class packets in a script actually led to the level packet being sent first and then being overridden by the others, with the player ending up at level 1 on their client.
7 years ago
David Cernat 494b10b97e [Server] Send player packets in a more appropriate order
Previously, the fact that a character class packet got sent after a dynamic stats packet caused the dynamic stats to get overridden on the client by the class change.
7 years ago
David Cernat ba161ddddd [Server] Make a few function names more consistent and fix typos 7 years ago
David Cernat 7788821a69
Merge pull request #340 from TES3MP/master
Add master commits up to 17 Nov 2017
7 years ago
David Cernat 068f733d1e
Merge pull request #338 from TES3MP/master
Add master commits up to 16 Nov 2017
7 years ago
David Cernat 1272b03f25 [Server] Fix typo in player script function 7 years ago
David Cernat b4e8560698 [Client] Send cell states correctly after inputting name
Previously, initial cell states were sent in LocalPlayer::processCharGen() and were ignored by the server because the player was not yet regarded as loaded. The result was that existing players logging in could not see each other until they went through at least one cell change.
7 years ago
David Cernat 926106cf8c [General] Rework CharGen slightly for clarity purposes
Previously, charGenStage.end was doing double duty as both the variable indicating the number of CharGen stages and – when set to 0 – the variable indicating that CharGen was over. The latter role is now filled by a new boolean.
7 years ago
David Cernat ac7a588632 [General] Update config files 7 years ago
David Cernat a21f5d18d6
Merge pull request #336 from TES3MP/master
Add master commits up to 12 Nov 2017
7 years ago
David Cernat a8261bb385 [General] Fix printing of packet identifiers after changes to logger 7 years ago
David Cernat 700e4d032e
Merge pull request #334 from TES3MP/master
Add master commits up to 11 Nov 2017
7 years ago
David Cernat 4dbada69bf Merge pull request #332 from TES3MP/master while resolving conflicts 7 years ago
Koncord ca7f3f7450 [Client] Disable focus on <tab> for chat window 7 years ago
Koncord 64b531aa3c [Server] Remove redundant argument 7 years ago
Koncord f377164db9 [Client] Fix build 7 years ago
Koncord 7ab01b66e4 [General] Rewrite Log class 7 years ago
Koncord d15c674584 [General] Move getFilenameTimestamp() to Utils 7 years ago
Koncord 0da44f69ad [Server] Isolate getModFolder() & getDataFolder() 7 years ago
Koncord 062d6a1824 [Server] Add sandboxed import() function 7 years ago
David Cernat 29cb51cdce [Server] Enable SOL_SAFE_USERTYPE for both Debug and RelWithDebInfo 7 years ago
Koncord 1d111fdca8 [General] Update sol submodule to 2.18.5 7 years ago
Koncord bd7082f57e [Server] Use custom Lua error handler not only on Windows
For some reason sol's default error handler does not wroking properly
7 years ago
David Cernat 71c921faa7 [Server] Rename property cell into description, initialize Cells type 7 years ago
David Cernat 5653d07c7b [Server] Fix build on Windows 7 years ago
Koncord 948090676a [Server] Impove Lua Error handler for Windows 7 years ago
David Cernat 61db22f5ae
Merge pull request #329 from TES3MP/master
Add master commits up to 31 Oct 2017
7 years ago
David Cernat b7e5e77166 [Server] Fix getCaseInsensitiveFilename, simplify Players.size() 7 years ago
David Cernat 378d30834b [Server] Add special error handler for Sol back in, but only for Windows 7 years ago
Koncord 14d47213ef [Server] Add Players.size() to Lua API 7 years ago
Koncord 3495fd43f4 [Client] Add network statistics (CTRL+F2) 7 years ago
Koncord e7a5919477 [Server] Fix path to native libs 7 years ago
Koncord 1aa630e4a9 [Server] Add StackWalker for Windows 7 years ago
Koncord e8915f8ec5 [Client] Fix build 7 years ago
Koncord 14fdec2478 [Server] Add forgotten stacktrace.cpp 7 years ago
David Cernat b801cf2c9e Merge pull request #327 from TES3MP/master
Add master commits up to 26 Oct 2017
7 years ago
David Cernat 878294e4fe Merge branch 'new-script-api' of https://github.com/TES3MP/openmw-tes3mp into new-script-api 7 years ago
Koncord d44848ecbb [Server] Fix build 7 years ago
Koncord 05abb8ace3 [Server] Add Log level constants to lua 7 years ago
Koncord 04a844a9c0 [Server] Use sol's default_handler 7 years ago
Koncord dad0b38f25 [Server] Add custom terminate handler with stacktrace 7 years ago
Koncord a3d5fbbdcd [Server] Add stacktrace 7 years ago
Koncord 916ada108f [General] Modernize Log utility
* Reverse Log levels
* Add LOG_TRACE
* Spawn instance of Log in Get() function
7 years ago
David Cernat bece095579 [Server] Add getCaseInsensitiveFilename script function back in 7 years ago
David Cernat d6dc75e94b Merge branch 'new-script-api' of https://github.com/TES3MP/openmw-tes3mp into new-script-api 7 years ago
David Cernat 76a4abd7c0 Merge pull request #325 from TES3MP/master while resolving conflicts
# Conflicts:
#	apps/openmw-mp/processors/player/ProcessorPlayerTopic.hpp
7 years ago
David Cernat 0e73571111 Merge pull request #322 from TES3MP/master
Add master commits up to 23 Oct 2017
7 years ago
David Cernat 8a93631e08 Merge pull request #320 from TES3MP/master
Add master commits up to 22 Oct 2017
7 years ago
Koncord ba8613a179 [Browser] Add "no password" filter to browser 7 years ago
David Cernat fb67180809 [Server] Fix build in Visual Studio 7 years ago
Koncord 4530370e52 [Server] Use old style of Server Plugins location 7 years ago
Koncord ce6a4e4032 [Server] Fix indents 7 years ago
Koncord fc3f2483ee [Server] Add manual Server Plugins sort 7 years ago
Koncord dffd3bfa7d [Server] Add customData to Player
example:
counter = 0

Event.register(Events.ON_PLAYER_CONNECT, function(player)
    player.customData.counter = counter
    counter = counter + 1
    return true
end)

CommandController.registerCommand("test", function(player, args)
    player:message(player.customData.counter, false)
    return true
end, "")
7 years ago
Koncord 7a0b45d456 [Server] Load mods in dependencies order 7 years ago
Koncord 66283943c5 [Browser] Fix Clang warnings 7 years ago
Koncord d702845026 [General] Fix Clang warnings in Log.hpp 7 years ago
Koncord 0a0c9893b1 [General] Update sol submodule 7 years ago
David Cernat 6e7c033a5d Merge pull request #318 from TES3MP/master
Add master commits up to 18 Oct 2017
7 years ago
David Cernat bbac26294f [Server] Fix typos and make all files end with newlines 7 years ago
David Cernat b20df4b7cd Merge pull request #316 from TES3MP/master
Add master commits up to 17 Oct 2017
7 years ago
David Cernat 17a8e32782 Merge pull request #314 from TES3MP/master
Add master commits up to 16 Oct 2017
7 years ago
Koncord 1fd16ba69c [Browser] Correctly init parent class 7 years ago
Koncord 62588ce088 [Server] Minor fixes 7 years ago
Koncord 6cb9c3c713 [General] Remove explicit specifier 7 years ago
Koncord 3839a2dcfd [Browser] Remove unused variables 7 years ago
Koncord ed75563a94 [Browser] Do not show "Unreachable" servers when ping filter is enabled 7 years ago
Koncord 01a5196a92 [Browser] Mark few strings translatable 7 years ago
Koncord 15723adb9a [Browser] Make sort servers case insensitive 7 years ago
Koncord 57353cdfff [Browser] Mark unreachable servers as "Unreachable" instead 999 7 years ago
David Cernat fe9a3088bd Merge pull request #312 from TES3MP/master
Add master commits up to 10 Oct 2017
7 years ago
Koncord 5c79e7106f [Browser] Minor improvements 7 years ago
David Cernat 4845599bda Merge pull request #308 from TES3MP/master while resolving conflicts
# Conflicts:
#	apps/openmw-mp/main.cpp
7 years ago
David Cernat 7a38a0b223 Merge pull request #305 from TES3MP/master
Add master commits up to 6 Oct 2017
7 years ago
Koncord cbabc91b06 [Server] Stop MasterClient thread and inform server owner on ban 7 years ago
Koncord 846f83e3e4 [Master] Use RakNet ban system instead homebrew 7 years ago
David Cernat 841a4f90c1 Merge pull request #301 from TES3MP/master
Add master commits up to 28 Sep 2017
7 years ago
David Cernat 3284769fef [Server] Add getModFolder() script function and Config environment 7 years ago
David Cernat b5ce3cebbc Merge pull request #297 from TES3MP/master
Add master commits up to 22 Sep 2017
7 years ago
Koncord 73aa83aa03 [Master] Use RakNet ban system with homebrew bansystem 7 years ago
Koncord 5fcdff843c [Master] Minor fixes in lua scripts 7 years ago
David Cernat 76f1a61538 Merge pull request #295 from TES3MP/master
Add master commits up to 20 Sep 2017
7 years ago
David Cernat d591180e99 [Server] Clean up logAppend and add new message for server shutdown 7 years ago
David Cernat 381a5fabc4 Merge pull request #291 from TES3MP/master
Add master commits up to 17 Sep 2017
7 years ago
David Cernat 9838cc680a [Server] Enable Lua debugging for RelWithDebInfo builds 7 years ago
David Cernat 98195b5e3c Merge pull request #289 from TES3MP/master
Add master commits up to 15 Sep 2017
7 years ago
Koncord 3124e627cf [General] Move hack back to the right place 7 years ago
Koncord c2286482ae [Master] Add 'savebans' command to RESTful API 7 years ago
Koncord f814ae795d [Master] Fix return from isServerValid()
Temporary accept all servers in OnServerAnnnounce
7 years ago
Koncord 50ef9677fe [Master] Fix master.lua
Remove unban() from loadBans()
Disable strict json order from saveBans()
7 years ago
Koncord 838e05521e [Master] Minor fixes 7 years ago
David Cernat cccbe753d7 [General] Update version to 0.7-alpha 7 years ago
Koncord 6decd148e5 [Server] Fix build on Windows 7 years ago
Koncord 045dc566ea Merge branch 'master' into new-script-api 7 years ago
Koncord bb183457a6 [Master] Init BanAddress/UnbanAddress before opening script 7 years ago
Koncord dc18916f46 [Master] Add isServerValid lambda. Add "OnServerAnnounce" callback 7 years ago
Koncord f99dafbf51 [Master] Add scripts for generating certificates for testing purposes 7 years ago
Koncord aee6fb1265 [Master] Add master lua scripts 7 years ago
Koncord d1388cdf84 [Master] Add Admin Rest Server
Add admin rest callback - "OnAdminRequest". Called on every POST request
7 years ago
Koncord b869fe0b76 [Master] Add Lua
Init master server values by config table in script
Add BanAddress/UnbanAddress to lua
Fix a couple of indents
Pass lua script as first launch parameter of master server
7 years ago
Koncord 0e2817da88 [Master] Move response stuff to ResponseUtils.hpp 7 years ago
Koncord fc8232f943 [Master] Add ban/unban functions 7 years ago
Koncord 26324c2578 [Master] Change Ban structure to vector<string> 7 years ago
Koncord 4e93905350 [Master] Backport SimpleWeb 7 years ago
Koncord 510e657c93 Merge branch 'master' into new-script-api 7 years ago
Koncord 060ebe3d4a [Server] Init isWerewolf variable
Comment unused code
7 years ago
Koncord 1c0adc47ee [Server] Fix [get/set]MajorSkills and [get/set]MinorSkills 7 years ago
Koncord 66fdba957b [Server] Add getDataFolder() to Script API 7 years ago
Koncord 1d16958910 [Server] Fix multiple calls of ON_POST_INIT 7 years ago
Koncord b18c6dec9d [Server] Fix sendList()
Remove return from requestContainers()
Rename "Cells" usertype to "Cell"
7 years ago
Koncord 991a1fe8d8 [General] Update submodules 7 years ago
Koncord 60fc0bedb8 [Server] Use lower case for methods 7 years ago
Koncord 7717f9bece [Server] Replace getKillRefId/getKillNumber with getKill 7 years ago
Koncord fe2dd1bad4 [Server] Use correct cells in "send" functions
Remove unused return type from ON_PLAYER_SENDMESSAGE
7 years ago
Koncord 2d0840cb3a [General] Modernize Script API
This commit changes the style of tes3mp serverside scripting mods. Short list of changes:
* Break compatibility with old server mods
* OOP style lua API
* Basic dependency checker, allowing the installation of multiple server mods without changing configs
* Remove support for C++ plugins
* Change outdated LuaBridge to [sol2](https://github.com/ThePhD/sol2);
* Support GCC, Clang and MSVC compilers
* New environment variables: "TES3MP_SERVER_DIR" and "TES3MP_SERVER_USERDIR";
* New entity "Command controller" for registering new chat commands;
* New Event system
* Simplified Timer API
* All Lua mods now run in their own environments
* Add global namespace - Data that can be used for communicating between mods
* Player and Actor inherit base class NetActor
7 years ago

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