Compare commits

...

54 Commits

Author SHA1 Message Date
David Cernat 8aad93b904
Merge pull request #519 from TES3MP/0.7.0-alpha
[General] Update positions for dead players on other clients
5 years ago
David Cernat 3effd5f1ff [General] Update positions for dead players on other clients
Dead players will now show up at the correct cell and position for living players, making server scripts that allow players to revive each other much more functional.
5 years ago
David Cernat 4692f29b9d
Merge pull request #517 from uramer/0.7.0markers
update player map markers when client changes cell
5 years ago
David Cernat 03d377ec54
Merge pull request #518 from TES3MP/0.7.0-alpha
[General] Rename CellReplace packet into CellReset
5 years ago
David Cernat 8ff2d1b829 [General] Rename CellReplace packet into CellReset 5 years ago
David Cernat cb82318c36 [General] Fix problems with Utils::getArchitectureType() 5 years ago
uramer 3b2098382b update player map markers when client changes cell 5 years ago
David Cernat cb5e24e6c5
Merge pull request #516 from TES3MP/0.7.0-alpha
[Server] Add GetMillisecondsSinceServerStart() server function
5 years ago
David Cernat 91f82d845c [Server] Add GetMillisecondsSinceServerStart() server function 5 years ago
David Cernat d35026bbf5
Merge pull request #515 from TES3MP/0.7.0-alpha
0.7.0 alpha
5 years ago
David Cernat bd677726bf [Server] Add StatsFunctions that get/set damage to attributes/skills 5 years ago
David Cernat 9fc4c83858 [Client] Send skill/attribute packets when skills/attributes are damaged 5 years ago
David Cernat ece39748de [Server] Fix typo causing recursion in deprecated actor list function 5 years ago
David Cernat 5c4d3df551 [Server] Deprecate DoesFileExist(), add DoesFilePathExist() 5 years ago
David Cernat 2cdabddc0e [Server] Move most MiscellaneousFunctions to ServerFunctions 5 years ago
David Cernat b46767de6e [Server] Clean up recent additions to ServerFunctions 5 years ago
David Cernat 911079e0bc
Merge pull request #512 from TES3MP/0.7.0-alpha
0.7.0 alpha
5 years ago
David Cernat 331fa86844 [Server] Call OnServerPostInit after OnRequestDataFileList
This allows different actions to be taken in OnServerPostInit based on what the data files being used are.
5 years ago
David Cernat a0ec9dfd2e [Server] Rename OnRequestPluginList into OnRequestDataFileList 5 years ago
David Cernat 986528c67d [Server] Add error message as argument to OnServerScriptCrash 5 years ago
David Cernat 552a94a0ca [Server] Add OnServerScriptCrash script event 5 years ago
David Cernat a508a0faf8 [Server] Turn GetArguments() from ScriptFunctions into Utils function 5 years ago
David Cernat dcbc9d1831 [Client] Print cells for actor deaths 5 years ago
David Cernat 828c52138f [Documentation] Update readme and credits
According to some legal advice I've received, the "TES3MP Team" is too ambiguous of a legal entity, so – with Koncord's agreement – the copyright is now assigned specifically to us, the project's developers.
5 years ago
David Cernat 69e7d3f2a7 [Documentation] Update credits 5 years ago
David Cernat f3b8a5b909 [General] Check integrity of credits only on Windows clients
This avoids the problems that were encountered in Linux and macOS builds regarding this check while also still addressing the scenario where official Windows builds had their credits modified by people unrelated to the project.
5 years ago
David Cernat a0ad0b29bc Merge branch '0.7.0' of https://github.com/TES3MP/openmw-tes3mp into 0.7.0 5 years ago
David Cernat 222837976c [Server] Fix type name warning for Player
The warning in Visual Studio was: "'Player': type name first seen using 'class' now seen using 'struct'"
5 years ago
Koncord 77386525f2 [General] Update Travis CI 5 years ago
David Cernat c058dce346 [General] Clarify meaning of commit hash displayed on start 5 years ago
David Cernat 1df1515c7e [Client] Add logging for invalid enchantmentIds in RecordHelper 5 years ago
David Cernat 999ce857c7 [Client] Add logging for records ignored due to their invalid baseIds 5 years ago
David Cernat db7e09f441 [Client] Use more consistent logging when reading dynamic record packets 5 years ago
David Cernat 0fa116b47d [Client] Remove useless lines in RecordHelper 5 years ago
Koncord 0df32accca [Server] Fix ARM build 5 years ago
David Cernat fd40e8c971 [Client] Prevent ObjectState spam by not resending an already sent state 5 years ago
David Cernat 6e47b65205 [Client] Set attribute increases & level progress after correct packets
Originally, the PlayerSkill packet contained skills, attribute increases and level progress. In 78441c769a, the attribute increases were moved to the PlayerAttribute packet and the level progress was moved to the PlayerLevel packet, but – due to an oversight – attribute increases and level progress were still being applied to the local player only when a PlayerSkill packet was received, based on whatever values were stored from the last PlayerAttribute and PlayerLevel packets.
5 years ago
David Cernat f481c85e07 [Client] Use ADD before REMOVE for PlayerInventory in repair/recharge
Previously, when recharging or repairing an item, the client sent a PlayerInventory packet to the server with the old version of the item that was supposed to be removed and then it sent a PlayerInventory packet with the new version of the item that was supposed to be added.

Unfortunately, the current CoreScripts make it so custom items using generated IDs have their records deleted when they are completely removed from the world, however briefly, even if they are added back immediately afterwards. In practice, this meant that – before this commit – recharging or repairing a custom item led to its removal from the player inventory stored on the server, followed by the deletion of its record, followed by its readdition to the inventory (but with the record staying deleted). Logging out and logging back in immediately prevented the player from receiving the item anymore because of its now non-existent record.
5 years ago
David Cernat 8a99f215f6 [Client] Add LocalPlayer::sendItemChange() variant with mwmp::Item arg 5 years ago
David Cernat db9c1b9882 [Client] Add MechanicsHelper::getItem() for getting mwmp::Item from Ptr 5 years ago
David Cernat 799241e8c6 [Client] Use informative error message for RefData::setCount() issue 5 years ago
David Cernat 43f195f0c7 [Client] Use clearer debug for actor initializations 5 years ago
David Cernat 2e1d4a9449 [Server] Fix non-Windows builds 5 years ago
David Cernat 81e2e48561 [Client] Fix item magic casting synchronization for spell scrolls
Previously, spell scrolls were used up before their IDs could be included in attacks packets supposed to be sent for them.
5 years ago
David Cernat d83160523f [Client] Add items required for item magic casting when they are missing 5 years ago
David Cernat 433a69a588 [Client] Send all data for newly initialized LocalActors at least once 5 years ago
David Cernat e70fd2cf3a [Server] Accept clients with wrong password on servers with no password 5 years ago
David Cernat eb52babf29 [Server] Print IP instead of name or PID for players unable to connect
The player name was always blank in such situations, providing no useful information. The PID was not useful in any way either.
5 years ago
David Cernat e96091fd6b [General] Use more consistent variable names for password, address, etc. 5 years ago
David Cernat 906d2a837d [Client] Send PlayerInventory packets when recharging items w/ soulgems 5 years ago
David Cernat 71679934a1 [Client] Send PlayerInventory packets when repairing items 5 years ago
David Cernat 5d9893ee92 [Client] Set actor killer correctly for spells that do damage over time
Additionally, clean up comments related to other code that sets actor killers.
5 years ago
David Cernat 6e1504f0a1 [Server] Use clearer variable & function names in TimerAPI 5 years ago
David Cernat 42b5a8054f [Server] Remove unusable position functions for players 5 years ago

@ -4,7 +4,7 @@ os:
osx_image: xcode9.4
language: cpp
sudo: required
dist: trusty
dist: xenial
branches:
only:
- master
@ -15,18 +15,18 @@ env:
global:
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
# via the "travis encrypt" command using the project repo's public key
- secure: NZmvVuA0O9NJXVQ12tXQZHDJC2mbFgYNFcsicw0DgW1It2Nk5hxIkF0pfu4/Z59mhQuOPgRVjl5b0FKy2Axh0gkWc1DJEXGwNaiW5lpTMNWR1LJG5rxa8LrDUpFkycpbzfAFuTUZu5z3iYVv64XzELvBuqNGhPMu1LeBnrlech0jFNjkR9p5qtJGWb8zYcPMCC57rig8a9g1ABoVYS6UXjrKpx0946ZLRsE5ukc9pXsypGwPmOMyfzZkxxzIqFaxoE5JIEdaJTWba/6Za315ozYYIi/N35ROI1YAv5GHRe/Iw9XAa4vQpbDzjM7ZSsZdTvvQsSU598gD2xC6jFUKSrpW6GZKwM2x236fZLGnOk5Uw7DUbG+AwpcEmxBwoy9PjBl9ZF3tJykI0gROewCy8MODhdsVMKr1HGIMVBIJySm/RnNqtoDbYV8mYnSl5b8rwJiCajoiR8Zuv4CIfGneeH1a3DOQDPH/qkDsU6ilzF4ANsBlMUUpgY653KBMBmTlNuVZSH527tnD7Fg6JgHVuSQkTbRa1vSkR7Zcre604RZcAoaEdbX3bhVDasPPghU/I742L0RH3oQNlR09pPBDZ8kG7ydl4aPHwpCWnvXNM1vgxtGvnYLztwrse7IoaRXRYiMFmrso78WhMWUDKgvY4wV9aeUu0DtnMezZVIQwCKg=
- secure: 1QK0yVyoOB+gf2I7XzvhXu9w/5lq4stBXIwJbVCTjz4Q4XVHCosURaW1MAgKzMrPnbFEwjyn5uQ8BwsvvfkuN1AZD0YXITgc7gyI+J1wQ/p/ljxRxglakU6WEgsTs2J5z9UmGac4YTXg+quK7YP3rv+zuGim2I2rhzImejyzp0Ym3kRCnNcy+SGBsiRaevRJMe00Ch8zGAbEhduQGeSoS6W0rcu02DNlQKiq5NktWsXR+TWWWVfIeIlQR/lbPsCd0pdxMaMv2QCY0rVbwrYxWJwr/Qe45dAdWp+8/C3PbXpeMSGxlLa33nJNX4Lf/djxbjm8KWk6edaXPajrjR/0iwcpwq0jg2Jt6XfEdnJt35F1gpXlc04sxStjG45uloOKCFYT0wdhIO1Lq+hDP54wypQl+JInd5qC001O7pwhVxO36EgKWqo8HD+BqGDBwsNj2engy9Qcp3wO6G0rLBPB3CrZsk9wrHVv5cSiQSLMhId3Xviu3ZI2qEDA+kgTvxrKrsnMj4bILVCyG5Ka2Mj22wIDW9e8oIab9oTdujax3DTN1GkD6QuOAGzwDsNwGASsgfoeZ+FUhgM75RlBWGMilgkmnF7EJ0oAXLEpjtABnEr2d4qHv+y08kOuTDBLB9ExzCIj024dYYYNLZrqPKx0ncHuCMG2QNj2aJAJEZtj1rQ=
addons:
apt:
sources:
- sourceline: 'ppa:openmw/openmw'
- sourceline: 'ppa:rakhimov/boost'
- ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.8
packages: [
# Dev
cmake, clang-3.8, libunshield-dev, libtinyxml-dev,
g++-6,
cmake, clang-6.0, libunshield-dev, libtinyxml-dev,
g++-8,
# Tests
libgtest-dev, google-mock,
# Boost
@ -45,7 +45,7 @@ addons:
project:
name: "TES3MP/openmw-tes3mp"
description: "<Your project description here>"
notification_email: stas5978@gmail.com
notification_email: koncord@tes3mp.com
build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE -DBUILD_OPENCS=FALSE -DBUILD_BSATOOL=FALSE -DBUILD_ESMTOOL=FALSE -DBUILD_MWINIIMPORTER=FALSE -DBUILD_LAUNCHER=FALSE"
build_command: "make -j3"
branch_pattern: coverity_scan
@ -53,21 +53,21 @@ matrix:
include:
- os: linux
env:
- ANALYZE="scan-build-3.8 --use-cc clang-3.8 --use-c++ clang++-3.8 "
- MATRIX_CC="CC=clang-3.8 && CXX=clang++-3.8"
- ANALYZE="scan-build-6.0 --use-cc clang-6.0 --use-c++ clang++-6.0 "
- MATRIX_CC="CC=clang-6.0 && CXX=clang++-6.0"
compiler: clang
- os: linux
env:
- MATRIX_CC="CC=gcc-6 && CXX=g++-6"
- MATRIX_CC="CC=gcc-8 && CXX=g++-8"
- os: linux
env:
- MATRIX_CC="CC=clang-3.8 && CXX=clang++-3.8"
- MATRIX_CC="CC=clang-6.0 && CXX=clang++-6.0"
allow_failures:
- env:
- MATRIX_CC="CC=clang-3.8 && CXX=clang++-3.8"
- MATRIX_CC="CC=clang-6.0 && CXX=clang++-6.0"
- env:
- ANALYZE="scan-build-3.8 --use-cc clang-3.8 --use-c++ clang++-3.8 "
- MATRIX_CC="CC=clang-3.8 && CXX=clang++-3.8"
- ANALYZE="scan-build-6.0 --use-cc clang-6.0 --use-c++ clang++-6.0 "
- MATRIX_CC="CC=clang-6.0 && CXX=clang++-6.0"
before_install:
- ./CI/before_install.${TRAVIS_OS_NAME}.sh

@ -15,19 +15,8 @@ sudo ln -s /usr/src/gtest/build/libgtest.so /usr/lib/libgtest.so
sudo ln -s /usr/src/gtest/build/libgtest_main.so /usr/lib/libgtest_main.so
cd ~/
git clone https://github.com/TES3MP/RakNet
cd RakNet
cmake . -DRAKNET_ENABLE_DLL=OFF -DRAKNET_ENABLE_SAMPLES=OFF -DCMAKE_BUILD_TYPE=Release
git clone https://github.com/TES3MP/CrabNet
cd CrabNet
cmake . -DCRABNET_ENABLE_DLL=OFF -DCRABNET_ENABLE_SAMPLES=OFF -DCMAKE_BUILD_TYPE=Release
make -j3
cd ~/
git clone https://github.com/Koncord/CallFF
cd CallFF
mkdir build
cd build
cmake ../
make -j3
cd ~/
wget https://github.com/zdevito/terra/releases/download/release-2016-03-25/terra-Linux-x86_64-332a506.zip
unzip terra-Linux-x86_64-332a506.zip

@ -9,8 +9,7 @@ if [ ! -z "${MATRIX_CC}" ]; then
eval "${MATRIX_CC}"
fi
export RAKNET_ROOT=~/RakNet
export Terra_ROOT=~/terra-Linux-x86_64-332a506
export RAKNET_ROOT=~/CrabNet
export CODE_COVERAGE=0
if [ ! -z "${ANALYZE}" ]; then
@ -36,7 +35,5 @@ ${ANALYZE}cmake .. \
-DBINDIR=/usr/games \
-DCMAKE_BUILD_TYPE="None" \
-DUSE_SYSTEM_TINYXML=TRUE \
-DRakNet_LIBRARY_RELEASE=~/RakNet/lib/libRakNetLibStatic.a \
-DRakNet_LIBRARY_DEBUG=~/RakNet/lib/libRakNetLibStatic.a \
-DCallFF_INCLUDES=~/CallFF/include \
-DCallFF_LIBRARY=~/CallFF/build/src/libcallff.a
-DRakNet_LIBRARY_RELEASE=~/CrabNet/lib/libRakNetLibStatic.a \
-DRakNet_LIBRARY_DEBUG=~/CrabNet/lib/libRakNetLibStatic.a

@ -2,7 +2,7 @@ TES3MP
======
Copyright (c) 2008-2015, OpenMW Team
Copyright (c) 2016-2018, TES3MP Team
Copyright (c) 2016-2019, Stanislav Zhukov & David Cernat
[![Build Status](https://travis-ci.org/TES3MP/openmw-tes3mp.svg?branch=0.7.0)](https://travis-ci.org/TES3MP/openmw-tes3mp)
@ -15,7 +15,7 @@ TES3MP is a project adding multiplayer functionality to [OpenMW](https://github.
Font Licenses:
* DejaVuLGCSansMono.ttf: custom (see [files/mygui/DejaVu Font License.txt](https://github.com/TES3MP/openmw-tes3mp/blob/master/files/mygui/DejaVu%20Font%20License.txt) for more information)
Project Status
Project status
--------------
[Version changelog](https://github.com/TES3MP/openmw-tes3mp/blob/master/tes3mp-changelog.md)
@ -24,25 +24,26 @@ As of version 0.7.0, TES3MP is fully playable, providing very extensive player,
Remaining gameplay problems mostly relate to AI and the synchronization of clientside script variables.
Donations
---------------
You can benefit the project by donating on Patreon to our two developers, [David Cernat](https://www.patreon.com/davidcernat) and [Koncord](https://www.patreon.com/Koncord), as well as by supporting [OpenMW](https://openmw.org).
Contributing
--------------
---------------
Development has been relatively fast, but any contribution regarding [code](https://github.com/TES3MP/openmw-tes3mp/blob/master/CONTRIBUTING.md), documentation, bug hunting or video showcases is greatly appreciated.
Helping us with documentation, bug hunting and video showcases is always greatly appreciated.
Test sessions are often advertised on [our Discord server](https://discord.gg/ECJk293) or in [our Steam group](https://steamcommunity.com/groups/mwmulti).
For code contributions, it's best to start out with modestly sized fixes and features and work your way up. There are so many different possible implementations of more major features many of which would cause undesirable code or vision conflicts with OpenMW that those should be talked over in advance with the existing developers before effort is spent on them.
Feel free to contact the [team members](https://github.com/TES3MP/openmw-tes3mp/blob/master/tes3mp-credits.md) for any questions you might have.
Getting Started
Getting started
---------------
* [Quickstart guide](https://github.com/TES3MP/openmw-tes3mp/wiki/Quickstart-guide)
* [Steam group](https://steamcommunity.com/groups/mwmulti) and its [detailed FAQ](https://steamcommunity.com/groups/mwmulti/discussions/1/353916184342480541/)
* [TES3MP section on OpenMW forums](https://forum.openmw.org/viewforum.php?f=45)
* [Discord server](https://discord.gg/ECJk293)
* [Subreddit](https://www.reddit.com/r/tes3mp)
* [Known issues and bug reports](https://github.com/TES3MP/openmw-tes3mp/issues)
Donations
---------------
You can benefit the project by contributing to the Patreon pages of our two developers, [David Cernat](https://www.patreon.com/davidcernat) and [Koncord](https://www.patreon.com/Koncord), as well as by supporting [OpenMW](https://openmw.org).

@ -75,9 +75,9 @@ Networking::~Networking()
delete worldstatePacketController;
}
void Networking::setServerPassword(std::string passw) noexcept
void Networking::setServerPassword(std::string password) noexcept
{
serverPassword = passw.empty() ? TES3MP_DEFAULT_PASSW : passw;
serverPassword = password.empty() ? TES3MP_DEFAULT_PASSW : password;
}
bool Networking::isPassworded() const
@ -98,25 +98,32 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
if (!myPacket->isPacketValid())
{
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Invalid handshake packet from %d", player->getId());
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Invalid handshake packet from client at %s", packet->systemAddress.ToString());
kickPlayer(player->guid);
return;
}
if (player->isHandshaked())
{
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Wrong handshake with player %d, name: %s", player->getId(),
player->npc.mName.c_str());
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Wrong handshake with client at %s", packet->systemAddress.ToString());
kickPlayer(player->guid);
return;
}
if (player->passw != serverPassword)
if (player->serverPassword != serverPassword)
{
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Wrong server password for player %d, name: %s (pass: %s)",
player->getId(), player->npc.mName.c_str(), player->passw.c_str());
kickPlayer(player->guid);
return;
if (isPassworded())
{
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Wrong server password %s used by client at %s",
player->serverPassword.c_str(), packet->systemAddress.ToString());
kickPlayer(player->guid);
return;
}
else
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Client at %s tried to join using password, despite the server not being passworded",
packet->systemAddress.ToString());
}
}
player->setHandshake();
return;
@ -125,9 +132,8 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
if (!player->isHandshaked())
{
player->incrementHandshakeAttempts();
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Have not completed handshake with player %d", player->getId());
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Attempts so far: %i", player->getHandshakeAttempts());
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Have not completed handshake with client at %s", packet->systemAddress.ToString());
LOG_APPEND(Log::LOG_WARN, "- Attempts so far: %i", player->getHandshakeAttempts());
if (player->getHandshakeAttempts() > 20)
kickPlayer(player->guid, false);
@ -579,8 +585,8 @@ void Networking::InitQuery(std::string queryAddr, unsigned short queryPort)
void Networking::postInit()
{
Script::Call<Script::CallbackIdentity("OnRequestDataFileList")>();
Script::Call<Script::CallbackIdentity("OnServerPostInit")>();
Script::Call<Script::CallbackIdentity("OnRequestPluginList")>();
}
PacketPreInit::PluginContainer &Networking::getSamples()

@ -21,7 +21,6 @@
#include "Cell.hpp"
#include "CellController.hpp"
struct Player;
typedef std::map<RakNet::RakNetGUID, Player*> TPlayers;
typedef std::map<unsigned short, Player*> TSlots;

@ -1,7 +1,3 @@
//
// Created by koncord on 15.03.16.
//
#include "TimerAPI.hpp"
#include <chrono>
@ -14,7 +10,7 @@ Timer::Timer(ScriptFunc callback, long msec, const std::string& def, std::vector
{
targetMsec = msec;
this->args = args;
end = true;
isEnded = true;
}
#if defined(ENABLE_LUA)
@ -22,13 +18,13 @@ Timer::Timer(lua_State *lua, ScriptFuncLua callback, long msec, const std::strin
{
targetMsec = msec;
this->args = args;
end = true;
isEnded = true;
}
#endif
void Timer::Tick()
{
if (end)
if (isEnded)
return;
const auto duration = chrono::system_clock::now().time_since_epoch();
@ -36,19 +32,19 @@ void Timer::Tick()
if (time - startTime >= targetMsec)
{
end = true;
isEnded = true;
Call(args);
}
}
bool Timer::IsEnd()
bool Timer::IsEnded()
{
return end;
return isEnded;
}
void Timer::Stop()
{
end = true;
isEnded = true;
}
void Timer::Restart(int msec)
@ -59,7 +55,7 @@ void Timer::Restart(int msec)
void Timer::Start()
{
end = false;
isEnded = false;
const auto duration = chrono::system_clock::now().time_since_epoch();
const auto msec = chrono::duration_cast<chrono::milliseconds>(duration).count();
@ -172,12 +168,12 @@ void TimerAPI::StopTimer(int timerid)
}
}
bool TimerAPI::IsEndTimer(int timerid)
bool TimerAPI::IsTimerElapsed(int timerid)
{
bool ret = false;
try
{
ret = timers.at(timerid)->IsEnd();
ret = timers.at(timerid)->IsEnded();
}
catch(...)
{

@ -1,7 +1,3 @@
//
// Created by koncord on 15.03.16.
//
#ifndef OPENMW_TIMERAPI_HPP
#define OPENMW_TIMERAPI_HPP
@ -27,7 +23,7 @@ namespace mwmp
#endif
void Tick();
bool IsEnd();
bool IsEnded();
void Stop();
void Start();
void Restart(int msec);
@ -36,7 +32,7 @@ namespace mwmp
std::string publ, arg_types;
std::vector<boost::any> args;
Script *scr;
bool end;
bool isEnded;
};
class TimerAPI
@ -50,7 +46,7 @@ namespace mwmp
static void ResetTimer(int timerid, long msec);
static void StartTimer(int timerid);
static void StopTimer(int timerid);
static bool IsEndTimer(int timerid);
static bool IsTimerElapsed(int timerid);
static void Terminate();

@ -537,7 +537,7 @@ void ActorFunctions::InitializeActorList(unsigned short pid) noexcept
void ActorFunctions::CopyLastActorListToStore() noexcept
{
CopyLastActorListToStore();
CopyReceivedActorListToStore();
}
unsigned int ActorFunctions::GetActorRefNumIndex(unsigned int index) noexcept

@ -1,38 +1,12 @@
#include "Miscellaneous.hpp"
#include <components/misc/stringops.hpp>
#include <components/openmw-mp/Log.hpp>
#include <apps/openmw-mp/Script/ScriptFunctions.hpp>
#include <apps/openmw-mp/Networking.hpp>
#include <iostream>
using namespace std;
static std::string tempFilename;
bool MiscellaneousFunctions::DoesFileExist(const char *filePath) noexcept
{
return boost::filesystem::exists(filePath);
}
const char *MiscellaneousFunctions::GetCaseInsensitiveFilename(const char *folderPath, const char *filename) noexcept
{
if (!boost::filesystem::exists(folderPath)) return "invalid";
boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end
for (boost::filesystem::directory_iterator itr(folderPath); itr != end_itr; ++itr)
{
if (Misc::StringUtils::ciEqual(itr->path().filename().string(), filename))
{
tempFilename = itr->path().filename().string();
return tempFilename.c_str();
}
}
return "invalid";
}
unsigned int MiscellaneousFunctions::GetLastPlayerId() noexcept
{
return Players::getLastPlayerId();
@ -47,13 +21,3 @@ 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);
}

@ -4,42 +4,15 @@
#include "../Types.hpp"
#define MISCELLANEOUSAPI \
{"DoesFileExist", MiscellaneousFunctions::DoesFileExist},\
{"GetCaseInsensitiveFilename", MiscellaneousFunctions::GetCaseInsensitiveFilename},\
\
{"GetLastPlayerId", MiscellaneousFunctions::GetLastPlayerId},\
\
{"GetCurrentMpNum", MiscellaneousFunctions::GetCurrentMpNum},\
{"SetCurrentMpNum", MiscellaneousFunctions::SetCurrentMpNum},\
\
{"LogMessage", MiscellaneousFunctions::LogMessage},\
{"LogAppend", MiscellaneousFunctions::LogAppend}
{"SetCurrentMpNum", MiscellaneousFunctions::SetCurrentMpNum}
class MiscellaneousFunctions
{
public:
/**
* \brief Check whether a certain file exists.
*
* This will be a case sensitive check on case sensitive filesystems.
*
* Whenever you want to enforce case insensitivity, use GetCaseInsensitiveFilename() instead.
*
* \return Whether the file exists or not.
*/
static bool DoesFileExist(const char *filePath) noexcept;
/**
* \brief Get the first filename in a folder that has a case insensitive match with the filename
* argument.
*
* This is used to retain case insensitivity when opening data files on Linux.
*
* \return The filename that matches.
*/
static const char *GetCaseInsensitiveFilename(const char *folderPath, const char *filename) noexcept;
/**
* \brief Get the last player ID currently connected to the server.
*
@ -75,30 +48,6 @@ public:
* \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

@ -7,20 +7,6 @@
#include <iostream>
using namespace std;
void PositionFunctions::GetPos(unsigned short pid, float *x, float *y, float *z) noexcept
{
*x = 0.00;
*y = 0.00;
*z = 0.00;
Player *player;
GET_PLAYER(pid, player,);
*x = player->position.pos[0];
*y = player->position.pos[1];
*z = player->position.pos[2];
}
double PositionFunctions::GetPosX(unsigned short pid) noexcept
{
Player *player;
@ -69,20 +55,6 @@ double PositionFunctions::GetPreviousCellPosZ(unsigned short pid) noexcept
return player->previousCellPosition.pos[2];
}
void PositionFunctions::GetRot(unsigned short pid, float *x, float *y, float *z) noexcept
{
*x = 0.00;
*y = 0.00;
*z = 0.00;
Player *player;
GET_PLAYER(pid, player, );
*x = player->position.rot[0];
*y = player->position.rot[1];
*z = player->position.rot[2];
}
double PositionFunctions::GetRotX(unsigned short pid) noexcept
{
Player *player;

@ -4,7 +4,6 @@
#include "../Types.hpp"
#define POSITIONAPI \
{"GetPos", PositionFunctions::GetPos},\
{"GetPosX", PositionFunctions::GetPosX},\
{"GetPosY", PositionFunctions::GetPosY},\
{"GetPosZ", PositionFunctions::GetPosZ},\
@ -13,7 +12,6 @@
{"GetPreviousCellPosY", PositionFunctions::GetPreviousCellPosY},\
{"GetPreviousCellPosZ", PositionFunctions::GetPreviousCellPosZ},\
\
{"GetRot", PositionFunctions::GetRot},\
{"GetRotX", PositionFunctions::GetRotX},\
{"GetRotZ", PositionFunctions::GetRotZ},\
\
@ -29,18 +27,6 @@ class PositionFunctions
{
public:
/**
* \brief Assign the player's positional coordinate values to the variables passed as
* parameters.
*
* \param pid The player ID.
* \param x The variable for the X position.
* \param y The variable for the Y position.
* \param z The variable for the Z position.
* \return void
*/
static void GetPos(unsigned short pid, float *x, float *y, float *z) noexcept;
/**
* \brief Get the X position of a player.
*
@ -89,18 +75,6 @@ public:
*/
static double GetPreviousCellPosZ(unsigned short pid) noexcept;
/**
* \brief Assign the player's rotational coordinate values to the variables passed as
* parameters.
*
* \param pid The player ID.
* \param x The variable for the X rotation.
* \param y The variable for the Y rotation.
* \param z The variable for the Z rotation.
* \return void
*/
static void GetRot(unsigned short pid, float *x, float *y, float *z) noexcept;
/**
* \brief Get the X rotation of a player.
*

@ -1,5 +1,6 @@
#include "Server.hpp"
#include <components/misc/stringops.hpp>
#include <components/openmw-mp/NetworkMessages.hpp>
#include <components/openmw-mp/Log.hpp>
#include <components/openmw-mp/Version.hpp>
@ -9,6 +10,18 @@
#include <apps/openmw-mp/MasterClient.hpp>
#include <Script/Script.hpp>
static std::string tempFilename;
static std::chrono::high_resolution_clock::time_point startupTime = std::chrono::high_resolution_clock::now();
void ServerFunctions::LogMessage(unsigned short level, const char *message) noexcept
{
LOG_MESSAGE_SIMPLE(level, "[Script]: %s", message);
}
void ServerFunctions::LogAppend(unsigned short level, const char *message) noexcept
{
LOG_APPEND(level, "[Script]: %s", message);
}
void ServerFunctions::StopServer(int code) noexcept
{
@ -35,6 +48,40 @@ void ServerFunctions::UnbanAddress(const char *ipAddress) noexcept
mwmp::Networking::getPtr()->unbanAddress(ipAddress);
}
bool ServerFunctions::DoesFilePathExist(const char *filePath) noexcept
{
return boost::filesystem::exists(filePath);
}
const char *ServerFunctions::GetCaseInsensitiveFilename(const char *folderPath, const char *filename) noexcept
{
if (!boost::filesystem::exists(folderPath)) return "invalid";
boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end
for (boost::filesystem::directory_iterator itr(folderPath); itr != end_itr; ++itr)
{
if (Misc::StringUtils::ciEqual(itr->path().filename().string(), filename))
{
tempFilename = itr->path().filename().string();
return tempFilename.c_str();
}
}
return "invalid";
}
const char* ServerFunctions::GetDataPath() noexcept
{
return Script::GetModDir();
}
unsigned int ServerFunctions::GetMillisecondsSinceServerStart() noexcept
{
std::chrono::high_resolution_clock::time_point currentTime = std::chrono::high_resolution_clock::now();
std::chrono::milliseconds milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - startupTime);
return milliseconds.count();
}
const char *ServerFunctions::GetOperatingSystemType() noexcept
{
return Utils::getOperatingSystemType().c_str();
@ -137,34 +184,46 @@ void ServerFunctions::SetRuleValue(const char *key, double value) noexcept
mc->SetRuleValue(key, value);
}
void ServerFunctions::AddPluginHash(const char *pluginName, const char *hashStr) noexcept
void ServerFunctions::AddDataFileRequirement(const char *dataFilename, const char *checksumString) noexcept
{
auto &samples = mwmp::Networking::getPtr()->getSamples();
auto it = std::find_if(samples.begin(), samples.end(), [&pluginName](mwmp::PacketPreInit::PluginPair &item) {
return item.first == pluginName;
auto it = std::find_if(samples.begin(), samples.end(), [&dataFilename](mwmp::PacketPreInit::PluginPair &item) {
return item.first == dataFilename;
});
if (it != samples.end())
it->second.push_back((unsigned) std::stoul(hashStr));
it->second.push_back((unsigned) std::stoul(checksumString));
else
{
mwmp::PacketPreInit::HashList hashList;
mwmp::PacketPreInit::HashList checksumList;
unsigned hash = 0;
unsigned checksum = 0;
if (strlen(hashStr) != 0)
if (strlen(checksumString) != 0)
{
hash = (unsigned) std::stoul(hashStr);
hashList.push_back(hash);
checksum = (unsigned) std::stoul(checksumString);
checksumList.push_back(checksum);
}
samples.emplace_back(pluginName, hashList);
samples.emplace_back(dataFilename, checksumList);
auto mclient = mwmp::Networking::getPtr()->getMasterClient();
if (mclient)
mclient->PushPlugin({pluginName, hash});
mclient->PushPlugin({dataFilename, checksum});
}
}
// All methods below are deprecated versions of methods from above
bool ServerFunctions::DoesFileExist(const char *filePath) noexcept
{
return DoesFilePathExist(filePath);
}
const char* ServerFunctions::GetModDir() noexcept
{
return Script::GetModDir();
return GetDataPath();
}
void ServerFunctions::AddPluginHash(const char *pluginName, const char *checksumString) noexcept
{
AddDataFileRequirement(pluginName, checksumString);
}

@ -4,38 +4,73 @@
#include "../Types.hpp"
#define SERVERAPI \
{"StopServer", ServerFunctions::StopServer},\
{"LogMessage", ServerFunctions::LogMessage},\
{"LogAppend", ServerFunctions::LogAppend},\
\
{"Kick", ServerFunctions::Kick},\
{"BanAddress", ServerFunctions::BanAddress},\
{"UnbanAddress", ServerFunctions::UnbanAddress},\
{"StopServer", ServerFunctions::StopServer},\
\
{"GetOperatingSystemType", ServerFunctions::GetOperatingSystemType},\
{"GetArchitectureType", ServerFunctions::GetArchitectureType},\
{"GetServerVersion", ServerFunctions::GetServerVersion},\
{"GetProtocolVersion", ServerFunctions::GetProtocolVersion},\
{"GetAvgPing", ServerFunctions::GetAvgPing},\
{"GetIP", ServerFunctions::GetIP},\
{"GetMaxPlayers", ServerFunctions::GetMaxPlayers},\
{"GetPort", ServerFunctions::GetPort},\
{"HasPassword", ServerFunctions::HasPassword},\
{"GetPluginEnforcementState", ServerFunctions::GetPluginEnforcementState},\
{"GetScriptErrorIgnoringState", ServerFunctions::GetScriptErrorIgnoringState},\
{"Kick", ServerFunctions::Kick},\
{"BanAddress", ServerFunctions::BanAddress},\
{"UnbanAddress", ServerFunctions::UnbanAddress},\
\
{"SetGameMode", ServerFunctions::SetGameMode},\
{"SetHostname", ServerFunctions::SetHostname},\
{"SetServerPassword", ServerFunctions::SetServerPassword},\
{"SetPluginEnforcementState", ServerFunctions::SetPluginEnforcementState},\
{"SetScriptErrorIgnoringState", ServerFunctions::SetScriptErrorIgnoringState},\
{"SetRuleString", ServerFunctions::SetRuleString},\
{"SetRuleValue", ServerFunctions::SetRuleValue},\
{"AddPluginHash", ServerFunctions::AddPluginHash},\
{"GetModDir", ServerFunctions::GetModDir}
{"DoesFilePathExist", ServerFunctions::DoesFilePathExist},\
{"GetCaseInsensitiveFilename", ServerFunctions::GetCaseInsensitiveFilename},\
{"GetDataPath", ServerFunctions::GetDataPath},\
{"GetMillisecondsSinceServerStart", ServerFunctions::GetMillisecondsSinceServerStart},\
{"GetOperatingSystemType", ServerFunctions::GetOperatingSystemType},\
{"GetArchitectureType", ServerFunctions::GetArchitectureType},\
{"GetServerVersion", ServerFunctions::GetServerVersion},\
{"GetProtocolVersion", ServerFunctions::GetProtocolVersion},\
{"GetAvgPing", ServerFunctions::GetAvgPing},\
{"GetIP", ServerFunctions::GetIP},\
{"GetMaxPlayers", ServerFunctions::GetMaxPlayers},\
{"GetPort", ServerFunctions::GetPort},\
{"HasPassword", ServerFunctions::HasPassword},\
{"GetPluginEnforcementState", ServerFunctions::GetPluginEnforcementState},\
{"GetScriptErrorIgnoringState", ServerFunctions::GetScriptErrorIgnoringState},\
\
{"SetGameMode", ServerFunctions::SetGameMode},\
{"SetHostname", ServerFunctions::SetHostname},\
{"SetServerPassword", ServerFunctions::SetServerPassword},\
{"SetPluginEnforcementState", ServerFunctions::SetPluginEnforcementState},\
{"SetScriptErrorIgnoringState", ServerFunctions::SetScriptErrorIgnoringState},\
{"SetRuleString", ServerFunctions::SetRuleString},\
{"SetRuleValue", ServerFunctions::SetRuleValue},\
\
{"AddDataFileRequirement", ServerFunctions::AddDataFileRequirement},\
\
{"DoesFileExist", ServerFunctions::DoesFileExist},\
{"GetModDir", ServerFunctions::GetModDir},\
{"AddPluginHash", ServerFunctions::AddPluginHash}
class ServerFunctions
{
public:
/**
* \brief Write a log message with its own timestamp.
*
* It will have "[Script]:" prepended to it so as to mark it as a script-generated log message.
*
* \param level The logging level used (0 for LOG_VERBOSE, 1 for LOG_INFO, 2 for LOG_WARN,
* 3 for LOG_ERROR, 4 for LOG_FATAL).
* \param message The message logged.
* \return void
*/
static void LogMessage(unsigned short level, const char *message) noexcept;
/**
* \brief Write a log message without its own timestamp.
*
* It will have "[Script]:" prepended to it so as to mark it as a script-generated log message.
*
* \param level The logging level used (0 for LOG_VERBOSE, 1 for LOG_INFO, 2 for LOG_WARN,
* 3 for LOG_ERROR, 4 for LOG_FATAL).
* \param message The message logged.
* \return void
*/
static void LogAppend(unsigned short level, const char *message) noexcept;
/**
* \brief Shut down the server.
*
@ -68,6 +103,41 @@ public:
*/
static void UnbanAddress(const char *ipAddress) noexcept;
/**
* \brief Check whether a certain file path exists.
*
* This will be a case sensitive check on case sensitive filesystems.
*
* Whenever you want to enforce case insensitivity, use GetCaseInsensitiveFilename() instead.
*
* \return Whether the file exists or not.
*/
static bool DoesFilePathExist(const char *filePath) noexcept;
/**
* \brief Get the first filename in a folder that has a case insensitive match with the filename
* argument.
*
* This is used to retain case insensitivity when opening data files on Linux.
*
* \return The filename that matches.
*/
static const char *GetCaseInsensitiveFilename(const char *folderPath, const char *filename) noexcept;
/**
* \brief Get the path of the server's data folder.
*
* \return The data path.
*/
static const char *GetDataPath() noexcept;
/**
* \brief Get the milliseconds elapsed since the server was started.
*
* \return The time since the server's startup in milliseconds.
*/
static unsigned int GetMillisecondsSinceServerStart() noexcept;
/**
* \brief Get the type of the operating system used by the server.
*
@ -119,7 +189,7 @@ public:
/**
* \brief Get the port used by the server.
*
* \return Port
* \return The port.
*/
static unsigned short GetPort() noexcept;
@ -220,13 +290,24 @@ public:
static void SetRuleValue(const char *key, double value) noexcept;
/**
* \brief Adds plugins to the internal server structure to validate players.
* @param pluginName Name with extension of the plugin or master file.
* @param hash Hash string
* \brief Add a data file and a corresponding CRC32 checksum to the data file loadout
* that connecting clients need to match.
*
* It can be used multiple times to set multiple checksums for the same data file.
*
* Note: If an empty string is provided for the checksum, a checksum will not be
* required for that data file.
*
* @param dataFilename The filename of the data file.
* @param checksumString A string with the CRC32 checksum required.
*/
static void AddPluginHash(const char *pluginName, const char *hash) noexcept;
static void AddDataFileRequirement(const char *dataFilename, const char *checksumString) noexcept;
// All methods below are deprecated versions of methods from above
static bool DoesFileExist(const char *filePath) noexcept;
static const char *GetModDir() noexcept;
static void AddPluginHash(const char *pluginName, const char *checksumString) noexcept;
};
#endif //OPENMW_SERVERAPI_HPP

@ -201,6 +201,17 @@ int StatsFunctions::GetAttributeModifier(unsigned short pid, unsigned short attr
return player->creatureStats.mAttributes[attributeId].mMod;
}
double StatsFunctions::GetAttributeDamage(unsigned short pid, unsigned short attributeId) noexcept
{
Player *player;
GET_PLAYER(pid, player, 0);
if (attributeId >= Attribute::Length)
return 0;
return player->creatureStats.mAttributes[attributeId].mDamage;
}
int StatsFunctions::GetSkillBase(unsigned short pid, unsigned short skillId) noexcept
{
Player *player;
@ -223,6 +234,17 @@ int StatsFunctions::GetSkillModifier(unsigned short pid, unsigned short skillId)
return player->npcStats.mSkills[skillId].mMod;
}
double StatsFunctions::GetSkillDamage(unsigned short pid, unsigned short skillId) noexcept
{
Player *player;
GET_PLAYER(pid, player, 0);
if (skillId >= Skill::Length)
return 0;
return player->npcStats.mSkills[skillId].mDamage;
}
double StatsFunctions::GetSkillProgress(unsigned short pid, unsigned short skillId) noexcept
{
Player *player;
@ -437,6 +459,20 @@ void StatsFunctions::ClearAttributeModifier(unsigned short pid, unsigned short a
player->attributeIndexChanges.push_back(attributeId);
}
void StatsFunctions::SetAttributeDamage(unsigned short pid, unsigned short attributeId, double value) noexcept
{
Player *player;
GET_PLAYER(pid, player, );
if (attributeId >= Attribute::Length)
return;
player->creatureStats.mAttributes[attributeId].mDamage = value;
if (!Utils::vectorContains(player->attributeIndexChanges, attributeId))
player->attributeIndexChanges.push_back(attributeId);
}
void StatsFunctions::SetSkillBase(unsigned short pid, unsigned short skillId, int value) noexcept
{
Player *player;
@ -465,6 +501,20 @@ void StatsFunctions::ClearSkillModifier(unsigned short pid, unsigned short skill
player->skillIndexChanges.push_back(skillId);
}
void StatsFunctions::SetSkillDamage(unsigned short pid, unsigned short skillId, double value) noexcept
{
Player *player;
GET_PLAYER(pid, player, );
if (skillId >= Skill::Length)
return;
player->npcStats.mSkills[skillId].mDamage = value;
if (!Utils::vectorContains(player->skillIndexChanges, skillId))
player->skillIndexChanges.push_back(skillId);
}
void StatsFunctions::SetSkillProgress(unsigned short pid, unsigned short skillId, double value) noexcept
{
Player *player;

@ -30,9 +30,11 @@
\
{"GetAttributeBase", StatsFunctions::GetAttributeBase},\
{"GetAttributeModifier", StatsFunctions::GetAttributeModifier},\
{"GetAttributeDamage", StatsFunctions::GetAttributeDamage},\
\
{"GetSkillBase", StatsFunctions::GetSkillBase},\
{"GetSkillModifier", StatsFunctions::GetSkillModifier},\
{"GetSkillDamage", StatsFunctions::GetSkillDamage},\
{"GetSkillProgress", StatsFunctions::GetSkillProgress},\
{"GetSkillIncrease", StatsFunctions::GetSkillIncrease},\
\
@ -58,9 +60,11 @@
\
{"SetAttributeBase", StatsFunctions::SetAttributeBase},\
{"ClearAttributeModifier", StatsFunctions::ClearAttributeModifier},\
{"SetAttributeDamage", StatsFunctions::SetAttributeDamage},\
\
{"SetSkillBase", StatsFunctions::SetSkillBase},\
{"ClearSkillModifier", StatsFunctions::ClearSkillModifier},\
{"SetSkillDamage", StatsFunctions::SetSkillDamage},\
{"SetSkillProgress", StatsFunctions::SetSkillProgress},\
{"SetSkillIncrease", StatsFunctions::SetSkillIncrease},\
\
@ -267,6 +271,16 @@ public:
*/
static int GetAttributeModifier(unsigned short pid, unsigned short attributeId) noexcept;
/**
* \brief Get the amount of damage (as caused through the Damage Attribute effect)
* to a player's attribute.
*
* \param pid The player ID.
* \param attributeId The attribute ID.
* \return The amount of damage to the attribute.
*/
static double GetAttributeDamage(unsigned short pid, unsigned short attributeId) noexcept;
/**
* \brief Get the base value of a player's skill.
*
@ -285,6 +299,16 @@ public:
*/
static int GetSkillModifier(unsigned short pid, unsigned short skillId) noexcept;
/**
* \brief Get the amount of damage (as caused through the Damage Skill effect)
* to a player's skill.
*
* \param pid The player ID.
* \param skillId The skill ID.
* \return The amount of damage to the skill.
*/
static double GetSkillDamage(unsigned short pid, unsigned short skillId) noexcept;
/**
* \brief Get the progress the player has made towards increasing a certain skill by 1.
*
@ -477,6 +501,17 @@ public:
*/
static void ClearAttributeModifier(unsigned short pid, unsigned short attributeId) noexcept;
/**
* \brief Set the amount of damage (as caused through the Damage Attribute effect) to
* a player's attribute.
*
* \param pid The player ID.
* \param attributeId The attribute ID.
* \param value The amount of damage to the player's attribute.
* \return void
*/
static void SetAttributeDamage(unsigned short pid, unsigned short attributeId, double value) noexcept;
/**
* \brief Set the base value of a player's skill.
*
@ -501,6 +536,17 @@ public:
*/
static void ClearSkillModifier(unsigned short pid, unsigned short skillId) noexcept;
/**
* \brief Set the amount of damage (as caused through the Damage Skill effect) to
* a player's skill.
*
* \param pid The player ID.
* \param skillId The skill ID.
* \param value The amount of damage to the player's skill.
* \return void
*/
static void SetSkillDamage(unsigned short pid, unsigned short skillId, double value) noexcept;
/**
* \brief Set the progress the player has made towards increasing a certain skill by 1.
*

@ -21,7 +21,7 @@ int ScriptFunctions::CreateTimerEx(ScriptFunc callback, int msec, const char *ty
try
{
vector<boost::any> params;
GetArguments(params, args, types);
Utils::getArguments(params, args, types);
return mwmp::TimerAPI::CreateTimer(callback, msec, types, params);
}
@ -54,5 +54,5 @@ void ScriptFunctions::FreeTimer(int timerId) noexcept
bool ScriptFunctions::IsTimerElapsed(int timerId) noexcept
{
return TimerAPI::IsEndTimer(timerId);
return TimerAPI::IsTimerElapsed(timerId);
}

@ -107,6 +107,32 @@ template<> struct F_<1> { static constexpr LuaFuctionData F{"CreateTimerEx", Lan
template<> struct F_<2> { static constexpr LuaFuctionData F{"MakePublic", LangLua::MakePublic}; };
template<> struct F_<3> { static constexpr LuaFuctionData F{"CallPublic", LangLua::CallPublic}; };
#ifdef __arm__
template<std::size_t... Is>
struct indices {};
template<std::size_t N, std::size_t... Is>
struct build_indices : build_indices<N-1, N-1, Is...> {};
template<std::size_t... Is>
struct build_indices<0, Is...> : indices<Is...> {};
template<std::size_t N>
using IndicesFor = build_indices<N>;
template<size_t... Indices>
LuaFuctionData *functions(indices<Indices...>)
{
static LuaFuctionData functions_[sizeof...(Indices)]{
F_<Indices>::F...
};
static_assert(
sizeof(functions_) / sizeof(functions_[0]) ==
sizeof(ScriptFunctions::functions) / sizeof(ScriptFunctions::functions[0]),
"Not all functions have been mapped to Lua");
return functions_;
}
#else
template<unsigned int I>
struct C
{
@ -117,7 +143,6 @@ struct C
}
};
template<>
struct C<0>
{
@ -141,6 +166,7 @@ LuaFuctionData *functions()
return functions_;
}
#endif
void LangLua::LoadProgram(const char *filename)
{
@ -152,8 +178,11 @@ void LangLua::LoadProgram(const char *filename)
constexpr auto functions_n = sizeof(ScriptFunctions::functions) / sizeof(ScriptFunctions::functions[0]);
#if __arm__
LuaFuctionData *functions_ = functions(IndicesFor<functions_n>{});
#else
LuaFuctionData *functions_ = functions<sizeof(ScriptFunctions::functions) / sizeof(ScriptFunctions::functions[0])>();
#endif
luabridge::Namespace tes3mp = luabridge::getGlobalNamespace(lua).beginNamespace("tes3mp");
for (unsigned i = 0; i < functions_n; i++)

@ -105,6 +105,7 @@ public:
catch (std::exception &e)
{
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, e.what());
Script::Call<Script::CallbackIdentity("OnServerScriptCrash")>(e.what());
if (!mwmp::Networking::getPtr()->getScriptErrorIgnoringState())
throw;

@ -13,62 +13,6 @@ constexpr ScriptCallbackData ScriptFunctions::callbacks[];
using namespace std;
void ScriptFunctions::GetArguments(std::vector<boost::any> &params, va_list args, const std::string &def)
{
params.reserve(def.length());
try
{
for (char c : def)
{
switch (c)
{
case 'i':
params.emplace_back(va_arg(args, unsigned int));
break;
case 'q':
params.emplace_back(va_arg(args, signed int));
break;
case 'l':
params.emplace_back(va_arg(args, unsigned long long));
break;
case 'w':
params.emplace_back(va_arg(args, signed long long));
break;
case 'f':
params.emplace_back(va_arg(args, double));
break;
case 'p':
params.emplace_back(va_arg(args, void*));
break;
case 's':
params.emplace_back(va_arg(args, const char*));
break;
case 'b':
params.emplace_back(va_arg(args, int));
break;
default:
throw runtime_error("C++ call: Unknown argument identifier " + c);
}
}
}
catch (...)
{
va_end(args);
throw;
}
va_end(args);
}
void ScriptFunctions::MakePublic(ScriptFunc _public, const char *name, char ret_type, const char *def) noexcept
{
Public::MakePublic(_public, name, ret_type, def);
@ -81,7 +25,7 @@ boost::any ScriptFunctions::CallPublic(const char *name, va_list args) noexcept
try
{
string def = Public::GetDefinition(name);
GetArguments(params, args, def);
Utils::getArguments(params, args, def);
return Public::Call(name, params);
}

@ -47,7 +47,6 @@ class ScriptFunctions
{
public:
static void GetArguments(std::vector<boost::any> &params, va_list args, const std::string &def);
static void MakePublic(ScriptFunc _public, const char *name, char ret_type, const char *def) noexcept;
static boost::any CallPublic(const char *name, va_list args) noexcept;
@ -157,6 +156,7 @@ public:
{"OnServerInit", Callback<>()},
{"OnServerPostInit", Callback<>()},
{"OnServerExit", Callback<bool>()},
{"OnServerScriptCrash", Callback<const char*>()},
{"OnPlayerConnect", Callback<unsigned short>()},
{"OnPlayerDisconnect", Callback<unsigned short>()},
{"OnPlayerDeath", Callback<unsigned short>()},
@ -209,7 +209,7 @@ public:
{"OnWorldMap", Callback<unsigned short>()},
{"OnWorldWeather", Callback<unsigned short>() },
{"OnMpNumIncrement", Callback<int>()},
{"OnRequestPluginList", Callback<>()}
{"OnRequestDataFileList", Callback<>()}
};
};

@ -105,7 +105,7 @@ struct ScriptFunctionPointer : public ScriptIdentity
void *addr;
#if (!defined(__clang__) && defined(__GNUC__))
template<typename R, typename... Types>
constexpr ScriptFunctionPointer(Function<R, Types...> addr) : ScriptIdentity(addr), addr(reinterpret_cast<void*>(addr)) {}
constexpr ScriptFunctionPointer(Function<R, Types...> addr) : ScriptIdentity(addr), addr((void*)(addr)) {}
#else
template<typename R, typename... Types>
constexpr ScriptFunctionPointer(Function<R, Types...> addr) : ScriptIdentity(addr), addr(addr) {}

@ -1,9 +1,7 @@
//
// Created by koncord on 04.03.17.
//
#include "Utils.hpp"
#include <cstdarg>
using namespace std;
const vector<string> Utils::split(const string &str, int delimiter)
@ -52,3 +50,59 @@ ESM::Cell Utils::getCellFromDescription(std::string cellDescription)
return cell;
}
void Utils::getArguments(std::vector<boost::any> &params, va_list args, const std::string &def)
{
params.reserve(def.length());
try
{
for (char c : def)
{
switch (c)
{
case 'i':
params.emplace_back(va_arg(args, unsigned int));
break;
case 'q':
params.emplace_back(va_arg(args, signed int));
break;
case 'l':
params.emplace_back(va_arg(args, unsigned long long));
break;
case 'w':
params.emplace_back(va_arg(args, signed long long));
break;
case 'f':
params.emplace_back(va_arg(args, double));
break;
case 'p':
params.emplace_back(va_arg(args, void*));
break;
case 's':
params.emplace_back(va_arg(args, const char*));
break;
case 'b':
params.emplace_back(va_arg(args, int));
break;
default:
throw runtime_error("C++ call: Unknown argument identifier " + c);
}
}
}
catch (...)
{
va_end(args);
throw;
}
va_end(args);
}

@ -1,7 +1,3 @@
//
// Created by koncord on 04.03.17.
//
#ifndef OPENMW_UTILS_HPP
#define OPENMW_UTILS_HPP
@ -9,6 +5,8 @@
#include <regex>
#include <vector>
#include <boost/any.hpp>
#include <components/esm/loadcell.hpp>
#include <components/openmw-mp/Utils.hpp>
@ -27,6 +25,8 @@ namespace Utils
ESM::Cell getCellFromDescription(std::string cellDescription);
void getArguments(std::vector<boost::any> &params, va_list args, const std::string &def);
template<size_t N>
constexpr unsigned int hash(const char(&str)[N], size_t I = N)
{

@ -190,47 +190,27 @@ int main(int argc, char *argv[])
LOG_INIT(logLevel);
int players = mgr.getInt("maximumPlayers", "General");
string addr = mgr.getString("localAddress", "General");
string address = mgr.getString("localAddress", "General");
int port = mgr.getInt("port", "General");
string passw = mgr.getString("password", "General");
string password = mgr.getString("password", "General");
string plugin_home = mgr.getString("home", "Plugins");
string moddir = Utils::convertPath(plugin_home + "/data");
string pluginHome = mgr.getString("home", "Plugins");
string dataDirectory = Utils::convertPath(pluginHome + "/data");
vector<string> plugins (Utils::split(mgr.getString("plugins", "Plugins"), ','));
vector<string> plugins(Utils::split(mgr.getString("plugins", "Plugins"), ','));
Utils::printVersion("TES3MP dedicated server", TES3MP_VERSION, version.mCommitHash, TES3MP_PROTO_VERSION);
// Check for unmodified tes3mp-credits file; this makes it so people can't repackage official releases with
// their own made-up credits, though it obviously has no bearing on unofficial releases that change
// the checksum below
boost::filesystem::path folderPath(boost::filesystem::initial_path<boost::filesystem::path>());
folderPath = boost::filesystem::system_complete(boost::filesystem::path(argv[0])).remove_filename();
std::string creditsPath = folderPath.string() + "/tes3mp-credits";
unsigned int expectedChecksumInt = Utils::hexStrToInt(TES3MP_CREDITS_CHECKSUM);
bool hasValidCredits = Utils::doesFileHaveChecksum(creditsPath + ".md", expectedChecksumInt);
if (!hasValidCredits)
hasValidCredits = Utils::doesFileHaveChecksum(creditsPath + ".txt", expectedChecksumInt);
if (!hasValidCredits)
{
LOG_MESSAGE_SIMPLE(Log::LOG_FATAL, "The server is shutting down");
LOG_APPEND(Log::LOG_FATAL, "- %s", TES3MP_CREDITS_ERROR);
return 1;
}
Script::SetModDir(moddir);
Script::SetModDir(dataDirectory);
#ifdef ENABLE_LUA
LangLua::AddPackagePath(Utils::convertPath(plugin_home + "/scripts/?.lua" + ";"
+ plugin_home + "/lib/lua/?.lua" + ";"));
LangLua::AddPackagePath(Utils::convertPath(pluginHome + "/scripts/?.lua" + ";"
+ pluginHome + "/lib/lua/?.lua" + ";"));
#ifdef _WIN32
LangLua::AddPackageCPath(Utils::convertPath(plugin_home + "/lib/?.dll"));
LangLua::AddPackageCPath(Utils::convertPath(pluginHome + "/lib/?.dll"));
#else
LangLua::AddPackageCPath(Utils::convertPath(plugin_home + "/lib/?.so"));
LangLua::AddPackageCPath(Utils::convertPath(pluginHome + "/lib/?.so"));
#endif
#endif
@ -246,18 +226,18 @@ int main(int argc, char *argv[])
peer->SetIncomingPassword(sstr.str().c_str(), (int) sstr.str().size());
if (RakNet::NonNumericHostString(addr.c_str()))
if (RakNet::NonNumericHostString(address.c_str()))
{
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "You cannot use non-numeric addresses for the server.");
return 1;
}
RakNet::SocketDescriptor sd((unsigned short) port, addr.c_str());
RakNet::SocketDescriptor sd((unsigned short) port, address.c_str());
try
{
for (auto plugin : plugins)
Script::LoadScript(plugin.c_str(), plugin_home.c_str());
Script::LoadScript(plugin.c_str(), pluginHome.c_str());
switch (peer->Startup((unsigned) players, &sd, 1))
{
@ -284,7 +264,7 @@ int main(int argc, char *argv[])
peer->SetMaximumIncomingConnections((unsigned short) (players));
Networking networking(peer);
networking.setServerPassword(passw);
networking.setServerPassword(password);
if (mgr.getBool("enabled", "MasterServer"))
{
@ -327,6 +307,7 @@ int main(int argc, char *argv[])
catch (std::exception &e)
{
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, e.what());
Script::Call<Script::CallbackIdentity("OnServerScriptCrash")>(e.what());
throw; //fall through
}

@ -1,7 +1,3 @@
//
// Created by koncord on 31.03.17.
//
#ifndef OPENMW_PROCESSORPLAYERPOSITION_HPP
#define OPENMW_PROCESSORPLAYERPOSITION_HPP
@ -19,11 +15,7 @@ namespace mwmp
void Do(PlayerPacket &packet, Player &player) override
{
//DEBUG_PRINTF(strPacketID);
if (!player.creatureStats.mDead)
{
player.sendToLoaded(&packet);
}
player.sendToLoaded(&packet);
}
};
}

@ -134,7 +134,7 @@ add_openmw_dir (mwmp/processors/object BaseObjectProcessor
ProcessorScriptGlobalFloat
)
add_openmw_dir (mwmp/processors/worldstate ProcessorCellCreate ProcessorCellReplace ProcessorRecordDynamic
add_openmw_dir (mwmp/processors/worldstate ProcessorCellCreate ProcessorCellReset ProcessorRecordDynamic
ProcessorWorldCollisionOverride ProcessorWorldMap ProcessorWorldRegionAuthority ProcessorWorldTime
ProcessorWorldWeather
)

@ -221,10 +221,11 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
/*
Start of tes3mp addition
Check for unmodified tes3mp-credits file; this makes it so people can't repackage official releases with
their own made-up credits, though it obviously has no bearing on unofficial releases that change
the checksum below
Check for unmodified tes3mp-credits file on Windows; this makes it so people can't repackage official
releases with their own made-up credits, though it obviously has no bearing on unofficial releases that
change the checksum below
*/
#ifdef _WIN32
boost::filesystem::path folderPath(boost::filesystem::initial_path<boost::filesystem::path>());
folderPath = boost::filesystem::system_complete(boost::filesystem::path(argv[0])).remove_filename();
std::string creditsPath = folderPath.string() + "/tes3mp-credits";
@ -242,6 +243,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "tes3mp", TES3MP_CREDITS_ERROR, 0);
return false;
}
#endif
/*
End of tes3mp addition
*/

@ -553,9 +553,10 @@ namespace MWClass
/*
Start of tes3mp addition
If the attacker was the LocalPlayer or LocalActor, record their target and send a packet with it
If the attacker was the LocalPlayer or LocalActor, record their target and send a
packet with it
If the victim was a LocalActor who died, record their attacker as the deathReason
If the victim was a LocalActor who died, record their attacker as the killer
*/
mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(attacker);

@ -966,12 +966,13 @@ namespace MWClass
/*
Start of tes3mp addition
If the attacker was the LocalPlayer or LocalActor, record their target and send a packet with it
If the attacker was the LocalPlayer or LocalActor, record their target and send a
packet with it
If the victim was the LocalPlayer, check whether packets should be sent about
their new dynamic stats and position
If the victim was a LocalActor who died, record their attacker as the deathReason
If the victim was a LocalActor who died, record their attacker as the killer
*/
mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(attacker);

@ -458,6 +458,15 @@ namespace MWGui
updateMagicMarkers();
updateCustomMarkers();
/*
Start of tes3mp addition
Update player markers when cell changes to fix their locations
*/
updatePlayerMarkers();
/*
End of tes3mp addition
*/
}
void LocalMapBase::requestMapRender(const MWWorld::CellStore *cell)

@ -9,6 +9,19 @@
#include <components/misc/rng.hpp>
/*
Start of tes3mp addition
Include additional headers for multiplayer purposes
*/
#include "../mwmp/Main.hpp"
#include "../mwmp/Networking.hpp"
#include "../mwmp/LocalPlayer.hpp"
#include "../mwmp/MechanicsHelper.hpp"
/*
End of tes3mp addition
*/
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
@ -163,9 +176,26 @@ void Recharge::onItemClicked(MyGUI::Widget *sender, const MWWorld::Ptr& item)
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(
item.getClass().getEnchantment(item));
/*
Start of tes3mp change (minor)
Send PlayerInventory packets that replace the original item with the new one
*/
mwmp::LocalPlayer *localPlayer = mwmp::Main::get().getLocalPlayer();
mwmp::Item removedItem = MechanicsHelper::getItem(item, 1);
item.getCellRef().setEnchantmentCharge(
std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast<float>(enchantment->mData.mCharge)));
mwmp::Item addedItem = MechanicsHelper::getItem(item, 1);
localPlayer->sendItemChange(addedItem, mwmp::InventoryChanges::ADD);
localPlayer->sendItemChange(removedItem, mwmp::InventoryChanges::REMOVE);
/*
End of tes3mp change (minor)
*/
MWBase::Environment::get().getWindowManager()->playSound("Enchant Success");
player.getClass().getContainerStore(player).restack(item);

@ -842,6 +842,22 @@ namespace MWMechanics
if (isDamageEffect)
{
/*
Start of tes3mp addition
If the victim was a LocalActor who died, record the caster as the killer
*/
if (mwmp::Main::get().getCellController()->isLocalActor(ptr))
{
bool isSuicide = ptr == caster || caster.isEmpty();
mwmp::Main::get().getCellController()->getLocalActor(ptr)->killer = isSuicide ?
MechanicsHelper::getTarget(ptr) : MechanicsHelper::getTarget(caster);
}
/*
End of tes3mp addition
*/
if (caster == player || playerFollowers.find(caster) != playerFollowers.end())
{
if (caster.getClass().getNpcStats(caster).isWerewolf())

@ -4,6 +4,19 @@
#include <components/misc/rng.hpp>
/*
Start of tes3mp addition
Include additional headers for multiplayer purposes
*/
#include "../mwmp/Main.hpp"
#include "../mwmp/Networking.hpp"
#include "../mwmp/LocalPlayer.hpp"
#include "../mwmp/MechanicsHelper.hpp"
/*
End of tes3mp addition
*/
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp"
@ -57,8 +70,25 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair)
// repair by 'y' points
int charge = itemToRepair.getClass().getItemHealth(itemToRepair);
charge = std::min(charge + y, itemToRepair.getClass().getItemMaxHealth(itemToRepair));
/*
Start of tes3mp change (minor)
Send PlayerInventory packets that replace the original item with the new one
*/
mwmp::LocalPlayer *localPlayer = mwmp::Main::get().getLocalPlayer();
mwmp::Item removedItem = MechanicsHelper::getItem(itemToRepair, 1);
itemToRepair.getCellRef().setCharge(charge);
mwmp::Item addedItem = MechanicsHelper::getItem(itemToRepair, 1);
localPlayer->sendItemChange(addedItem, mwmp::InventoryChanges::ADD);
localPlayer->sendItemChange(removedItem, mwmp::InventoryChanges::REMOVE);
/*
End of tes3mp change (minor)
*/
// attempt to re-stack item, in case it was fully repaired
MWWorld::ContainerStoreIterator stacked = player.getClass().getContainerStore(player).restack(itemToRepair);

@ -537,7 +537,7 @@ namespace MWMechanics
/*
Start of tes3mp addition
If the victim was a LocalPlayer or LocalActor who died, record their attacker as the deathReason
If the victim was a LocalPlayer or LocalActor who died, record the caster as the killer
*/
if (!wasDead && isDead)
{

@ -82,8 +82,10 @@ void Cell::updateLocal(bool forceUpdate)
}
else
{
// Forcibly update this local actor if its data has never been sent before;
// otherwise, use the current forceUpdate value
if (actor->getPtr().getRefData().isEnabled())
actor->update(forceUpdate);
actor->update(actor->hasSentData ? forceUpdate : true);
++it;
}
@ -397,6 +399,9 @@ void Cell::readCellChange(ActorList& actorList)
void Cell::initializeLocalActor(const MWWorld::Ptr& ptr)
{
std::string mapIndex = Main::get().getCellController()->generateMapIndex(ptr);
LOG_APPEND(Log::LOG_VERBOSE, "- Initializing LocalActor %s in %s", mapIndex.c_str(), getDescription().c_str());
LocalActor *actor = new LocalActor();
actor->cell = *store->getCell();
actor->setPtr(ptr);
@ -406,16 +411,17 @@ void Cell::initializeLocalActor(const MWWorld::Ptr& ptr)
if (ptr.getClass().getCreatureStats(ptr).isDead())
actor->wasDead = true;
std::string mapIndex = Main::get().getCellController()->generateMapIndex(ptr);
localActors[mapIndex] = actor;
Main::get().getCellController()->setLocalActorRecord(mapIndex, getDescription());
LOG_APPEND(Log::LOG_VERBOSE, "- Initialized LocalActor %s in %s", mapIndex.c_str(), getDescription().c_str());
LOG_APPEND(Log::LOG_VERBOSE, "- Successfully initialized LocalActor %s in %s", mapIndex.c_str(), getDescription().c_str());
}
void Cell::initializeLocalActors()
{
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Initializing LocalActors in %s", getDescription().c_str());
for (const auto &mergedRef : store->getMergedRefs())
{
if (mergedRef->mClass->isActor())
@ -432,20 +438,24 @@ void Cell::initializeLocalActors()
initializeLocalActor(ptr);
}
}
LOG_APPEND(Log::LOG_VERBOSE, "- Successfully initialized LocalActors in %s", getDescription().c_str());
}
void Cell::initializeDedicatedActor(const MWWorld::Ptr& ptr)
{
std::string mapIndex = Main::get().getCellController()->generateMapIndex(ptr);
LOG_APPEND(Log::LOG_VERBOSE, "- Initializing DedicatedActor %s in %s", mapIndex.c_str(), getDescription().c_str());
DedicatedActor *actor = new DedicatedActor();
actor->cell = *store->getCell();
actor->setPtr(ptr);
std::string mapIndex = Main::get().getCellController()->generateMapIndex(ptr);
dedicatedActors[mapIndex] = actor;
Main::get().getCellController()->setDedicatedActorRecord(mapIndex, getDescription());
LOG_APPEND(Log::LOG_VERBOSE, "- Initialized DedicatedActor %s in %s", mapIndex.c_str(), getDescription().c_str());
LOG_APPEND(Log::LOG_VERBOSE, "- Successfully initialized DedicatedActor %s in %s", mapIndex.c_str(), getDescription().c_str());
}
void Cell::initializeDedicatedActors(ActorList& actorList)

@ -77,6 +77,8 @@ void CellController::initializeCell(const ESM::Cell& cell)
// If this key doesn't exist, create it
if (cellsInitialized.count(mapIndex) == 0)
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Initializing mwmp::Cell %s", cell.getDescription().c_str());
MWWorld::CellStore *cellStore = getCellStore(cell);
if (!cellStore) return;
@ -84,7 +86,7 @@ void CellController::initializeCell(const ESM::Cell& cell)
mwmp::Cell *mpCell = new mwmp::Cell(cellStore);
cellsInitialized[mapIndex] = mpCell;
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "- Initialized mwmp::Cell %s", mpCell->getDescription().c_str());
LOG_APPEND(Log::LOG_VERBOSE, "- Successfully initialized mwmp::Cell %s", cell.getDescription().c_str());
}
}

@ -71,6 +71,13 @@ DedicatedPlayer::~DedicatedPlayer()
void DedicatedPlayer::update(float dt)
{
// Only move and set anim flags if the framerate isn't too low
if (dt < 0.1)
{
move(dt);
setAnimFlags();
}
MWMechanics::CreatureStats *ptrCreatureStats = &ptr.getClass().getCreatureStats(ptr);
MWMechanics::DynamicStat<float> value;
@ -100,13 +107,6 @@ void DedicatedPlayer::update(float dt)
ptrCreatureStats->setAiSetting(MWMechanics::CreatureStats::AI_Fight, 0);
ptrCreatureStats->setAiSetting(MWMechanics::CreatureStats::AI_Flee, 0);
ptrCreatureStats->setAiSetting(MWMechanics::CreatureStats::AI_Hello, 0);
// Only move and set anim flags if the framerate isn't too low
if (dt < 0.1)
{
move(dt);
setAnimFlags();
}
}
void DedicatedPlayer::move(float dt)

@ -22,6 +22,7 @@ using namespace std;
LocalActor::LocalActor()
{
hasSentData = false;
posWasChanged = false;
equipmentChanged = false;
@ -61,14 +62,16 @@ void LocalActor::update(bool forceUpdate)
updateSpeech();
updateAttack();
}
hasSentData = true;
}
void LocalActor::updateCell()
{
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Sending ID_ACTOR_CELL_CHANGE about %s %i-%i to server",
refId.c_str(), refNum, mpNum);
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Sending ID_ACTOR_CELL_CHANGE about %s %i-%i in cell %s to server",
refId.c_str(), refNum, mpNum, cell.getDescription().c_str());
LOG_APPEND(Log::LOG_VERBOSE, "- Moved from %s to %s", cell.getDescription().c_str(), ptr.getCell()->getCell()->getDescription().c_str());
LOG_APPEND(Log::LOG_VERBOSE, "- Moved to cell %s", ptr.getCell()->getCell()->getDescription().c_str());
cell = *ptr.getCell()->getCell();
position = ptr.getRefData().getPosition();
@ -191,8 +194,8 @@ void LocalActor::updateStatsDynamic(bool forceUpdate)
if (MechanicsHelper::isEmptyTarget(killer))
killer = MechanicsHelper::getTarget(ptr);
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_ACTOR_DEATH about %s %i-%i to server",
refId.c_str(), refNum, mpNum);
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_ACTOR_DEATH about %s %i-%i in cell %s to server",
refId.c_str(), refNum, mpNum, cell.getDescription().c_str());
mwmp::Main::get().getNetworking()->getActorList()->addDeathActor(*this);

@ -28,6 +28,7 @@ namespace mwmp
MWWorld::Ptr getPtr();
void setPtr(const MWWorld::Ptr& newPtr);
bool hasSentData;
bool wasDead;
private:

@ -284,6 +284,7 @@ void LocalPlayer::updateAttributes(bool forceUpdate)
{
if (ptrNpcStats.getAttribute(i).getBase() != creatureStats.mAttributes[i].mBase ||
ptrNpcStats.getAttribute(i).getModifier() != creatureStats.mAttributes[i].mMod ||
ptrNpcStats.getAttribute(i).getDamage() != creatureStats.mAttributes[i].mDamage ||
ptrNpcStats.getSkillIncrease(i) != npcStats.mSkillIncrease[i] ||
forceUpdate)
{
@ -318,6 +319,7 @@ void LocalPlayer::updateSkills(bool forceUpdate)
// Update a skill if its base value has changed at all or its progress has changed enough
if (ptrNpcStats.getSkill(i).getBase() != npcStats.mSkills[i].mBase ||
ptrNpcStats.getSkill(i).getModifier() != npcStats.mSkills[i].mMod ||
ptrNpcStats.getSkill(i).getDamage() != npcStats.mSkills[i].mDamage ||
abs(ptrNpcStats.getSkill(i).getProgress() - npcStats.mSkills[i].mProgress) > 0.75 ||
forceUpdate)
{
@ -920,7 +922,7 @@ void LocalPlayer::setAttributes()
{
MWWorld::Ptr ptrPlayer = getPlayerPtr();
MWMechanics::CreatureStats *ptrCreatureStats = &ptrPlayer.getClass().getCreatureStats(ptrPlayer);
MWMechanics::NpcStats *ptrNpcStats = &ptrPlayer.getClass().getNpcStats(ptrPlayer);
MWMechanics::AttributeValue attributeValue;
for (int attributeIndex = 0; attributeIndex < 8; ++attributeIndex)
@ -928,14 +930,14 @@ void LocalPlayer::setAttributes()
// If the server wants to clear our attribute's non-zero modifier, we need to remove
// the spell effect causing it, to avoid an infinite loop where the effect keeps resetting
// the modifier
if (creatureStats.mAttributes[attributeIndex].mMod == 0 && ptrCreatureStats->getAttribute(attributeIndex).getModifier() > 0)
if (creatureStats.mAttributes[attributeIndex].mMod == 0 && ptrNpcStats->getAttribute(attributeIndex).getModifier() > 0)
{
ptrCreatureStats->getActiveSpells().purgeEffectByArg(ESM::MagicEffect::FortifyAttribute, attributeIndex);
ptrNpcStats->getActiveSpells().purgeEffectByArg(ESM::MagicEffect::FortifyAttribute, attributeIndex);
MWBase::Environment::get().getMechanicsManager()->updateMagicEffects(ptrPlayer);
// Is the modifier for this attribute still higher than 0? If so, unequip items that
// fortify the attribute
if (ptrCreatureStats->getAttribute(attributeIndex).getModifier() > 0)
if (ptrNpcStats->getAttribute(attributeIndex).getModifier() > 0)
{
MechanicsHelper::unequipItemsByEffect(ptrPlayer, ESM::Enchantment::ConstantEffect, ESM::MagicEffect::FortifyAttribute, attributeIndex, -1);
mwmp::Main::get().getGUIController()->refreshGuiMode(MWGui::GM_Inventory);
@ -943,7 +945,9 @@ void LocalPlayer::setAttributes()
}
attributeValue.readState(creatureStats.mAttributes[attributeIndex]);
ptrCreatureStats->setAttribute(attributeIndex, attributeValue);
ptrNpcStats->setAttribute(attributeIndex, attributeValue);
ptrNpcStats->setSkillIncrease(attributeIndex, npcStats.mSkillIncrease[attributeIndex]);
}
}
@ -976,11 +980,6 @@ void LocalPlayer::setSkills()
skillValue.readState(npcStats.mSkills[skillIndex]);
ptrNpcStats->setSkill(skillIndex, skillValue);
}
for (int attributeIndex = 0; attributeIndex < 8; ++attributeIndex)
ptrNpcStats->setSkillIncrease(attributeIndex, npcStats.mSkillIncrease[attributeIndex]);
ptrNpcStats->setLevelProgress(npcStats.mLevelProgress);
}
void LocalPlayer::setLevel()
@ -988,8 +987,9 @@ void LocalPlayer::setLevel()
MWBase::World *world = MWBase::Environment::get().getWorld();
MWWorld::Ptr ptrPlayer = world->getPlayerPtr();
MWMechanics::CreatureStats *ptrCreatureStats = &ptrPlayer.getClass().getCreatureStats(ptrPlayer);
ptrCreatureStats->setLevel(creatureStats.mLevel);
MWMechanics::NpcStats *ptrNpcStats = &ptrPlayer.getClass().getNpcStats(ptrPlayer);
ptrNpcStats->setLevel(creatureStats.mLevel);
ptrNpcStats->setLevelProgress(npcStats.mLevelProgress);
}
void LocalPlayer::setBounty()
@ -1403,33 +1403,25 @@ void LocalPlayer::sendInventory()
getNetworking()->getPlayerPacket(ID_PLAYER_INVENTORY)->Send();
}
void LocalPlayer::sendItemChange(const MWWorld::Ptr& itemPtr, int count, unsigned int action)
void LocalPlayer::sendItemChange(const mwmp::Item& item, unsigned int action)
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending item change for %s with action %i, count %i",
itemPtr.getCellRef().getRefId().c_str(), action, count);
item.refId.c_str(), action, item.count);
inventoryChanges.items.clear();
mwmp::Item item;
if (itemPtr.getClass().isGold(itemPtr))
item.refId = MWWorld::ContainerStore::sGoldId;
else
item.refId = itemPtr.getCellRef().getRefId();
item.count = count;
item.charge = itemPtr.getCellRef().getCharge();
item.enchantmentCharge = itemPtr.getCellRef().getEnchantmentCharge();
item.soul = itemPtr.getCellRef().getSoul();
inventoryChanges.items.push_back(item);
inventoryChanges.action = action;
getNetworking()->getPlayerPacket(ID_PLAYER_INVENTORY)->setPlayer(this);
getNetworking()->getPlayerPacket(ID_PLAYER_INVENTORY)->Send();
}
void LocalPlayer::sendItemChange(const MWWorld::Ptr& itemPtr, int count, unsigned int action)
{
mwmp::Item item = MechanicsHelper::getItem(itemPtr, count);
sendItemChange(item, action);
}
void LocalPlayer::sendItemChange(const std::string& refId, int count, unsigned int action)
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending item change for %s with action %i, count %i",

@ -77,6 +77,7 @@ namespace mwmp
void sendClass();
void sendInventory();
void sendItemChange(const mwmp::Item& item, unsigned int action);
void sendItemChange(const MWWorld::Ptr& itemPtr, int count, unsigned int action);
void sendItemChange(const std::string& refId, int count, unsigned int action);
void sendSpellbook();

@ -51,8 +51,8 @@ using namespace mwmp;
using namespace std;
Main *Main::pMain = 0;
std::string Main::addr = "";
std::string Main::passw = TES3MP_DEFAULT_PASSW;
std::string Main::address = "";
std::string Main::serverPassword = TES3MP_DEFAULT_PASSW;
std::string Main::resourceDir = "";
std::string Main::getResDir()
@ -118,8 +118,8 @@ void Main::optionsDesc(boost::program_options::options_description *desc)
void Main::configure(const boost::program_options::variables_map &variables)
{
Main::addr = variables["connect"].as<string>();
Main::passw = variables["password"].as<string>();
Main::address = variables["connect"].as<string>();
Main::serverPassword = variables["password"].as<string>();
resourceDir = variables["resources"].as<Files::EscapeHashString>().toStdString();
}
@ -155,22 +155,22 @@ bool Main::init(std::vector<std::string> &content, Files::Collections &collectio
int logLevel = mgr.getInt("logLevel", "General");
Log::SetLevel(logLevel);
if (addr.empty())
if (address.empty())
{
pMain->server = mgr.getString("destinationAddress", "General");
pMain->port = (unsigned short) mgr.getInt("port", "General");
passw = mgr.getString("password", "General");
if (passw.empty())
passw = TES3MP_DEFAULT_PASSW;
serverPassword = mgr.getString("password", "General");
if (serverPassword.empty())
serverPassword = TES3MP_DEFAULT_PASSW;
}
else
{
size_t delim_pos = addr.find(':');
pMain->server = addr.substr(0, delim_pos);
pMain->port = atoi(addr.substr(delim_pos + 1).c_str());
size_t delimPos = address.find(':');
pMain->server = address.substr(0, delimPos);
pMain->port = atoi(address.substr(delimPos + 1).c_str());
}
get().mLocalPlayer->passw = passw;
get().mLocalPlayer->serverPassword = serverPassword;
pMain->mNetworking->connect(pMain->server, pMain->port, content, collections);
RestoreMgr(mgr);

@ -39,8 +39,8 @@ namespace mwmp
private:
static std::string resourceDir;
static std::string addr;
static std::string passw;
static std::string address;
static std::string serverPassword;
Main (const Main&);
///< not implemented
Main& operator= (const Main&);

@ -125,6 +125,23 @@ MWWorld::Ptr MechanicsHelper::getPlayerPtr(const Target& target)
return nullptr;
}
mwmp::Item MechanicsHelper::getItem(const MWWorld::Ptr& itemPtr, int count)
{
mwmp::Item item;
if (itemPtr.getClass().isGold(itemPtr))
item.refId = MWWorld::ContainerStore::sGoldId;
else
item.refId = itemPtr.getCellRef().getRefId();
item.count = count;
item.charge = itemPtr.getCellRef().getCharge();
item.enchantmentCharge = itemPtr.getCellRef().getEnchantmentCharge();
item.soul = itemPtr.getCellRef().getSoul();
return item;
}
mwmp::Target MechanicsHelper::getTarget(const MWWorld::Ptr& ptr)
{
mwmp::Target target;
@ -393,18 +410,14 @@ void MechanicsHelper::processAttack(Attack attack, const MWWorld::Ptr& attacker)
break;
}
if (it != inventoryStore.end())
{
inventoryStore.setSelectedEnchantItem(it);
LOG_APPEND(Log::LOG_VERBOSE, "- itemId: %s", attack.itemId.c_str());
MWBase::Environment::get().getWorld()->castSpell(attacker);
inventoryStore.setSelectedEnchantItem(inventoryStore.end());
}
else
{
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Could not find item %s used by %s to cast item spell!",
attack.itemId.c_str(), attacker.getCellRef().getRefId().c_str());
}
// Add the item if it's missing
if (it == inventoryStore.end())
it = attacker.getClass().getContainerStore(attacker).add(attack.itemId, 1, attacker);
inventoryStore.setSelectedEnchantItem(it);
LOG_APPEND(Log::LOG_VERBOSE, "- itemId: %s", attack.itemId.c_str());
MWBase::Environment::get().getWorld()->castSpell(attacker);
inventoryStore.setSelectedEnchantItem(inventoryStore.end());
}
}

@ -22,6 +22,7 @@ namespace MechanicsHelper
MWWorld::Ptr getPlayerPtr(const mwmp::Target& target);
mwmp::Item getItem(const MWWorld::Ptr& itemPtr, int count);
mwmp::Target getTarget(const MWWorld::Ptr& ptr);
void clearTarget(mwmp::Target& target);
bool isEmptyTarget(const mwmp::Target& target);

@ -110,7 +110,7 @@ void RecordHelper::overrideCreatureRecord(const mwmp::CreatureRecord& record)
if (recordData.mId.empty())
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with no id provided");
return;
}
@ -164,6 +164,11 @@ void RecordHelper::overrideCreatureRecord(const mwmp::CreatureRecord& record)
world->getModifiableStore().overrideRecord(finalData);
}
else
{
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with invalid baseId %s", record.baseId.c_str());
return;
}
if (isExistingId)
world->updatePtrsWithRefId(recordData.mId);
@ -175,7 +180,7 @@ void RecordHelper::overrideNpcRecord(const mwmp::NpcRecord& record)
if (recordData.mId.empty())
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with no id provided");
return;
}
@ -186,12 +191,12 @@ void RecordHelper::overrideNpcRecord(const mwmp::NpcRecord& record)
{
if (!doesRaceRecordExist(recordData.mRace))
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring new NPC record with invalid race provided");
LOG_APPEND(Log::LOG_INFO, "-- Ignoring new NPC record with invalid race provided");
return;
}
else if (!doesClassRecordExist(recordData.mClass))
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring new NPC record with invalid class provided");
LOG_APPEND(Log::LOG_INFO, "-- Ignoring new NPC record with invalid class provided");
return;
}
else
@ -272,6 +277,11 @@ void RecordHelper::overrideNpcRecord(const mwmp::NpcRecord& record)
world->getModifiableStore().overrideRecord(finalData);
}
else
{
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with invalid baseId %s", record.baseId.c_str());
return;
}
if (isExistingId)
world->updatePtrsWithRefId(recordData.mId);
@ -283,18 +293,17 @@ void RecordHelper::overrideEnchantmentRecord(const mwmp::EnchantmentRecord& reco
if (recordData.mId.empty())
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with no id provided");
return;
}
bool isExistingId = doesEnchantmentRecordExist(recordData.mId);
MWBase::World *world = MWBase::Environment::get().getWorld();
if (record.baseId.empty())
{
if (recordData.mEffects.mList.empty())
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring new enchantment record with no effects");
LOG_APPEND(Log::LOG_INFO, "-- Ignoring new enchantment record with no effects");
return;
}
else
@ -323,6 +332,11 @@ void RecordHelper::overrideEnchantmentRecord(const mwmp::EnchantmentRecord& reco
world->getModifiableStore().overrideRecord(finalData);
}
else
{
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with invalid baseId %s", record.baseId.c_str());
return;
}
}
void RecordHelper::overridePotionRecord(const mwmp::PotionRecord& record)
@ -331,7 +345,7 @@ void RecordHelper::overridePotionRecord(const mwmp::PotionRecord& record)
if (recordData.mId.empty())
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with no id provided");
return;
}
@ -374,6 +388,11 @@ void RecordHelper::overridePotionRecord(const mwmp::PotionRecord& record)
world->getModifiableStore().overrideRecord(finalData);
}
else
{
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with invalid baseId %s", record.baseId.c_str());
return;
}
if (isExistingId)
world->updatePtrsWithRefId(recordData.mId);
@ -385,7 +404,7 @@ void RecordHelper::overrideSpellRecord(const mwmp::SpellRecord& record)
if (recordData.mId.empty())
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with no id provided");
return;
}
@ -419,9 +438,11 @@ void RecordHelper::overrideSpellRecord(const mwmp::SpellRecord& record)
world->getModifiableStore().overrideRecord(finalData);
}
if (isExistingId)
world->updatePtrsWithRefId(recordData.mId);
else
{
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with invalid baseId %s", record.baseId.c_str());
return;
}
}
void RecordHelper::overrideArmorRecord(const mwmp::ArmorRecord& record)
@ -430,7 +451,7 @@ void RecordHelper::overrideArmorRecord(const mwmp::ArmorRecord& record)
if (recordData.mId.empty())
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with no id provided");
return;
}
@ -441,7 +462,7 @@ void RecordHelper::overrideArmorRecord(const mwmp::ArmorRecord& record)
{
if (!recordData.mEnchant.empty() && !doesEnchantmentRecordExist(recordData.mEnchant))
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring new armor record with invalid enchantment provided");
LOG_APPEND(Log::LOG_INFO, "-- Ignoring new armor record with invalid enchantmentId %s", recordData.mEnchant.c_str());
return;
}
else
@ -479,8 +500,13 @@ void RecordHelper::overrideArmorRecord(const mwmp::ArmorRecord& record)
if (record.baseOverrides.hasArmorRating)
finalData.mData.mArmor = recordData.mData.mArmor;
if (record.baseOverrides.hasEnchantmentId && doesEnchantmentRecordExist(recordData.mEnchant))
finalData.mEnchant = recordData.mEnchant;
if (record.baseOverrides.hasEnchantmentId)
{
if (doesEnchantmentRecordExist(recordData.mEnchant))
finalData.mEnchant = recordData.mEnchant;
else
LOG_APPEND(Log::LOG_INFO, "-- Ignoring invalid enchantmentId %s", recordData.mEnchant.c_str());
}
if (record.baseOverrides.hasEnchantmentCharge)
finalData.mData.mEnchant = recordData.mData.mEnchant;
@ -493,6 +519,11 @@ void RecordHelper::overrideArmorRecord(const mwmp::ArmorRecord& record)
world->getModifiableStore().overrideRecord(finalData);
}
else
{
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with invalid baseId %s", record.baseId.c_str());
return;
}
if (isExistingId)
world->updatePtrsWithRefId(recordData.mId);
@ -504,7 +535,7 @@ void RecordHelper::overrideBookRecord(const mwmp::BookRecord& record)
if (recordData.mId.empty())
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with no id provided");
return;
}
@ -515,7 +546,7 @@ void RecordHelper::overrideBookRecord(const mwmp::BookRecord& record)
{
if (!recordData.mEnchant.empty() && !doesEnchantmentRecordExist(recordData.mEnchant))
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring new book record with invalid enchantment provided");
LOG_APPEND(Log::LOG_INFO, "-- Ignoring new book record with invalid enchantmentId %s", recordData.mEnchant.c_str());
return;
}
else
@ -551,8 +582,13 @@ void RecordHelper::overrideBookRecord(const mwmp::BookRecord& record)
if (record.baseOverrides.hasSkillId)
finalData.mData.mSkillId = recordData.mData.mSkillId;
if (record.baseOverrides.hasEnchantmentId && doesEnchantmentRecordExist(recordData.mEnchant))
finalData.mEnchant = recordData.mEnchant;
if (record.baseOverrides.hasEnchantmentId)
{
if (doesEnchantmentRecordExist(recordData.mEnchant))
finalData.mEnchant = recordData.mEnchant;
else
LOG_APPEND(Log::LOG_INFO, "-- Ignoring invalid enchantmentId %s", recordData.mEnchant.c_str());
}
if (record.baseOverrides.hasEnchantmentCharge)
finalData.mData.mEnchant = recordData.mData.mEnchant;
@ -562,6 +598,11 @@ void RecordHelper::overrideBookRecord(const mwmp::BookRecord& record)
world->getModifiableStore().overrideRecord(finalData);
}
else
{
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with invalid baseId %s", record.baseId.c_str());
return;
}
if (isExistingId)
world->updatePtrsWithRefId(recordData.mId);
@ -573,7 +614,7 @@ void RecordHelper::overrideClothingRecord(const mwmp::ClothingRecord& record)
if (recordData.mId.empty())
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with no id provided");
return;
}
@ -584,7 +625,7 @@ void RecordHelper::overrideClothingRecord(const mwmp::ClothingRecord& record)
{
if (!recordData.mEnchant.empty() && !doesEnchantmentRecordExist(recordData.mEnchant))
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring new clothing record with invalid enchantment provided");
LOG_APPEND(Log::LOG_INFO, "-- Ignoring new clothing record with invalid enchantmentId %s", recordData.mEnchant.c_str());
return;
}
else
@ -614,8 +655,13 @@ void RecordHelper::overrideClothingRecord(const mwmp::ClothingRecord& record)
if (record.baseOverrides.hasValue)
finalData.mData.mValue = recordData.mData.mValue;
if (record.baseOverrides.hasEnchantmentId && doesEnchantmentRecordExist(recordData.mEnchant))
finalData.mEnchant = recordData.mEnchant;
if (record.baseOverrides.hasEnchantmentId)
{
if (doesEnchantmentRecordExist(recordData.mEnchant))
finalData.mEnchant = recordData.mEnchant;
else
LOG_APPEND(Log::LOG_INFO, "-- Ignoring invalid enchantmentId %s", recordData.mEnchant.c_str());
}
if (record.baseOverrides.hasEnchantmentCharge)
finalData.mData.mEnchant = recordData.mData.mEnchant;
@ -628,6 +674,11 @@ void RecordHelper::overrideClothingRecord(const mwmp::ClothingRecord& record)
world->getModifiableStore().overrideRecord(finalData);
}
else
{
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with invalid baseId %s", record.baseId.c_str());
return;
}
if (isExistingId)
world->updatePtrsWithRefId(recordData.mId);
@ -639,7 +690,7 @@ void RecordHelper::overrideMiscellaneousRecord(const mwmp::MiscellaneousRecord&
if (recordData.mId.empty())
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with no id provided");
return;
}
@ -679,6 +730,11 @@ void RecordHelper::overrideMiscellaneousRecord(const mwmp::MiscellaneousRecord&
world->getModifiableStore().overrideRecord(finalData);
}
else
{
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with invalid baseId %s", record.baseId.c_str());
return;
}
if (isExistingId)
world->updatePtrsWithRefId(recordData.mId);
@ -690,7 +746,7 @@ void RecordHelper::overrideWeaponRecord(const mwmp::WeaponRecord& record)
if (recordData.mId.empty())
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with no id provided");
return;
}
@ -701,7 +757,7 @@ void RecordHelper::overrideWeaponRecord(const mwmp::WeaponRecord& record)
{
if (!recordData.mEnchant.empty() && !doesEnchantmentRecordExist(recordData.mEnchant))
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring new weapon record with invalid enchantment provided");
LOG_APPEND(Log::LOG_INFO, "-- Ignoring new weapon record with invalid enchantmentId %s", recordData.mEnchant.c_str());
return;
}
else
@ -761,8 +817,13 @@ void RecordHelper::overrideWeaponRecord(const mwmp::WeaponRecord& record)
if (record.baseOverrides.hasFlags)
finalData.mData.mFlags = recordData.mData.mFlags;
if (record.baseOverrides.hasEnchantmentId && doesEnchantmentRecordExist(recordData.mEnchant))
finalData.mEnchant = recordData.mEnchant;
if (record.baseOverrides.hasEnchantmentId)
{
if (doesEnchantmentRecordExist(recordData.mEnchant))
finalData.mEnchant = recordData.mEnchant;
else
LOG_APPEND(Log::LOG_INFO, "-- Ignoring invalid enchantmentId %s", recordData.mEnchant.c_str());
}
if (record.baseOverrides.hasEnchantmentCharge)
finalData.mData.mEnchant = recordData.mData.mEnchant;
@ -772,6 +833,11 @@ void RecordHelper::overrideWeaponRecord(const mwmp::WeaponRecord& record)
world->getModifiableStore().overrideRecord(finalData);
}
else
{
LOG_APPEND(Log::LOG_INFO, "-- Ignoring record override with invalid baseId %s", record.baseId.c_str());
return;
}
if (isExistingId)
world->updatePtrsWithRefId(recordData.mId);

@ -89,7 +89,7 @@
#include "WorldstateProcessor.hpp"
#include "worldstate/ProcessorCellCreate.hpp"
#include "worldstate/ProcessorCellReplace.hpp"
#include "worldstate/ProcessorCellReset.hpp"
#include "worldstate/ProcessorRecordDynamic.hpp"
#include "worldstate/ProcessorWorldCollisionOverride.hpp"
#include "worldstate/ProcessorWorldMap.hpp"
@ -186,7 +186,7 @@ void ProcessorInitializer()
ActorProcessor::AddProcessor(new ProcessorActorTest());
WorldstateProcessor::AddProcessor(new ProcessorCellCreate());
WorldstateProcessor::AddProcessor(new ProcessorCellReplace());
WorldstateProcessor::AddProcessor(new ProcessorCellReset());
WorldstateProcessor::AddProcessor(new ProcessorRecordDynamic());
WorldstateProcessor::AddProcessor(new ProcessorWorldCollisionOverride());
WorldstateProcessor::AddProcessor(new ProcessorWorldMap());

@ -1,23 +0,0 @@
#ifndef OPENMW_PROCESSORCELLREPLACE_HPP
#define OPENMW_PROCESSORCELLREPLACE_HPP
#include "../WorldstateProcessor.hpp"
namespace mwmp
{
class ProcessorCellReplace : public WorldstateProcessor
{
public:
ProcessorCellReplace()
{
BPP_INIT(ID_CELL_REPLACE)
}
virtual void Do(WorldstatePacket &packet, Worldstate &worldstate)
{
// Placeholder
}
};
}
#endif //OPENMW_PROCESSORCELLREPLACE_HPP

@ -0,0 +1,23 @@
#ifndef OPENMW_PROCESSORCELLRESET_HPP
#define OPENMW_PROCESSORCELLRESET_HPP
#include "../WorldstateProcessor.hpp"
namespace mwmp
{
class ProcessorCellReset : public WorldstateProcessor
{
public:
ProcessorCellReset()
{
BPP_INIT(ID_CELL_RESET)
}
virtual void Do(WorldstatePacket &packet, Worldstate &worldstate)
{
// Placeholder
}
};
}
#endif //OPENMW_PROCESSORCELLRESET_HPP

@ -613,13 +613,17 @@ namespace MWScript
Start of tes3mp addition
Send an ID_OBJECT_STATE packet whenever an object is enabled, as long as
the player is logged in on the server and the object wasn't already
enabled previously
the player is logged in on the server, the object is still disabled, and our last
packet regarding its state did not already attempt to enable it (to prevent
packet spam)
*/
if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())
{
if (ref.isInCell() && !ref.getRefData().isEnabled())
if (ref.isInCell() && !ref.getRefData().isEnabled() &&
ref.getRefData().getLastCommunicatedState() != MWWorld::RefData::StateCommunication::Enabled)
{
ref.getRefData().setLastCommunicatedState(MWWorld::RefData::StateCommunication::Enabled);
mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();
objectList->reset();
objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(getContextType());
@ -650,14 +654,18 @@ namespace MWScript
/*
Start of tes3mp addition
Send an ID_OBJECT_STATE packet whenever an object is disabled, as long as
the player is logged in on the server and the object wasn't already
disabled previously
Send an ID_OBJECT_STATE packet whenever an object should be disabled, as long as
the player is logged in on the server, the object is still enabled, and our last
packet regarding its state did not already attempt to disable it (to prevent
packet spam)
*/
if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())
{
if (ref.isInCell() && ref.getRefData().isEnabled())
if (ref.isInCell() && ref.getRefData().isEnabled() &&
ref.getRefData().getLastCommunicatedState() != MWWorld::RefData::StateCommunication::Disabled)
{
ref.getRefData().setLastCommunicatedState(MWWorld::RefData::StateCommunication::Disabled);
mwmp::ObjectList *objectList = mwmp::Main::get().getNetworking()->getObjectList();
objectList->reset();
objectList->packetOrigin = ScriptController::getPacketOriginFromContextType(getContextType());

@ -13,6 +13,7 @@
Include additional headers for multiplayer purposes
*/
#include <components/openmw-mp/Log.hpp>
#include "../mwmp/Main.hpp"
#include "../mwmp/CellController.hpp"
#include "../mwmp/PlayerList.hpp"
@ -251,7 +252,19 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::findSlot (int slot) con
{
// Object has been deleted
// This should no longer happen, since the new remove function will unequip first
throw std::runtime_error("Invalid slot, make sure you are not calling RefData::setCount for a container object");
/*
Start of tes3mp change (major)
Instead of throwing an error, display an error log message with information about
the item
*/
//throw std::runtime_error("Invalid slot, make sure you are not calling RefData::setCount for a container object");
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Invalid slot, make sure you are not calling RefData::setCount for a container object\n- item was %s",
mSlots[slot]->getCellRef().getRefId().c_str());
/*
End of tes3mp change (major)
*/
}
return mSlots[slot];

@ -138,6 +138,34 @@ namespace MWWorld
const ESM::AnimationState& getAnimationState() const;
ESM::AnimationState& getAnimationState();
/*
Start of tes3mp addition
Track the last state communicated to the server for this reference,
to avoid packet spam when the server denies our state change request or
is slow to reply
*/
enum StateCommunication
{
None = 0,
Enabled = 1,
Disabled = 2
};
private:
short mLastCommunicatedState = StateCommunication::None;
public:
short getLastCommunicatedState() { return mLastCommunicatedState; };
void setLastCommunicatedState(short communicationState) { mLastCommunicatedState = communicationState; };
/*
End of tes3mp addition
*/
};
}

@ -3258,10 +3258,11 @@ namespace MWWorld
If this actor is a LocalPlayer or LocalActor, get their Attack and prepare
it for sending
Set the attack details before going through with the casting, in case it's
a one use item that would get removed through the casting (like a scroll)
*/
{
cast.cast(*inv.getSelectedEnchantItem());
mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(actor);
if (localAttack)
@ -3271,6 +3272,8 @@ namespace MWWorld
localAttack->itemId = inv.getSelectedEnchantItem()->getCellRef().getRefId();
localAttack->shouldSend = true;
}
cast.cast(*inv.getSelectedEnchantItem());
}
/*
End of tes3mp addition

@ -202,7 +202,7 @@ add_component_dir (openmw-mp/Packets/Object
add_component_dir (openmw-mp/Packets/Worldstate
WorldstatePacket
PacketCellCreate PacketCellReplace PacketRecordDynamic PacketWorldCollisionOverride PacketWorldMap
PacketCellCreate PacketCellReset PacketRecordDynamic PacketWorldCollisionOverride PacketWorldMap
PacketWorldRegionAuthority PacketWorldTime PacketWorldWeather
)

@ -232,6 +232,8 @@ namespace mwmp
}
RakNet::RakNetGUID guid;
std::string serverPassword;
GUIMessageBox guiMessageBox;
// Track only the indexes of the attributes that have been changed,
@ -295,7 +297,6 @@ namespace mwmp
std::string birthsign;
std::string chatMessage;
CharGenState charGenState;
std::string passw;
std::string sound;
Animation animation;

@ -1,5 +1,5 @@
#include "../Packets/Worldstate/PacketCellCreate.hpp"
#include "../Packets/Worldstate/PacketCellReplace.hpp"
#include "../Packets/Worldstate/PacketCellReset.hpp"
#include "../Packets/Worldstate/PacketRecordDynamic.hpp"
#include "../Packets/Worldstate/PacketWorldCollisionOverride.hpp"
#include "../Packets/Worldstate/PacketWorldMap.hpp"
@ -20,7 +20,7 @@ inline void AddPacket(mwmp::WorldstatePacketController::packets_t *packets, RakN
mwmp::WorldstatePacketController::WorldstatePacketController(RakNet::RakPeerInterface *peer)
{
AddPacket<PacketCellCreate>(&packets, peer);
AddPacket<PacketCellReplace>(&packets, peer);
AddPacket<PacketCellReset>(&packets, peer);
AddPacket<PacketRecordDynamic>(&packets, peer);
AddPacket<PacketWorldCollisionOverride>(&packets, peer);
AddPacket<PacketWorldMap>(&packets, peer);

@ -104,7 +104,7 @@ enum GameMessages
ID_GAME_PREINIT,
ID_CELL_CREATE,
ID_CELL_REPLACE,
ID_CELL_RESET,
ID_RECORD_DYNAMIC,
ID_WORLD_COLLISION_OVERRIDE,
ID_WORLD_MAP,

@ -1,7 +1,3 @@
//
// Created by koncord on 28.04.16.
//
#include <components/openmw-mp/NetworkMessages.hpp>
#include "PacketHandshake.hpp"
@ -17,8 +13,8 @@ void PacketHandshake::Packet(RakNet::BitStream *bs, bool send)
{
PlayerPacket::Packet(bs, send);
if (!RW(player->npc.mName, send, true, maxNameLen) ||
!RW(player->passw, send, true, maxPasswLen))
if (!RW(player->npc.mName, send, true, maxNameLength) ||
!RW(player->serverPassword, send, true, maxPasswordLength))
{
packetValid = false;
return;

@ -16,8 +16,8 @@ namespace mwmp
virtual void Packet(RakNet::BitStream *bs, bool send);
const static uint32_t maxNameLen = 256;
const static uint32_t maxPasswLen = 256;
const static uint32_t maxNameLength = 256;
const static uint32_t maxPasswordLength = 256;
};
}

@ -1,17 +0,0 @@
#include "PacketCellReplace.hpp"
#include <components/openmw-mp/NetworkMessages.hpp>
using namespace mwmp;
PacketCellReplace::PacketCellReplace(RakNet::RakPeerInterface *peer) : WorldstatePacket(peer)
{
packetID = ID_CELL_REPLACE;
orderChannel = CHANNEL_SYSTEM;
}
void PacketCellReplace::Packet(RakNet::BitStream *bs, bool send)
{
WorldstatePacket::Packet(bs, send);
// Placeholder
}

@ -0,0 +1,17 @@
#include "PacketCellReset.hpp"
#include <components/openmw-mp/NetworkMessages.hpp>
using namespace mwmp;
PacketCellReset::PacketCellReset(RakNet::RakPeerInterface *peer) : WorldstatePacket(peer)
{
packetID = ID_CELL_RESET;
orderChannel = CHANNEL_SYSTEM;
}
void PacketCellReset::Packet(RakNet::BitStream *bs, bool send)
{
WorldstatePacket::Packet(bs, send);
// Placeholder
}

@ -1,18 +1,18 @@
#ifndef OPENMW_PACKETCELLREPLACE_HPP
#define OPENMW_PACKETCELLREPLACE_HPP
#ifndef OPENMW_PACKETCELLRESET_HPP
#define OPENMW_PACKETCELLRESET_HPP
#include <components/openmw-mp/Packets/Worldstate/WorldstatePacket.hpp>
#include <components/openmw-mp/NetworkMessages.hpp>
namespace mwmp
{
class PacketCellReplace: public WorldstatePacket
class PacketCellReset: public WorldstatePacket
{
public:
PacketCellReplace(RakNet::RakPeerInterface *peer);
PacketCellReset(RakNet::RakPeerInterface *peer);
virtual void Packet(RakNet::BitStream *bs, bool send);
};
}
#endif //OPENMW_PACKETCELLREPLACE_HPP
#endif //OPENMW_PACKETCELLRESET_HPP

@ -192,15 +192,16 @@ std::string Utils::getArchitectureType()
{
#if defined(__x86_64__) || defined(_M_X64)
return "64-bit";
#elif defined(__i386__) || defined(_M_I86)
#elif defined(__i386__) || defined(_M_I86) || defined(_M_IX86)
return "32-bit";
#elif defined(__ARM_ARCH)
return "ARMv" + __ARM_ARCH;
std::string architectureType = "ARMv" + __ARM_ARCH;
#ifdef __aarch64__
return "64-bit";
architectureType = architectureType + " 64-bit";
#else
return "32-bit";
architectureType = architectureType + " 32-bit";
#endif
return architectureType;
#else
return "Unknown architecture";
#endif
@ -211,7 +212,7 @@ void Utils::printVersion(std::string appName, std::string version, std::string c
cout << appName << " " << version;
cout << " (" << getOperatingSystemType() << " " << getArchitectureType() << ")" << endl;
cout << "Protocol version: " << protocol << endl;
cout << "Commit hash: " << commitHash.substr(0, 10) << endl;
cout << "Oldest compatible commit hash: " << commitHash.substr(0, 10) << endl;
cout << "------------------------------------------------------------" << endl;
}

@ -7,6 +7,7 @@
#define TES3MP_DEFAULT_PASSW "SuperPassword"
#define TES3MP_MASTERSERVER_PASSW "12345"
#define TES3MP_CREDITS_CHECKSUM "BC39D2E9"
#define TES3MP_CREDITS_CHECKSUM "BAEFF920"
#endif //OPENMW_VERSION_HPP

@ -4,14 +4,14 @@ tes3mp Credits
Programmers
----------------
Stanislav Zhukov (Koncord) - Overall architecture, networking & scripting systems, player sync, server browser & master server
Stanislav Zhukov (Koncord) - Architecture, networking & scripting systems, player sync, server browser & master server
David Cernat - World, NPC & quest sync, player sync improvements, state saving & loading, extensive bug fixes
Additional programming
----------------------
Grim Kriegor - Lua teleportation commands, early script fixes
Grim Kriegor - Linux deployment scripts, Lua teleportation commands, early script fixes
Battlerax - Various small fixes
@ -26,6 +26,7 @@ Community administrators
Community moderators
--------------------
Lysol
Michael Fitzmayer (mupf)
Nac
NicholasAH
@ -53,6 +54,7 @@ Translation
Super special thanks
--------------------
Alexander Ovsyannikov
Bret Curtis (psi29a)
Gabriel Pascu (iGrebla)
greetasdf
@ -73,8 +75,9 @@ Special thanks
Gluka
Goodevil
Ignatious
James Wards of Gore Corps LAN Club (www.gorecorps.co.nz)
James Wards of Gore Corps LAN Club (gorecorps.co.nz)
Jeremiah
Kyle Willey of Loreshaper Games (steempeak.com/@loreshapergames)
Lewis Sadlier
Luc Keating
Michael Zagar (Zoops)
@ -84,6 +87,7 @@ Special thanks
Scorcio
Simon Nemes
Texafornian
Thrud
Zaphida
All the developers of OpenMW for creating an amazing open source project

Loading…
Cancel
Save