Merge pull request #373 from TES3MP/0.6.2 while resolving conflicts

# Conflicts:
#	apps/openmw/mwmp/DedicatedActor.cpp
#	apps/openmw/mwmp/processors/player/ProcessorPlayerResurrect.hpp
sol2-server-rewrite
David Cernat 7 years ago
commit d70b93e095

@ -908,23 +908,8 @@ namespace MWGui
window->onFrame(frameDuration); window->onFrame(frameDuration);
} }
/*
Start of tes3mp change (major)
Custom GUI elements added by TES3MP often cause a crash here when their
mMainWidget becomes null, so a temporary fix has been added until a
more appropriate solution is researched
*/
if (!mCurrentModals.empty()) if (!mCurrentModals.empty())
{
if (mCurrentModals.back()->mMainWidget != 0)
mCurrentModals.back()->onFrame(frameDuration); mCurrentModals.back()->onFrame(frameDuration);
else
mCurrentModals.pop_back();
}
/*
End of tes3mp change (major)
*/
mKeyboardNavigation->onFrame(); mKeyboardNavigation->onFrame();

@ -168,6 +168,11 @@ void DedicatedActor::setEquipment()
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
{ {
int count = equipedItems[slot].count;
// If we've somehow received a corrupted item with a count lower than 0, ignore it
if (count < 0) continue;
MWWorld::ContainerStoreIterator it = invStore.getSlot(slot); MWWorld::ContainerStoreIterator it = invStore.getSlot(slot);
const string &packetRefId = equipmentItems[slot].refId; const string &packetRefId = equipmentItems[slot].refId;
@ -188,8 +193,6 @@ void DedicatedActor::setEquipment()
if (packetRefId.empty() || equal) if (packetRefId.empty() || equal)
continue; continue;
int count = equipmentItems[slot].count;
if (hasItem(packetRefId, packetCharge)) if (hasItem(packetRefId, packetCharge))
equipItem(packetRefId, packetCharge); equipItem(packetRefId, packetCharge);
else else

@ -60,6 +60,15 @@ void mwmp::GUIController::cleanUp()
mChat = nullptr; mChat = nullptr;
} }
void mwmp::GUIController::refreshGuiMode(MWGui::GuiMode guiMode)
{
if (MWBase::Environment::get().getWindowManager()->containsMode(guiMode))
{
MWBase::Environment::get().getWindowManager()->removeGuiMode(guiMode);
MWBase::Environment::get().getWindowManager()->pushGuiMode(guiMode);
}
}
void mwmp::GUIController::setupChat(const Settings::Manager &mgr) void mwmp::GUIController::setupChat(const Settings::Manager &mgr)
{ {
assert(mChat == nullptr); assert(mChat == nullptr);
@ -120,8 +129,13 @@ void mwmp::GUIController::setChatVisible(bool chatVisible)
void mwmp::GUIController::showDialogList(const mwmp::BasePlayer::GUIMessageBox &guiMessageBox) void mwmp::GUIController::showDialogList(const mwmp::BasePlayer::GUIMessageBox &guiMessageBox)
{ {
MWBase::WindowManager *windowManager = MWBase::Environment::get().getWindowManager(); MWBase::WindowManager *windowManager = MWBase::Environment::get().getWindowManager();
if (mListBox != NULL)
{
windowManager->removeDialog(mListBox); windowManager->removeDialog(mListBox);
mListBox = nullptr; windowManager->removeCurrentModal(mListBox);
mListBox = NULL;
}
std::vector<std::string> list; std::vector<std::string> list;

@ -37,6 +37,9 @@ namespace mwmp
GUIController(); GUIController();
~GUIController(); ~GUIController();
void cleanUp(); void cleanUp();
void refreshGuiMode(MWGui::GuiMode guiMode);
void setupChat(const Settings::Manager &manager); void setupChat(const Settings::Manager &manager);
void printChatMessage(const mwmp::Chat &chat); void printChatMessage(const mwmp::Chat &chat);

@ -35,6 +35,7 @@
#include "Main.hpp" #include "Main.hpp"
#include "Networking.hpp" #include "Networking.hpp"
#include "CellController.hpp" #include "CellController.hpp"
#include "GUIController.hpp"
#include "MechanicsHelper.hpp" #include "MechanicsHelper.hpp"
using namespace mwmp; using namespace mwmp;
@ -428,6 +429,7 @@ void LocalPlayer::updateEquipment(bool forceUpdate)
{ {
auto &item = equipmentItems[slot]; auto &item = equipmentItems[slot];
MWWorld::ContainerStoreIterator it = invStore.getSlot(slot); MWWorld::ContainerStoreIterator it = invStore.getSlot(slot);
if (it != invStore.end()) if (it != invStore.end())
{ {
if (!::Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), equipmentItems[slot].refId) || forceUpdate) if (!::Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), equipmentItems[slot].refId) || forceUpdate)
@ -753,6 +755,61 @@ void LocalPlayer::removeSpells()
} }
} }
void LocalPlayer::resurrect()
{
creatureStats.mDead = false;
MWBase::World *world = MWBase::Environment::get().getWorld();
MWWorld::Ptr ptrPlayer = getPlayerPtr();
switch (player->resurrectType)
{
case ResurrectType::Regular:
break;
case ResurrectType::ImperialShrine:
world->teleportToClosestMarker(ptrPlayer, "divinemarker");
break;
case ResurrectType::TribunalTemple:
world->teleportToClosestMarker(ptrPlayer, "templemarker");
break;
}
ptrPlayer.getClass().getCreatureStats(ptrPlayer).resurrect();
// The player could have died from a hand-to-hand attack, so reset their fatigue
// as well
if (creatureStats.mDynamic[2].mMod < 1)
creatureStats.mDynamic[2].mMod = 1;
creatureStats.mDynamic[2].mCurrent = creatureStats.mDynamic[2].mMod;
MWMechanics::DynamicStat<float> fatigue;
fatigue.readState(creatureStats.mDynamic[2]);
ptrPlayer.getClass().getCreatureStats(ptrPlayer).setFatigue(fatigue);
// If this player had a weapon or spell readied when dying, they will still have it
// readied but be unable to use it unless we clear it here
ptrPlayer.getClass().getNpcStats(ptrPlayer).setDrawState(MWMechanics::DrawState_Nothing);
// Record that the player has died since the last attempt was made to arrest them,
// used to make guards lenient enough to attempt an arrest again
diedSinceArrestAttempt = true;
LOG_APPEND(Log::LOG_INFO, "- diedSinceArrestAttempt is now true");
// Ensure we unequip any items with constant effects that can put us into an infinite
// death loop
MechanicsHelper::unequipItemsByEffect(ptrPlayer, ESM::Enchantment::ConstantEffect, ESM::MagicEffect::DrainHealth);
MechanicsHelper::unequipItemsByEffect(ptrPlayer, ESM::Enchantment::ConstantEffect, ESM::MagicEffect::FireDamage);
MechanicsHelper::unequipItemsByEffect(ptrPlayer, ESM::Enchantment::ConstantEffect, ESM::MagicEffect::FrostDamage);
MechanicsHelper::unequipItemsByEffect(ptrPlayer, ESM::Enchantment::ConstantEffect, ESM::MagicEffect::ShockDamage);
MechanicsHelper::unequipItemsByEffect(ptrPlayer, ESM::Enchantment::ConstantEffect, ESM::MagicEffect::SunDamage);
Main::get().getNetworking()->getPlayerPacket(ID_PLAYER_RESURRECT)->setPlayer(this);
Main::get().getNetworking()->getPlayerPacket(ID_PLAYER_RESURRECT)->Send();
updateStatsDynamic(true);
}
void LocalPlayer::closeInventoryWindows() void LocalPlayer::closeInventoryWindows()
{ {
if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Container) || if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Container) ||
@ -781,47 +838,67 @@ void LocalPlayer::setDynamicStats()
void LocalPlayer::setAttributes() void LocalPlayer::setAttributes()
{ {
MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr ptrPlayer = getPlayerPtr();
MWWorld::Ptr ptrPlayer = world->getPlayerPtr();
MWMechanics::CreatureStats *ptrCreatureStats = &ptrPlayer.getClass().getCreatureStats(ptrPlayer); MWMechanics::CreatureStats *ptrCreatureStats = &ptrPlayer.getClass().getCreatureStats(ptrPlayer);
MWMechanics::AttributeValue attributeValue; MWMechanics::AttributeValue attributeValue;
for (int i = 0; i < 8; ++i) for (int attributeIndex = 0; attributeIndex < 8; ++attributeIndex)
{ {
// If the server wants to clear our attribute's non-zero modifier, we need to remove // 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 spell effect causing it, to avoid an infinite loop where the effect keeps resetting
// the modifier // the modifier
if (creatureStats.mAttributes[i].mMod == 0 && ptrCreatureStats->getAttribute(i).getModifier() > 0) if (creatureStats.mAttributes[attributeIndex].mMod == 0 && ptrCreatureStats->getAttribute(attributeIndex).getModifier() > 0)
ptrCreatureStats->getActiveSpells().purgeEffectByArg(ESM::MagicEffect::FortifyAttribute, i); {
ptrCreatureStats->getActiveSpells().purgeEffectByArg(ESM::MagicEffect::FortifyAttribute, attributeIndex);
MWBase::Environment::get().getMechanicsManager()->updateMagicEffects(ptrPlayer);
attributeValue.readState(creatureStats.mAttributes[i]); // Is the modifier for this attribute still higher than 0? If so, unequip items that
ptrCreatureStats->setAttribute(i, attributeValue); // fortify the attribute
if (ptrCreatureStats->getAttribute(attributeIndex).getModifier() > 0)
{
MechanicsHelper::unequipItemsByEffect(ptrPlayer, ESM::Enchantment::ConstantEffect, ESM::MagicEffect::FortifyAttribute, attributeIndex, -1);
mwmp::Main::get().getGUIController()->refreshGuiMode(MWGui::GM_Inventory);
}
}
attributeValue.readState(creatureStats.mAttributes[attributeIndex]);
ptrCreatureStats->setAttribute(attributeIndex, attributeValue);
} }
} }
void LocalPlayer::setSkills() void LocalPlayer::setSkills()
{ {
MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr ptrPlayer = getPlayerPtr();
MWWorld::Ptr ptrPlayer = world->getPlayerPtr();
MWMechanics::NpcStats *ptrNpcStats = &ptrPlayer.getClass().getNpcStats(ptrPlayer); MWMechanics::NpcStats *ptrNpcStats = &ptrPlayer.getClass().getNpcStats(ptrPlayer);
MWMechanics::SkillValue skillValue; MWMechanics::SkillValue skillValue;
for (int i = 0; i < 27; ++i) for (int skillIndex = 0; skillIndex < 27; ++skillIndex)
{ {
// If the server wants to clear our skill's non-zero modifier, we need to remove // If the server wants to clear our skill's non-zero modifier, we need to remove
// the spell effect causing it, to avoid an infinite loop where the effect keeps resetting // the spell effect causing it, to avoid an infinite loop where the effect keeps resetting
// the modifier // the modifier
if (npcStats.mSkills[i].mMod == 0 && ptrNpcStats->getSkill(i).getModifier() > 0) if (npcStats.mSkills[skillIndex].mMod == 0 && ptrNpcStats->getSkill(skillIndex).getModifier() > 0)
ptrNpcStats->getActiveSpells().purgeEffectByArg(ESM::MagicEffect::FortifySkill, i); {
ptrNpcStats->getActiveSpells().purgeEffectByArg(ESM::MagicEffect::FortifySkill, skillIndex);
MWBase::Environment::get().getMechanicsManager()->updateMagicEffects(ptrPlayer);
skillValue.readState(npcStats.mSkills[i]); // Is the modifier for this skill still higher than 0? If so, unequip items that
ptrNpcStats->setSkill(i, skillValue); // fortify the skill
if (ptrNpcStats->getSkill(skillIndex).getModifier() > 0)
{
MechanicsHelper::unequipItemsByEffect(ptrPlayer, ESM::Enchantment::ConstantEffect, ESM::MagicEffect::FortifySkill, -1, skillIndex);
mwmp::Main::get().getGUIController()->refreshGuiMode(MWGui::GM_Inventory);
}
} }
for (int i = 0; i < 8; ++i) skillValue.readState(npcStats.mSkills[skillIndex]);
ptrNpcStats->setSkillIncrease(i, npcStats.mSkillIncrease[i]); ptrNpcStats->setSkill(skillIndex, skillValue);
}
for (int attributeIndex = 0; attributeIndex < 8; ++attributeIndex)
ptrNpcStats->setSkillIncrease(attributeIndex, npcStats.mSkillIncrease[attributeIndex]);
ptrNpcStats->setLevelProgress(npcStats.mLevelProgress); ptrNpcStats->setLevelProgress(npcStats.mLevelProgress);
} }
@ -918,6 +995,8 @@ void LocalPlayer::setCell()
void LocalPlayer::setClass() void LocalPlayer::setClass()
{ {
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Received ID_PLAYER_CLASS from server");
if (charClass.mId.empty()) // custom class if (charClass.mId.empty()) // custom class
{ {
charClass.mData.mIsPlayable = 0x1; charClass.mData.mIsPlayable = 0x1;
@ -926,13 +1005,16 @@ void LocalPlayer::setClass()
} }
else else
{ {
MWBase::Environment::get().getMechanicsManager()->setPlayerClass(charClass.mId); const ESM::Class *existingCharClass = MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().search(charClass.mId);
const ESM::Class *existingCharClass = MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find(charClass.mId);
if (existingCharClass) if (existingCharClass)
{
MWBase::Environment::get().getMechanicsManager()->setPlayerClass(charClass.mId);
MWBase::Environment::get().getWindowManager()->setPlayerClass(charClass); MWBase::Environment::get().getWindowManager()->setPlayerClass(charClass);
} }
else
LOG_APPEND(Log::LOG_INFO, "- Ignored invalid default class %s", charClass.mId.c_str());
}
} }
void LocalPlayer::setEquipment() void LocalPlayer::setEquipment()
@ -1074,8 +1156,18 @@ void LocalPlayer::setFactions()
MWWorld::Ptr ptrPlayer = getPlayerPtr(); MWWorld::Ptr ptrPlayer = getPlayerPtr();
MWMechanics::NpcStats &ptrNpcStats = ptrPlayer.getClass().getNpcStats(ptrPlayer); MWMechanics::NpcStats &ptrNpcStats = ptrPlayer.getClass().getNpcStats(ptrPlayer);
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Received ID_PLAYER_FACTION from server\n- action: %i", factionChanges.action);
for (const auto &faction : factionChanges.factions) for (const auto &faction : factionChanges.factions)
{ {
const ESM::Faction *esmFaction = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().search(faction.factionId);
if (!esmFaction)
{
LOG_APPEND(Log::LOG_INFO, "- Ignored invalid faction %s", faction.factionId.c_str());
continue;
}
// If the player isn't in this faction, make them join it // If the player isn't in this faction, make them join it
if (!ptrNpcStats.isInFaction(faction.factionId)) if (!ptrNpcStats.isInFaction(faction.factionId))
ptrNpcStats.joinFaction(faction.factionId); ptrNpcStats.joinFaction(faction.factionId);
@ -1145,18 +1237,18 @@ void LocalPlayer::setShapeshift()
void LocalPlayer::sendClass() void LocalPlayer::sendClass()
{ {
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
const ESM::NPC *cpl = world->getPlayerPtr().get<ESM::NPC>()->mBase; const ESM::NPC *npcBase = world->getPlayerPtr().get<ESM::NPC>()->mBase;
const ESM::Class *cls = world->getStore().get<ESM::Class>().find(cpl->mClass); const ESM::Class *esmClass = world->getStore().get<ESM::Class>().find(npcBase->mClass);
if (cpl->mClass.find("$dynamic") != string::npos) // custom class if (npcBase->mClass.find("$dynamic") != string::npos) // custom class
{ {
charClass.mId = ""; charClass.mId = "";
charClass.mName = cls->mName; charClass.mName = esmClass->mName;
charClass.mDescription = cls->mDescription; charClass.mDescription = esmClass->mDescription;
charClass.mData = cls->mData; charClass.mData = esmClass->mData;
} }
else else
charClass.mId = cls->mId; charClass.mId = esmClass->mId;
getNetworking()->getPlayerPacket(ID_PLAYER_CHARCLASS)->setPlayer(this); getNetworking()->getPlayerPacket(ID_PLAYER_CHARCLASS)->setPlayer(this);
getNetworking()->getPlayerPacket(ID_PLAYER_CHARCLASS)->Send(); getNetworking()->getPlayerPacket(ID_PLAYER_CHARCLASS)->Send();

@ -46,6 +46,8 @@ namespace mwmp
void removeItems(); void removeItems();
void removeSpells(); void removeSpells();
void resurrect();
void closeInventoryWindows(); void closeInventoryWindows();
void setDynamicStats(); void setDynamicStats();

@ -213,3 +213,45 @@ void MechanicsHelper::processAttack(Attack attack, const MWWorld::Ptr& attacker)
LOG_APPEND(Log::LOG_VERBOSE, " - success: %d", attack.success); LOG_APPEND(Log::LOG_VERBOSE, " - success: %d", attack.success);
} }
} }
bool MechanicsHelper::doesEffectListContainEffect(const ESM::EffectList& effectList, short effectId, short attributeId, short skillId)
{
for (const auto &effect : effectList.mList)
{
if (effect.mEffectID == effectId)
{
if (attributeId == -1 || effect.mAttribute == attributeId)
{
if (skillId == -1 || effect.mSkill == skillId)
{
return true;
}
}
}
}
return false;
}
void MechanicsHelper::unequipItemsByEffect(const MWWorld::Ptr& ptr, short enchantmentType, short effectId, short attributeId, short skillId)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
MWWorld::InventoryStore &ptrInventory = ptr.getClass().getInventoryStore(ptr);
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; slot++)
{
if (ptrInventory.getSlot(slot) != ptrInventory.end())
{
MWWorld::ConstContainerStoreIterator itemIterator = ptrInventory.getSlot(slot);
std::string enchantmentName = itemIterator->getClass().getEnchantment(*itemIterator);
if (!enchantmentName.empty())
{
const ESM::Enchantment* enchantment = world->getStore().get<ESM::Enchantment>().find(enchantmentName);
if (enchantment->mData.mType == enchantmentType && doesEffectListContainEffect(enchantment->mEffects, effectId, attributeId, skillId))
ptrInventory.unequipSlot(slot, ptr);
}
}
}
}

@ -23,6 +23,9 @@ namespace MechanicsHelper
bool getSpellSuccess(std::string spellId, const MWWorld::Ptr& caster); bool getSpellSuccess(std::string spellId, const MWWorld::Ptr& caster);
void processAttack(mwmp::Attack attack, const MWWorld::Ptr& attacker); void processAttack(mwmp::Attack attack, const MWWorld::Ptr& attacker);
bool doesEffectListContainEffect(const ESM::EffectList& effectList, short effectId, short attributeId = -1, short skillId = -1);
void unequipItemsByEffect(const MWWorld::Ptr& ptr, short enchantmentType, short effectId, short attributeId = -1, short skillId = -1);
} }

@ -27,53 +27,7 @@ namespace mwmp
{ {
LOG_APPEND(Log::LOG_INFO, "- Packet was about me with resurrectType of %i", (int) player->resurrectType); LOG_APPEND(Log::LOG_INFO, "- Packet was about me with resurrectType of %i", (int) player->resurrectType);
player->creatureStats.mDead = false; static_cast<LocalPlayer*>(player)->resurrect();
MWBase::World *world = MWBase::Environment::get().getWorld();
MWWorld::Ptr playerPtr = world->getPlayerPtr();
switch(player->resurrectType)
{
case ResurrectType::Regular:
break;
case ResurrectType::ImperialShrine:
world->teleportToClosestMarker(playerPtr, "divinemarker");
break;
case ResurrectType::TribunalTemple:
world->teleportToClosestMarker(playerPtr, "templemarker");
break;
}
playerPtr.getClass().getCreatureStats(playerPtr).resurrect();
// The player could have died from a hand-to-hand attack, so reset their fatigue
// as well
if (player->creatureStats.mDynamic[2].mMod < 1)
player->creatureStats.mDynamic[2].mMod = 1;
player->creatureStats.mDynamic[2].mCurrent = player->creatureStats.mDynamic[2].mMod;
MWMechanics::DynamicStat<float> fatigue;
fatigue.readState(player->creatureStats.mDynamic[2]);
playerPtr.getClass().getCreatureStats(playerPtr).setFatigue(fatigue);
// If this player had a weapon or spell readied when dying, they will still have it
// readied but be unable to use it unless we clear it here
playerPtr.getClass().getNpcStats(playerPtr).setDrawState(MWMechanics::DrawState_Nothing);
// Record that the player has died since the last attempt was made to arrest them,
// used to make guards lenient enough to attempt an arrest again
player->diedSinceArrestAttempt = true;
LOG_APPEND(Log::LOG_INFO, "- diedSinceArrestAttempt is now true");
packet.setPlayer(player);
packet.Send(serverAddr);
static_cast<LocalPlayer*>(player)->updateStatsDynamic(true);
Main::get().getNetworking()->getPlayerPacket(ID_PLAYER_STATS_DYNAMIC)->setPlayer(player);
Main::get().getNetworking()->getPlayerPacket(ID_PLAYER_STATS_DYNAMIC)->Send(serverAddr);
} }
else if (player != 0) else if (player != 0)
{ {

Loading…
Cancel
Save