Merge pull request #1 from TES3MP/master

tes3mp changes
pull/163/head
Battlerax 8 years ago committed by GitHub
commit ba4b77f2c3

@ -3,9 +3,9 @@ TES3MP
[![Build Status](https://travis-ci.org/TES3MP/openmw-tes3mp.svg?branch=master)](https://travis-ci.org/TES3MP/openmw-tes3mp) [![Build Status](https://travis-ci.org/TES3MP/openmw-tes3mp.svg?branch=master)](https://travis-ci.org/TES3MP/openmw-tes3mp)
TES3MP is a project aiming to add multiplayer functionality to [OpenMW](https://github.com/OpenMW/openmw), a free and open source recreation of the popular Bethesda Softworks' game "The Elder Scrolls III: Morrowind". TES3MP is a project aiming to add multiplayer functionality to [OpenMW](https://github.com/OpenMW/openmw), a free and open source recreation of the popular Bethesda Softworks game "The Elder Scrolls III: Morrowind".
* Version: 0.4.2 * Version: 0.5.0
* License: GPLv3 (see docs/license/GPL3.txt for more information) * License: GPLv3 (see docs/license/GPL3.txt for more information)
* Website: https://steamcommunity.com/groups/mwmulti * Website: https://steamcommunity.com/groups/mwmulti
@ -17,14 +17,16 @@ Project Status
[Version changelog](https://github.com/TES3MP/openmw-tes3mp/blob/master/tes3mp-changelog.md) [Version changelog](https://github.com/TES3MP/openmw-tes3mp/blob/master/tes3mp-changelog.md)
We are still in an early phase of development. At the moment we have synchronization of character appearance, character skills, stats, attributes and death as well as movement on interiors and exteriors, melee and ranged combat, picking up and dropping items in the world and using doors and levers. Our project is not yet in a playable state, though we are getting close. At the moment we have synchronization of character appearance, character skills, stats, attributes and death, movement in interiors and exteriors, melee and ranged combat, spell casting, picking up and dropping items in the world, using doors and levers, and adding and removing items from containers, as well as [serverside Lua scripts](https://github.com/TES3MP/PluginExamples) used to save and load the state of most of the aforementioned.
Contributing Contributing
-------------- --------------
Development has been somewhat fast, but any contribution regarding [code](https://github.com/TES3MP/openmw-tes3mp/blob/master/CONTRIBUTING.md), documentation, bug hunting, artwork or nice Jobasha's gift cards are greatly appreciated. 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.
Test sessions are often advertised in our Steam Group, there is also a chat room there.
Feel free to contact any of the [team members](https://github.com/TES3MP/openmw-tes3mp/blob/master/tes3mp-credits.md) for any questions you might have. Test sessions are often advertised in [our Steam group](https://steamcommunity.com/groups/mwmulti) or [our Discord server](https://discord.gg/H8zhhuk).
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
--------------- ---------------
@ -36,4 +38,4 @@ Getting Started
Donations Donations
--------------- ---------------
You can benefit the project by supporting OpenMW and or by [becoming Koncord's patron](https://www.patreon.com/Koncord). You can benefit the project by supporting OpenMW and/or by [becoming Koncord's patron](https://www.patreon.com/Koncord).

@ -125,6 +125,15 @@ void MainWindow::play()
QStringList arguments; QStringList arguments;
arguments.append(QLatin1String("--connect=") + sm->myData[sourceId].addr.toLatin1()); arguments.append(QLatin1String("--connect=") + sm->myData[sourceId].addr.toLatin1());
if(sm->myData[sourceId].needPassw)
{
bool ok;
QString passw = QInputDialog::getText(this, "Connecting to: " + sm->myData[sourceId].addr, "Password: ", QLineEdit::Password, "", &ok);
if(!ok)
return;
arguments.append(QLatin1String("--password=") + passw.toLatin1());
}
if (mGameInvoker->startProcess(QLatin1String("tes3mp"), arguments, true)) if (mGameInvoker->startProcess(QLatin1String("tes3mp"), arguments, true))
return qApp->quit(); return qApp->quit();
} }

@ -78,6 +78,12 @@ void NetController::setData(QString address, QJsonObject server, ServerModel *mo
mi = model->index(0, ServerData::MODNAME); mi = model->index(0, ServerData::MODNAME);
model->setData(mi, server["modname"].toString()); model->setData(mi, server["modname"].toString());
mi = model->index(0, ServerData::VERSION);
model->setData(mi, server["version"].toString());
mi = model->index(0, ServerData::PASSW);
model->setData(mi, server["passw"].toBool());
mi = model->index(0, ServerData::PING); mi = model->index(0, ServerData::PING);
QStringList addr = address.split(":"); QStringList addr = address.split(":");
@ -149,6 +155,8 @@ bool NetController::downloadInfo(QAbstractItemModel *pModel, QModelIndex index)
qDebug() << server["modname"].toString(); qDebug() << server["modname"].toString();
qDebug() << server["players"].toInt(); qDebug() << server["players"].toInt();
qDebug() << server["max_players"].toInt(); qDebug() << server["max_players"].toInt();
qDebug() << server["version"].toString();
qDebug() << server["passw"].toBool();
QVector<ServerData>::Iterator value = std::find_if(model->myData.begin(), model->myData.end(), pattern(iter.key())); QVector<ServerData>::Iterator value = std::find_if(model->myData.begin(), model->myData.end(), pattern(iter.key()));
if(value == model->myData.end()) if(value == model->myData.end())
@ -209,6 +217,8 @@ void NetController::updateInfo()
qDebug() << map["modname"].toString(); qDebug() << map["modname"].toString();
qDebug() << map["players"].toInt(); qDebug() << map["players"].toInt();
qDebug() << map["max_players"].toInt(); qDebug() << map["max_players"].toInt();
qDebug() << map["version"].toString();
qDebug() << map["passw"].toBool();
sd->hostName = map["hostname"].toString(); sd->hostName = map["hostname"].toString();
sd->modName = map["modname"].toString(); sd->modName = map["modname"].toString();

@ -33,6 +33,12 @@ QVariant ServerModel::data(const QModelIndex &index, int role) const
case ServerData::ADDR: case ServerData::ADDR:
var = sd.addr; var = sd.addr;
break; break;
case ServerData::PASSW:
var = sd.needPassw ? "Yes" : "No";
break;
case ServerData::VERSION:
var = sd.version;
break;
case ServerData::PLAYERS: case ServerData::PLAYERS:
var = sd.players; var = sd.players;
break; break;
@ -75,6 +81,12 @@ QVariant ServerModel::headerData(int section, Qt::Orientation orientation, int r
case ServerData::ADDR: case ServerData::ADDR:
var = "Address"; var = "Address";
break; break;
case ServerData::PASSW:
var = "Password";
break;
case ServerData::VERSION:
var = "Version";
break;
case ServerData::HOSTNAME: case ServerData::HOSTNAME:
var = "Host name"; var = "Host name";
break; break;
@ -82,7 +94,7 @@ QVariant ServerModel::headerData(int section, Qt::Orientation orientation, int r
var = "Players"; var = "Players";
break; break;
case ServerData::MAX_PLAYERS: case ServerData::MAX_PLAYERS:
var = "Player Max"; var = "Max players";
break; break;
case ServerData::PING: case ServerData::PING:
var = "Ping"; var = "Ping";
@ -120,6 +132,13 @@ bool ServerModel::setData(const QModelIndex &index, const QVariant &value, int r
sd.addr = value.toString(); sd.addr = value.toString();
ok = !sd.addr.isEmpty(); ok = !sd.addr.isEmpty();
break; break;
case ServerData::PASSW:
sd.needPassw = value.toBool();
break;
case ServerData::VERSION:
sd.version = value.toString();
ok = !sd.addr.isEmpty();
break;
case ServerData::PLAYERS: case ServerData::PLAYERS:
sd.players = value.toInt(&ok); sd.players = value.toInt(&ok);
break; break;
@ -152,7 +171,7 @@ bool ServerModel::insertRows(int position, int count, const QModelIndex &index)
beginInsertRows(QModelIndex(), position, position + count - 1); beginInsertRows(QModelIndex(), position, position + count - 1);
for (int row = 0; row < count; ++row) { for (int row = 0; row < count; ++row) {
ServerData sd {"", -1, -1, -1, "", ""}; ServerData sd {"", -1, -1, -1, "", "", false, 0};
myData.insert(position, sd); myData.insert(position, sd);
} }

@ -13,14 +13,18 @@ struct ServerData
int ping; int ping;
QString hostName; QString hostName;
QString modName; QString modName;
bool needPassw;
QString version;
enum IDS enum IDS
{ {
ADDR, ADDR,
HOSTNAME, HOSTNAME,
PLAYERS, PLAYERS,
MAX_PLAYERS, MAX_PLAYERS,
PASSW,
MODNAME, MODNAME,
PING, PING,
VERSION,
LAST LAST
}; };
}; };

@ -1758,6 +1758,41 @@ namespace CSMWorld
} }
}; };
template<typename ESXRecordT>
struct GenderNpcColumn : public Column<ESXRecordT>
{
GenderNpcColumn()
: Column<ESXRecordT>(Columns::ColumnId_GenderNpc, ColumnBase::Display_GenderNpc)
{}
virtual QVariant get(const Record<ESXRecordT>& record) const
{
// Implemented this way to allow additional gender types in the future.
if ((record.get().mData.mFlags & ESM::BodyPart::BPF_Female) == ESM::BodyPart::BPF_Female)
return 1;
return 0;
}
virtual void set(Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
// Implemented this way to allow additional gender types in the future.
if (data.toInt() == 1)
record2.mData.mFlags = (record2.mData.mFlags & ~ESM::BodyPart::BPF_Female) | ESM::BodyPart::BPF_Female;
else
record2.mData.mFlags = record2.mData.mFlags & ~ESM::BodyPart::BPF_Female;
record.setModified(record2);
}
virtual bool isEditable() const
{
return true;
}
};
template<typename ESXRecordT> template<typename ESXRecordT>
struct EnchantmentTypeColumn : public Column<ESXRecordT> struct EnchantmentTypeColumn : public Column<ESXRecordT>
{ {

@ -363,7 +363,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
mBodyParts.addColumn (new FixedRecordTypeColumn<ESM::BodyPart> (UniversalId::Type_BodyPart)); mBodyParts.addColumn (new FixedRecordTypeColumn<ESM::BodyPart> (UniversalId::Type_BodyPart));
mBodyParts.addColumn (new BodyPartTypeColumn<ESM::BodyPart>); mBodyParts.addColumn (new BodyPartTypeColumn<ESM::BodyPart>);
mBodyParts.addColumn (new VampireColumn<ESM::BodyPart>); mBodyParts.addColumn (new VampireColumn<ESM::BodyPart>);
mBodyParts.addColumn (new FlagColumn<ESM::BodyPart> (Columns::ColumnId_Female, ESM::BodyPart::BPF_Female)); mBodyParts.addColumn(new GenderNpcColumn<ESM::BodyPart>);
mBodyParts.addColumn (new FlagColumn<ESM::BodyPart> (Columns::ColumnId_Playable, mBodyParts.addColumn (new FlagColumn<ESM::BodyPart> (Columns::ColumnId_Playable,
ESM::BodyPart::BPF_NotPlayable, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, true)); ESM::BodyPart::BPF_NotPlayable, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, true));

@ -35,9 +35,10 @@ if(BUILD_WITH_PAWN)
endif(BUILD_WITH_PAWN) endif(BUILD_WITH_PAWN)
option(BUILD_WITH_LUA "Enable Terra/Lua language" ON) option(BUILD_WITH_LUA "Enable Terra/Lua language" ON)
option(FORCE_LUA "Use Lua instead Terra" OFF)
if(BUILD_WITH_LUA) if(BUILD_WITH_LUA)
#set(Terra_ROOT ${CMAKE_SOURCE_DIR}/external/terra/) #set(Terra_ROOT ${CMAKE_SOURCE_DIR}/external/terra/)
if(WIN32) if(WIN32 OR FORCE_LUA)
find_package(Lua51 REQUIRED) find_package(Lua51 REQUIRED)
MESSAGE(STATUS "Found LUA_LIBRARY: ${LUA_LIBRARY}") MESSAGE(STATUS "Found LUA_LIBRARY: ${LUA_LIBRARY}")
MESSAGE(STATUS "Found LUA_INCLUDE_DIR: ${LUA_INCLUDE_DIR}") MESSAGE(STATUS "Found LUA_INCLUDE_DIR: ${LUA_INCLUDE_DIR}")
@ -69,6 +70,7 @@ set(SERVER
Networking.cpp Networking.cpp
Utils.cpp Utils.cpp
MasterClient.cpp MasterClient.cpp
Cell.cpp
Script/Script.cpp Script/ScriptFunction.cpp Script/Script.cpp Script/ScriptFunction.cpp
Script/ScriptFunctions.cpp Script/ScriptFunctions.cpp

@ -0,0 +1,223 @@
//
// Created by koncord on 18.02.17.
//
#include "Cell.hpp"
#include <iostream>
#include "Player.hpp"
using namespace std;
void Cell::addPlayer(Player *player)
{
auto it = find(player->cells.begin(), player->cells.end(), this);
if (it == player->cells.end())
player->cells.push_back(this);
players.push_back(player);
}
void Cell::removePlayer(Player *player)
{
for (Iterator it = begin(); it != end(); it++)
{
if (*it == player)
{
auto it2 = find(player->cells.begin(), player->cells.end(), this);
if (it2 != player->cells.end())
player->cells.erase(it2);
players.erase(it);
return;
}
}
}
Cell::TPlayers Cell::getPlayers()
{
return players;
}
void Cell::sendToLoaded(mwmp::WorldPacket *worldPacket, mwmp::BaseEvent *baseEvent)
{
std::list <Player*> plList;
for (auto pl :getPlayers())
plList.push_back(pl);
plList.sort();
plList.unique();
for (auto pl : plList)
{
if (pl->guid == baseEvent->guid) continue;
worldPacket->Send(baseEvent, pl->guid);
}
}
std::string Cell::getDescription() const
{
return cell.getDescription();
}
CellController::CellController()
{
}
CellController::~CellController()
{
}
CellController *CellController::sThis = nullptr;
void CellController::create()
{
sThis = new CellController;
}
void CellController::destroy()
{
assert(sThis);
delete sThis;
sThis = nullptr;
}
CellController *CellController::get()
{
return sThis;
}
Cell *CellController::getCell(ESM::Cell *esmCell)
{
if (esmCell->isExterior())
return getCellByXY(esmCell->mData.mX, esmCell->mData.mY);
else
return getCellByName(esmCell->mName);
}
Cell *CellController::getCellByXY(int x, int y)
{
auto it = find_if(cells.begin(), cells.end(), [x, y](const Cell *c) {
return c->cell.mData.mX == x && c->cell.mData.mY == y;
});
if (it == cells.end())
return nullptr;
return *it;
}
Cell *CellController::getCellByName(std::string cellName)
{
auto it = find_if(cells.begin(), cells.end(), [cellName](const Cell *c) {
return c->cell.mName == cellName;
});
if (it == cells.end())
return nullptr;
return *it;
}
Cell *CellController::addCell(ESM::Cell cellData)
{
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Loaded cells: %d", cells.size());
auto it = find_if(cells.begin(), cells.end(), [cellData](const Cell *c) {
//return c->cell.sRecordId == cellData.sRecordId; // Currently we cannot compare because plugin lists can be loaded in different order
return c->cell.mData.mX == cellData.mData.mX && c->cell.mData.mY == cellData.mData.mY &&
c->cell.mCellId.mWorldspace == cellData.mCellId.mWorldspace;
});
Cell *cell;
if (it == cells.end())
{
cell = new Cell(cellData);
cells.push_back(cell);
}
else
cell = *it;
return cell;
}
void CellController::removeCell(Cell *cell)
{
if (cell == nullptr)
return;
for (auto it = cells.begin(); it != cells.end();)
{
if (*it != nullptr && *it == cell)
{
delete *it;
it = cells.erase(it);
}
else
++it;
}
}
void CellController::removePlayer(Cell *cell, Player *player)
{
cell->removePlayer(player);
if (cell->players.empty())
{
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Deleting empty cell from memory: %s", player->npc.mName.c_str(),
player->getId(), cell->cell.getDescription().c_str());
auto it = find(cells.begin(), cells.end(), cell);
delete *it;
cells.erase(it);
}
}
void CellController::deletePlayer(Player *player)
{
for_each(player->getCells()->begin(), player->getCells()->end(), [&player](Cell *cell) {
for (auto it = cell->begin(); it != cell->end(); ++it)
{
if (*it == player)
{
cell->players.erase(it);
break;
}
}
});
}
void CellController::update(Player *player)
{
for (auto cell : player->cellStateChanges.cellStates)
{
if (cell.type == mwmp::CellState::LOAD)
{
Cell *c = addCell(cell.cell);
c->addPlayer(player);
}
else
{
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Player %s (%d) unloaded cell: %s", player->npc.mName.c_str(), player->getId(), cell.cell.getDescription().c_str());
Cell *c;
if (!cell.cell.isExterior())
c = getCellByName(cell.cell.mName);
else
c = getCellByXY(cell.cell.getGridX(), cell.cell.getGridY());
if (c != nullptr)
removePlayer(c, player);
}
}
}
Cell::Cell(ESM::Cell cell): cell(cell)
{
}
Cell::Iterator Cell::begin() const
{
return players.begin();
}
Cell::Iterator Cell::end() const
{
return players.end();
}

@ -0,0 +1,76 @@
//
// Created by koncord on 18.02.17.
//
#ifndef OPENMW_CELL_HPP
#define OPENMW_CELL_HPP
#include <deque>
#include <string>
#include <components/esm/records.hpp>
#include <components/openmw-mp/Base/BaseEvent.hpp>
#include <components/openmw-mp/Packets/World/WorldPacket.hpp>
class Player;
class Cell;
class CellController
{
private:
CellController();
~CellController();
CellController(CellController&); // not used
public:
static void create();
static void destroy();
static CellController *get();
public:
typedef std::deque<Cell*> TContainer;
typedef TContainer::iterator TIter;
Cell * addCell(ESM::Cell cell);
void removeCell(Cell *);
void removePlayer(Cell *cell, Player *player);
void deletePlayer(Player *player);
Cell *getCell(ESM::Cell *esmCell);
Cell *getCellByXY(int x, int y);
Cell *getCellByName(std::string cellName);
void update(Player *player);
private:
static CellController *sThis;
TContainer cells;
};
class Cell
{
friend class CellController;
public:
Cell(ESM::Cell cell);
typedef std::deque<Player*> TPlayers;
typedef TPlayers::const_iterator Iterator;
Iterator begin() const;
Iterator end() const;
void addPlayer(Player *player);
void removePlayer(Player *player);
TPlayers getPlayers();
void sendToLoaded(mwmp::WorldPacket *worldPacket, mwmp::BaseEvent *baseEvent);
std::string getDescription() const;
private:
TPlayers players;
ESM::Cell cell;
};
#endif //OPENMW_CELL_HPP

@ -10,6 +10,7 @@
#include <RakPeerInterface.h> #include <RakPeerInterface.h>
#include "MasterClient.hpp" #include "MasterClient.hpp"
#include <components/openmw-mp/Log.hpp> #include <components/openmw-mp/Log.hpp>
#include <components/openmw-mp/Version.hpp>
#include "Networking.hpp" #include "Networking.hpp"
using namespace std; using namespace std;
@ -69,7 +70,9 @@ MasterClient::Send(std::string hostname, std::string modname, unsigned maxPlayer
sstr << "\"hostname\": \"" << hostname.c_str() << "\", "; sstr << "\"hostname\": \"" << hostname.c_str() << "\", ";
sstr << "\"modname\": \"" << modname.c_str() << "\", "; sstr << "\"modname\": \"" << modname.c_str() << "\", ";
sstr << "\"players\": " << players << ", "; sstr << "\"players\": " << players << ", ";
sstr << "\"max_players\": " << maxPlayers; sstr << "\"max_players\": " << maxPlayers << ", ";
sstr << "\"version\": \"" << TES3MP_VERSION << "\", ";
sstr << "\"passw\": " << (mwmp::Networking::get().isPassworded() ? "true" : "false");
sstr << "}"; sstr << "}";
mutexData.unlock(); mutexData.unlock();
@ -152,7 +155,7 @@ void MasterClient::Update()
{ {
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Update rate is too low, and the master server has deleted information about" LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Update rate is too low, and the master server has deleted information about"
" the server. Trying low rate..."); " the server. Trying low rate...");
if((timeout - step_rate) >= step_rate) if ((timeout - step_rate) >= step_rate)
SetUpdateRate(timeout - step_rate); SetUpdateRate(timeout - step_rate);
update = false; update = false;
} }
@ -177,10 +180,10 @@ void MasterClient::Start()
void MasterClient::Stop() void MasterClient::Stop()
{ {
if(!sRun) if (!sRun)
return; return;
sRun = false; sRun = false;
if(thrQuery.joinable()) if (thrQuery.joinable())
thrQuery.join(); thrQuery.join();
} }

@ -15,6 +15,8 @@
#include "Networking.hpp" #include "Networking.hpp"
#include "MasterClient.hpp" #include "MasterClient.hpp"
#include "Cell.hpp"
#include <components/openmw-mp/Version.hpp>
using namespace mwmp; using namespace mwmp;
using namespace std; using namespace std;
@ -29,6 +31,8 @@ Networking::Networking(RakNet::RakPeerInterface *peer)
this->peer = peer; this->peer = peer;
players = Players::getPlayers(); players = Players::getPlayers();
CellController::create();
playerController = new PlayerPacketController(peer); playerController = new PlayerPacketController(peer);
worldController = new WorldPacketController(peer); worldController = new WorldPacketController(peer);
@ -40,17 +44,31 @@ Networking::Networking(RakNet::RakPeerInterface *peer)
exitCode = 0; exitCode = 0;
Script::Call<Script::CallbackIdentity("OnServerInit")>(); Script::Call<Script::CallbackIdentity("OnServerInit")>();
serverPassword = TES3MP_DEFAULT_PASSW;
} }
Networking::~Networking() Networking::~Networking()
{ {
Script::Call<Script::CallbackIdentity("OnServerExit")>(false); Script::Call<Script::CallbackIdentity("OnServerExit")>(false);
CellController::destroy();
sThis = 0; sThis = 0;
delete playerController; delete playerController;
LOG_QUIT(); LOG_QUIT();
} }
void Networking::setServerPassword(std::string passw) noexcept
{
serverPassword = passw.empty() ? TES3MP_DEFAULT_PASSW : passw;
}
bool Networking::isPassworded() const
{
return serverPassword != TES3MP_DEFAULT_PASSW;
}
void Networking::processPlayerPacket(RakNet::Packet *packet) void Networking::processPlayerPacket(RakNet::Packet *packet)
{ {
Player *player = Players::getPlayer(packet->guid); Player *player = Players::getPlayer(packet->guid);
@ -59,7 +77,6 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
if (packet->data[0] == ID_HANDSHAKE) if (packet->data[0] == ID_HANDSHAKE)
{ {
string passw = "SuperPassword";
myPacket->Read(player); myPacket->Read(player);
@ -72,7 +89,7 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
return; return;
} }
if (player->passw != passw) if (player->passw != serverPassword)
{ {
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Wrong server password for player %d, name: %s (pass: %s)", LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Wrong server password for player %d, name: %s (pass: %s)",
player->getId(), player->getId(),
@ -145,7 +162,10 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
if (!player->creatureStats.mDead) if (!player->creatureStats.mDead)
{ {
myPacket->Read(player); myPacket->Read(player);
myPacket->Send(player, true); //send to other clients //myPacket->Send(player, true); //send to other clients
player->sendToLoaded(myPacket);
} }
break; break;
@ -162,7 +182,27 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
LOG_APPEND(Log::LOG_INFO, "- Moved to %s", LOG_APPEND(Log::LOG_INFO, "- Moved to %s",
player->cell.getDescription().c_str()); player->cell.getDescription().c_str());
player->forEachLoaded([this](Player *pl, Player *other) {
playerController->GetPacket(ID_PLAYER_DYNAMICSTATS)->Send(other, pl->guid);
playerController->GetPacket(ID_PLAYER_ATTRIBUTE)->Send(other, pl->guid);
playerController->GetPacket(ID_PLAYER_POS)->Send(other, pl->guid);
playerController->GetPacket(ID_PLAYER_SKILL)->Send(other, pl->guid);
playerController->GetPacket(ID_PLAYER_EQUIPMENT)->Send(other, pl->guid);
playerController->GetPacket(ID_PLAYER_DRAWSTATE)->Send(other, pl->guid);
playerController->GetPacket(ID_PLAYER_DYNAMICSTATS)->Send(pl, other->guid);
playerController->GetPacket(ID_PLAYER_ATTRIBUTE)->Send(pl, other->guid);
playerController->GetPacket(ID_PLAYER_SKILL)->Send(pl, other->guid);
playerController->GetPacket(ID_PLAYER_EQUIPMENT)->Send(pl, other->guid);
playerController->GetPacket(ID_PLAYER_DRAWSTATE)->Send(pl, other->guid);
LOG_APPEND(Log::LOG_INFO, "- Exchanged information with %s",
other->npc.mName.c_str());
});
playerController->GetPacket(ID_PLAYER_POS)->Send(player);
myPacket->Send(player, true); //send to other clients myPacket->Send(player, true); //send to other clients
Script::Call<Script::CallbackIdentity("OnPlayerCellChange")>(player->getId()); Script::Call<Script::CallbackIdentity("OnPlayerCellChange")>(player->getId());
} }
else else
@ -180,6 +220,8 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
myPacket->Read(player); myPacket->Read(player);
CellController::get()->update(player);
Script::Call<Script::CallbackIdentity("OnPlayerCellState")>(player->getId()); Script::Call<Script::CallbackIdentity("OnPlayerCellState")>(player->getId());
break; break;
@ -190,7 +232,9 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
if (!player->creatureStats.mDead) if (!player->creatureStats.mDead)
{ {
myPacket->Read(player); myPacket->Read(player);
myPacket->Send(player, true); //myPacket->Send(player, true);
player->sendToLoaded(myPacket);
Script::Call<Script::CallbackIdentity("OnPlayerAttributesChange")>(player->getId()); Script::Call<Script::CallbackIdentity("OnPlayerAttributesChange")>(player->getId());
} }
@ -203,7 +247,8 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
if (!player->creatureStats.mDead) if (!player->creatureStats.mDead)
{ {
myPacket->Read(player); myPacket->Read(player);
myPacket->Send(player, true); //myPacket->Send(player, true);
player->sendToLoaded(myPacket);
Script::Call<Script::CallbackIdentity("OnPlayerSkillsChange")>(player->getId()); Script::Call<Script::CallbackIdentity("OnPlayerSkillsChange")>(player->getId());
} }
@ -216,7 +261,7 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
if (!player->creatureStats.mDead) if (!player->creatureStats.mDead)
{ {
myPacket->Read(player); myPacket->Read(player);
myPacket->Send(player, true); //myPacket->Send(player, true);
Script::Call<Script::CallbackIdentity("OnPlayerLevelChange")>(player->getId()); Script::Call<Script::CallbackIdentity("OnPlayerLevelChange")>(player->getId());
} }
@ -228,7 +273,9 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
DEBUG_PRINTF("ID_PLAYER_EQUIPMENT\n"); DEBUG_PRINTF("ID_PLAYER_EQUIPMENT\n");
myPacket->Read(player); myPacket->Read(player);
myPacket->Send(player, true); //myPacket->Send(player, true);
player->sendToLoaded(myPacket);
Script::Call<Script::CallbackIdentity("OnPlayerEquipmentChange")>(player->getId()); Script::Call<Script::CallbackIdentity("OnPlayerEquipmentChange")>(player->getId());
@ -291,7 +338,8 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
} }
} }
myPacket->Send(player, true); //myPacket->Send(player, true);
player->sendToLoaded(myPacket);
playerController->GetPacket(ID_PLAYER_DYNAMICSTATS)->RequestData(player->attack.target); playerController->GetPacket(ID_PLAYER_DYNAMICSTATS)->RequestData(player->attack.target);
} }
break; break;
@ -301,7 +349,10 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
{ {
DEBUG_PRINTF("ID_PLAYER_DYNAMICSTATS\n"); DEBUG_PRINTF("ID_PLAYER_DYNAMICSTATS\n");
myPacket->Read(player); myPacket->Read(player);
myPacket->Send(player, true); //myPacket->Send(player, true);
player->sendToLoaded(myPacket);
break; break;
} }
@ -352,7 +403,10 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
{ {
DEBUG_PRINTF("ID_PLAYER_DRAWSTATE\n"); DEBUG_PRINTF("ID_PLAYER_DRAWSTATE\n");
myPacket->Read(player); myPacket->Read(player);
myPacket->Send(player, true); //myPacket->Send(player, true);
player->sendToLoaded(myPacket);
break; break;
} }
@ -558,7 +612,16 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
player->npc.mName.c_str()); player->npc.mName.c_str());
myPacket->Read(baseEvent); myPacket->Read(baseEvent);
myPacket->Send(baseEvent, true);
LOG_APPEND(Log::LOG_WARN, "- action: %i", baseEvent->action);
// Until we have a timestamp-based system, send packets pertaining to more
// than one container (i.e. replies to server requests for container contents)
// only to players who have the container's cell loaded
if (baseEvent->action == BaseEvent::SET && baseEvent->objectChanges.count > 1)
CellController::get()->getCell(&baseEvent->cell)->sendToLoaded(myPacket, baseEvent);
else
myPacket->Send(baseEvent, true);
Script::Call<Script::CallbackIdentity("OnContainer")>( Script::Call<Script::CallbackIdentity("OnContainer")>(
player->getId(), player->getId(),
@ -817,7 +880,7 @@ int Networking::mainLoop()
RakNet::BitStream bs; RakNet::BitStream bs;
bs.Write((unsigned char) ID_MASTER_QUERY); bs.Write((unsigned char) ID_MASTER_QUERY);
bs.Write(Players::getPlayers()->size()); bs.Write(Players::getPlayers()->size());
for(auto player : *Players::getPlayers()) for (auto player : *Players::getPlayers())
bs.Write(RakNet::RakString(player.second->npc.mName.c_str())); bs.Write(RakNet::RakString(player.second->npc.mName.c_str()));
bs.Write(0); // plugins bs.Write(0); // plugins
peer->Send(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false); peer->Send(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false);

@ -40,11 +40,14 @@ namespace mwmp
MasterClient *getMasterClient(); MasterClient *getMasterClient();
void InitQuery(std::string queryAddr, unsigned short queryPort, std::string serverAddr, unsigned short serverPort); void InitQuery(std::string queryAddr, unsigned short queryPort, std::string serverAddr, unsigned short serverPort);
void setServerPassword(std::string passw) noexcept;
bool isPassworded() const;
static const Networking &get(); static const Networking &get();
static Networking *getPtr(); static Networking *getPtr();
private: private:
std::string serverPassword;
static Networking *sThis; static Networking *sThis;
RakNet::RakPeerInterface *peer; RakNet::RakPeerInterface *peer;
RakNet::BitStream bsOut; RakNet::BitStream bsOut;

@ -15,6 +15,8 @@ void Players::deletePlayer(RakNet::RakNetGUID guid)
if (players[guid] != 0) if (players[guid] != 0)
{ {
CellController::get()->deletePlayer(players[guid]);
LOG_APPEND(Log::LOG_INFO, "- Emptying slot %i", LOG_APPEND(Log::LOG_INFO, "- Emptying slot %i",
players[guid]->getId()); players[guid]->getId());
@ -52,7 +54,7 @@ void Players::newPlayer(RakNet::RakNetGUID guid)
Player *Players::getPlayer(RakNet::RakNetGUID guid) Player *Players::getPlayer(RakNet::RakNetGUID guid)
{ {
if(players.count(guid) == 0) if (players.count(guid) == 0)
return nullptr; return nullptr;
return players[guid]; return players[guid];
} }
@ -135,3 +137,44 @@ std::chrono::steady_clock::time_point Player::getLastAttackerTime()
{ {
return lastAttackerTime; return lastAttackerTime;
} }
CellController::TContainer *Player::getCells()
{
return &cells;
}
void Player::sendToLoaded(mwmp::PlayerPacket *myPacket)
{
std::list <Player*> plList;
for (auto cell : cells)
for (auto pl : *cell)
plList.push_back(pl);
plList.sort();
plList.unique();
for (auto pl : plList)
{
if (pl == this) continue;
myPacket->Send(this, pl->guid);
}
}
void Player::forEachLoaded(std::function<void(Player *pl, Player *other)> func)
{
std::list <Player*> plList;
for (auto cell : cells)
for (auto pl : *cell)
plList.push_back(pl);
plList.sort();
plList.unique();
for (auto pl : plList)
{
if (pl == this) continue;
func(this, pl);
}
}

@ -17,6 +17,8 @@
#include <components/openmw-mp/Log.hpp> #include <components/openmw-mp/Log.hpp>
#include <components/openmw-mp/Base/BasePlayer.hpp> #include <components/openmw-mp/Base/BasePlayer.hpp>
#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>
#include "Cell.hpp"
struct Player; struct Player;
typedef std::map<RakNet::RakNetGUID, Player*> TPlayers; typedef std::map<RakNet::RakNetGUID, Player*> TPlayers;
@ -38,6 +40,7 @@ private:
class Player : public mwmp::BasePlayer class Player : public mwmp::BasePlayer
{ {
friend class Cell;
unsigned short id; unsigned short id;
public: public:
@ -67,12 +70,18 @@ public:
virtual ~Player(); virtual ~Player();
CellController::TContainer *getCells();
void sendToLoaded(mwmp::PlayerPacket *myPacket);
void forEachLoaded(std::function<void(Player *pl, Player *other)> func);
public: public:
mwmp::InventoryChanges inventoryChangesBuffer; mwmp::InventoryChanges inventoryChangesBuffer;
mwmp::SpellbookChanges spellbookChangesBuffer; mwmp::SpellbookChanges spellbookChangesBuffer;
mwmp::JournalChanges journalChangesBuffer; mwmp::JournalChanges journalChangesBuffer;
private: private:
CellController::TContainer cells;
bool handshakeState; bool handshakeState;
int loadState; int loadState;
unsigned short lastAttacker; unsigned short lastAttacker;

@ -132,12 +132,6 @@ int WorldFunctions::GetContainerItemCharge(unsigned int objectIndex, unsigned in
.containerChanges.items.at(itemIndex).charge; .containerChanges.items.at(itemIndex).charge;
} }
int WorldFunctions::GetContainerItemGoldValue(unsigned int objectIndex, unsigned int itemIndex) noexcept
{
return mwmp::Networking::getPtr()->getLastEvent()->objectChanges.objects.at(objectIndex)
.containerChanges.items.at(itemIndex).goldValue;
}
int WorldFunctions::GetContainerItemActionCount(unsigned int objectIndex, unsigned int itemIndex) noexcept int WorldFunctions::GetContainerItemActionCount(unsigned int objectIndex, unsigned int itemIndex) noexcept
{ {
return mwmp::Networking::getPtr()->getLastEvent()->objectChanges.objects.at(objectIndex) return mwmp::Networking::getPtr()->getLastEvent()->objectChanges.objects.at(objectIndex)

@ -26,7 +26,6 @@
{"GetContainerItemRefId", WorldFunctions::GetContainerItemRefId},\ {"GetContainerItemRefId", WorldFunctions::GetContainerItemRefId},\
{"GetContainerItemCount", WorldFunctions::GetContainerItemCount},\ {"GetContainerItemCount", WorldFunctions::GetContainerItemCount},\
{"GetContainerItemCharge", WorldFunctions::GetContainerItemCharge},\ {"GetContainerItemCharge", WorldFunctions::GetContainerItemCharge},\
{"GetContainerItemGoldValue", WorldFunctions::GetContainerItemGoldValue},\
{"GetContainerItemActionCount", WorldFunctions::GetContainerItemActionCount},\ {"GetContainerItemActionCount", WorldFunctions::GetContainerItemActionCount},\
\ \
{"SetBaseEventCell", WorldFunctions::SetBaseEventCell},\ {"SetBaseEventCell", WorldFunctions::SetBaseEventCell},\
@ -90,7 +89,6 @@ public:
static const char *GetContainerItemRefId(unsigned int objectIndex, unsigned int itemIndex) noexcept; static const char *GetContainerItemRefId(unsigned int objectIndex, unsigned int itemIndex) noexcept;
static int GetContainerItemCount(unsigned int objectIndex, unsigned int itemIndex) noexcept; static int GetContainerItemCount(unsigned int objectIndex, unsigned int itemIndex) noexcept;
static int GetContainerItemCharge(unsigned int objectIndex, unsigned int itemIndex) noexcept; static int GetContainerItemCharge(unsigned int objectIndex, unsigned int itemIndex) noexcept;
static int GetContainerItemGoldValue(unsigned int objectIndex, unsigned int itemIndex) noexcept;
static int GetContainerItemActionCount(unsigned int objectIndex, unsigned int itemIndex) noexcept; static int GetContainerItemActionCount(unsigned int objectIndex, unsigned int itemIndex) noexcept;
static void SetBaseEventCell(const char* cellDescription) noexcept; static void SetBaseEventCell(const char* cellDescription) noexcept;

@ -139,3 +139,8 @@ void ScriptFunctions::SetHostname(const char *name) noexcept
{ {
mwmp::Networking::getPtr()->getMasterClient()->SetHostname(name); mwmp::Networking::getPtr()->getMasterClient()->SetHostname(name);
} }
void ScriptFunctions::SetServerPassword(const char *passw) noexcept
{
mwmp::Networking::getPtr()->setServerPassword(passw);
}

@ -66,6 +66,7 @@ public:
static int GetAvgPing(unsigned short pid) noexcept; static int GetAvgPing(unsigned short pid) noexcept;
static void SetModname(const char* name) noexcept; static void SetModname(const char* name) noexcept;
static void SetHostname(const char* name) noexcept; static void SetHostname(const char* name) noexcept;
static void SetServerPassword(const char *passw) noexcept;
static constexpr ScriptFunctionData functions[]{ static constexpr ScriptFunctionData functions[]{
{"CreateTimer", ScriptFunctions::CreateTimer}, {"CreateTimer", ScriptFunctions::CreateTimer},
@ -88,6 +89,7 @@ public:
{"GetAvgPing", ScriptFunctions::GetAvgPing}, {"GetAvgPing", ScriptFunctions::GetAvgPing},
{"SetModname", ScriptFunctions::SetModname}, {"SetModname", ScriptFunctions::SetModname},
{"SetHostname", ScriptFunctions::SetHostname}, {"SetHostname", ScriptFunctions::SetHostname},
{"SetServerPassword", ScriptFunctions::SetServerPassword},
POSITIONAPI, POSITIONAPI,
CELLAPI, CELLAPI,

@ -44,7 +44,12 @@ void printVersion(string version, int protocol)
#elif defined(__i386__) || defined(_M_I86) #elif defined(__i386__) || defined(_M_I86)
cout << "32-bit"; cout << "32-bit";
#elif defined(__arm__) #elif defined(__arm__)
cout << "ARMv" << __ARM_ARCH << " " << (__aarch64__ ? "64-bit" : "32-bit"); cout << "ARMv" << __ARM_ARCH << " ";
#ifdef __aarch64__
cout << "64-bit";
#else
cout << "32-bit";
#endif
#else #else
cout << "Unknown architecture"; cout << "Unknown architecture";
#endif #endif
@ -183,6 +188,8 @@ int main(int argc, char *argv[])
string addr = mgr.getString("address", "General"); string addr = mgr.getString("address", "General");
int port = mgr.getInt("port", "General"); int port = mgr.getInt("port", "General");
string passw = mgr.getString("password", "General");
string plugin_home = mgr.getString("home", "Plugins"); string plugin_home = mgr.getString("home", "Plugins");
string moddir = Utils::convertPath(plugin_home + "/data"); string moddir = Utils::convertPath(plugin_home + "/data");
@ -227,6 +234,7 @@ int main(int argc, char *argv[])
peer->SetMaximumIncomingConnections((unsigned short)(players)); peer->SetMaximumIncomingConnections((unsigned short)(players));
Networking networking(peer); Networking networking(peer);
networking.setServerPassword(passw);
if ( mgr.getBool("enabled", "MasterServer")) if ( mgr.getBool("enabled", "MasterServer"))
{ {

@ -209,7 +209,6 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
: mWindow(NULL) : mWindow(NULL)
, mEncoding(ToUTF8::WINDOWS_1252) , mEncoding(ToUTF8::WINDOWS_1252)
, mEncoder(NULL) , mEncoder(NULL)
, mVerboseScripts (false)
, mSkipMenu (false) , mSkipMenu (false)
, mUseSound (true) , mUseSound (true)
, mCompileAll (false) , mCompileAll (false)
@ -305,11 +304,6 @@ void OMW::Engine::addContentFile(const std::string& file)
mContentFiles.push_back(file); mContentFiles.push_back(file);
} }
void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity)
{
mVerboseScripts = scriptsVerbosity;
}
void OMW::Engine::setSkipMenu (bool skipMenu, bool newGame) void OMW::Engine::setSkipMenu (bool skipMenu, bool newGame)
{ {
mSkipMenu = skipMenu; mSkipMenu = skipMenu;
@ -555,8 +549,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mScriptContext = new MWScript::CompilerContext (MWScript::CompilerContext::Type_Full); mScriptContext = new MWScript::CompilerContext (MWScript::CompilerContext::Type_Full);
mScriptContext->setExtensions (&mExtensions); mScriptContext->setExtensions (&mExtensions);
mEnvironment.setScriptManager (new MWScript::ScriptManager (mEnvironment.getWorld()->getStore(), mEnvironment.setScriptManager (new MWScript::ScriptManager (mEnvironment.getWorld()->getStore(), *mScriptContext, mWarningsMode,
mVerboseScripts, *mScriptContext, mWarningsMode,
mScriptBlacklistUse ? mScriptBlacklist : std::vector<std::string>())); mScriptBlacklistUse ? mScriptBlacklist : std::vector<std::string>()));
// Create game mechanics system // Create game mechanics system
@ -565,7 +558,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
// Create dialog system // Create dialog system
mEnvironment.setJournal (new MWDialogue::Journal); mEnvironment.setJournal (new MWDialogue::Journal);
mEnvironment.setDialogueManager (new MWDialogue::DialogueManager (mExtensions, mVerboseScripts, mTranslationDataStorage)); mEnvironment.setDialogueManager (new MWDialogue::DialogueManager (mExtensions, mTranslationDataStorage));
// scripts // scripts
if (mCompileAll) if (mCompileAll)
@ -646,6 +639,8 @@ void OMW::Engine::go()
if(!mwmp::Main::init(mContentFiles)) if(!mwmp::Main::init(mContentFiles))
return; return;
std::cout << "OSG version: " << osgGetVersion() << std::endl;
mViewer = new osgViewer::Viewer; mViewer = new osgViewer::Viewer;
mViewer->setReleaseContextAtEndOfFrameHint(false); mViewer->setReleaseContextAtEndOfFrameHint(false);

@ -84,7 +84,6 @@ namespace OMW
osg::ref_ptr<osgViewer::ScreenCaptureHandler> mScreenCaptureHandler; osg::ref_ptr<osgViewer::ScreenCaptureHandler> mScreenCaptureHandler;
std::string mCellName; std::string mCellName;
std::vector<std::string> mContentFiles; std::vector<std::string> mContentFiles;
bool mVerboseScripts;
bool mSkipMenu; bool mSkipMenu;
bool mUseSound; bool mUseSound;
bool mCompileAll; bool mCompileAll;
@ -158,9 +157,6 @@ namespace OMW
*/ */
void addContentFile(const std::string& file); void addContentFile(const std::string& file);
/// Enable or disable verbose script output
void setScriptsVerbosity(bool scriptsVerbosity);
/// Disable or enable all sounds /// Disable or enable all sounds
void setSoundUsage(bool soundUsage); void setSoundUsage(bool soundUsage);

@ -100,9 +100,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
("no-sound", bpo::value<bool>()->implicit_value(true) ("no-sound", bpo::value<bool>()->implicit_value(true)
->default_value(false), "disable all sounds") ->default_value(false), "disable all sounds")
("script-verbose", bpo::value<bool>()->implicit_value(true)
->default_value(false), "verbose script output")
("script-all", bpo::value<bool>()->implicit_value(true) ("script-all", bpo::value<bool>()->implicit_value(true)
->default_value(false), "compile all scripts (excluding dialogue scripts) at startup") ->default_value(false), "compile all scripts (excluding dialogue scripts) at startup")
@ -242,7 +239,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
// scripts // scripts
engine.setCompileAll(variables["script-all"].as<bool>()); engine.setCompileAll(variables["script-all"].as<bool>());
engine.setCompileAllDialogue(variables["script-all-dialogue"].as<bool>()); engine.setCompileAllDialogue(variables["script-all-dialogue"].as<bool>());
engine.setScriptsVerbosity(variables["script-verbose"].as<bool>());
engine.setScriptConsoleMode (variables["script-console"].as<bool>()); engine.setScriptConsoleMode (variables["script-console"].as<bool>());
engine.setStartupScript (variables["script-run"].as<Files::EscapeHashString>().toStdString()); engine.setStartupScript (variables["script-run"].as<Files::EscapeHashString>().toStdString());
engine.setWarningsMode (variables["script-warn"].as<int>()); engine.setWarningsMode (variables["script-warn"].as<int>());

@ -38,7 +38,6 @@ namespace MWClass
{ {
if(!model.empty()) if(!model.empty())
physics.addObject(ptr, model); physics.addObject(ptr, model);
MWBase::Environment::get().getMechanicsManager()->add(ptr);
} }
std::string Activator::getModel(const MWWorld::ConstPtr &ptr) const std::string Activator::getModel(const MWWorld::ConstPtr &ptr) const
@ -52,6 +51,11 @@ namespace MWClass
return ""; return "";
} }
bool Activator::useAnim() const
{
return true;
}
std::string Activator::getName (const MWWorld::ConstPtr& ptr) const std::string Activator::getName (const MWWorld::ConstPtr& ptr) const
{ {
const MWWorld::LiveCellRef<ESM::Activator> *ref = ptr.get<ESM::Activator>(); const MWWorld::LiveCellRef<ESM::Activator> *ref = ptr.get<ESM::Activator>();

@ -39,6 +39,9 @@ namespace MWClass
static void registerSelf(); static void registerSelf();
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
virtual bool useAnim() const;
///< Whether or not to use animated variant of model (default false)
}; };
} }

@ -34,7 +34,11 @@ namespace MWClass
if (getCreatureStats(ptr).isDead()) if (getCreatureStats(ptr).isDead())
MWBase::Environment::get().getWorld()->enableActorCollision(ptr, false); MWBase::Environment::get().getWorld()->enableActorCollision(ptr, false);
} }
MWBase::Environment::get().getMechanicsManager()->add(ptr); }
bool Actor::useAnim() const
{
return true;
} }
void Actor::block(const MWWorld::Ptr &ptr) const void Actor::block(const MWWorld::Ptr &ptr) const

@ -26,6 +26,8 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const;
virtual bool useAnim() const;
virtual void block(const MWWorld::Ptr &ptr) const; virtual void block(const MWWorld::Ptr &ptr) const;
virtual osg::Vec3f getRotationVector(const MWWorld::Ptr& ptr) const; virtual osg::Vec3f getRotationVector(const MWWorld::Ptr& ptr) const;

@ -100,7 +100,6 @@ namespace MWClass
{ {
if(!model.empty()) if(!model.empty())
physics.addObject(ptr, model); physics.addObject(ptr, model);
MWBase::Environment::get().getMechanicsManager()->add(ptr);
} }
std::string Container::getModel(const MWWorld::ConstPtr &ptr) const std::string Container::getModel(const MWWorld::ConstPtr &ptr) const
@ -114,6 +113,11 @@ namespace MWClass
return ""; return "";
} }
bool Container::useAnim() const
{
return true;
}
boost::shared_ptr<MWWorld::Action> Container::activate (const MWWorld::Ptr& ptr, boost::shared_ptr<MWWorld::Action> Container::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const const MWWorld::Ptr& actor) const
{ {

@ -70,6 +70,8 @@ namespace MWClass
virtual void restock (const MWWorld::Ptr &ptr) const; virtual void restock (const MWWorld::Ptr &ptr) const;
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
virtual bool useAnim() const;
}; };
} }

@ -74,8 +74,11 @@ namespace MWClass
MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState); MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState);
} }
} }
}
MWBase::Environment::get().getMechanicsManager()->add(ptr); bool Door::useAnim() const
{
return true;
} }
std::string Door::getModel(const MWWorld::ConstPtr &ptr) const std::string Door::getModel(const MWWorld::ConstPtr &ptr) const

@ -20,6 +20,8 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const;
virtual bool useAnim() const;
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string. /// can return an empty string.

@ -51,8 +51,11 @@ namespace MWClass
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, ref->mBase->mSound, 1.0, 1.0, MWBase::Environment::get().getSoundManager()->playSound3D(ptr, ref->mBase->mSound, 1.0, 1.0,
MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_TypeSfx,
MWBase::SoundManager::Play_Loop); MWBase::SoundManager::Play_Loop);
}
MWBase::Environment::get().getMechanicsManager()->add(ptr); bool Light::useAnim() const
{
return true;
} }
std::string Light::getModel(const MWWorld::ConstPtr &ptr) const std::string Light::getModel(const MWWorld::ConstPtr &ptr) const

@ -16,6 +16,8 @@ namespace MWClass
virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const; virtual void insertObject(const MWWorld::Ptr& ptr, const std::string& model, MWPhysics::PhysicsSystem& physics) const;
virtual bool useAnim() const;
virtual std::string getName (const MWWorld::ConstPtr& ptr) const; virtual std::string getName (const MWWorld::ConstPtr& ptr) const;
///< \return name (the one that is to be presented to the user; not the internal one); ///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string. /// can return an empty string.

@ -48,7 +48,7 @@
namespace MWDialogue namespace MWDialogue
{ {
DialogueManager::DialogueManager (const Compiler::Extensions& extensions, bool scriptVerbose, Translation::Storage& translationDataStorage) : DialogueManager::DialogueManager (const Compiler::Extensions& extensions, Translation::Storage& translationDataStorage) :
mTranslationDataStorage(translationDataStorage) mTranslationDataStorage(translationDataStorage)
, mCompilerContext (MWScript::CompilerContext::Type_Dialogue) , mCompilerContext (MWScript::CompilerContext::Type_Dialogue)
, mErrorStream(std::cout.rdbuf()) , mErrorStream(std::cout.rdbuf())
@ -198,6 +198,8 @@ namespace MWDialogue
{ {
mErrorHandler.reset(); mErrorHandler.reset();
mErrorHandler.setContext("[dialogue script]");
std::istringstream input (cmd + "\n"); std::istringstream input (cmd + "\n");
Compiler::Scanner scanner (mErrorHandler, input, mCompilerContext.getExtensions()); Compiler::Scanner scanner (mErrorHandler, input, mCompilerContext.getExtensions());

@ -58,7 +58,7 @@ namespace MWDialogue
public: public:
DialogueManager (const Compiler::Extensions& extensions, bool scriptVerbose, Translation::Storage& translationDataStorage); DialogueManager (const Compiler::Extensions& extensions, Translation::Storage& translationDataStorage);
virtual void clear(); virtual void clear();

@ -116,7 +116,6 @@ namespace MWGui
containerItem.refId =itemPtr.getCellRef().getRefId(); containerItem.refId =itemPtr.getCellRef().getRefId();
containerItem.count = itemPtr.getRefData().getCount(); containerItem.count = itemPtr.getRefData().getCount();
containerItem.charge = itemPtr.getCellRef().getCharge(); containerItem.charge = itemPtr.getCellRef().getCharge();
containerItem.goldValue = itemPtr.getCellRef().getGoldValue();
containerItem.actionCount = count; containerItem.actionCount = count;
worldObject.containerChanges.items.push_back(containerItem); worldObject.containerChanges.items.push_back(containerItem);
@ -178,7 +177,6 @@ namespace MWGui
containerItem.count = mDragAndDrop->mDraggedCount; containerItem.count = mDragAndDrop->mDraggedCount;
containerItem.charge = itemPtr.getCellRef().getCharge(); containerItem.charge = itemPtr.getCellRef().getCharge();
containerItem.goldValue = itemPtr.getCellRef().getGoldValue();
worldObject.containerChanges.items.push_back(containerItem); worldObject.containerChanges.items.push_back(containerItem);
event->addObject(worldObject); event->addObject(worldObject);

@ -1189,4 +1189,40 @@ namespace MWMechanics
return true; return true;
} }
std::string getSummonedCreature(int effectId)
{
static std::map<int, std::string> summonMap;
if (summonMap.empty())
{
summonMap[ESM::MagicEffect::SummonAncestralGhost] = "sMagicAncestralGhostID";
summonMap[ESM::MagicEffect::SummonBonelord] = "sMagicBonelordID";
summonMap[ESM::MagicEffect::SummonBonewalker] = "sMagicLeastBonewalkerID";
summonMap[ESM::MagicEffect::SummonCenturionSphere] = "sMagicCenturionSphereID";
summonMap[ESM::MagicEffect::SummonClannfear] = "sMagicClannfearID";
summonMap[ESM::MagicEffect::SummonDaedroth] = "sMagicDaedrothID";
summonMap[ESM::MagicEffect::SummonDremora] = "sMagicDremoraID";
summonMap[ESM::MagicEffect::SummonFabricant] = "sMagicFabricantID";
summonMap[ESM::MagicEffect::SummonFlameAtronach] = "sMagicFlameAtronachID";
summonMap[ESM::MagicEffect::SummonFrostAtronach] = "sMagicFrostAtronachID";
summonMap[ESM::MagicEffect::SummonGoldenSaint] = "sMagicGoldenSaintID";
summonMap[ESM::MagicEffect::SummonGreaterBonewalker] = "sMagicGreaterBonewalkerID";
summonMap[ESM::MagicEffect::SummonHunger] = "sMagicHungerID";
summonMap[ESM::MagicEffect::SummonScamp] = "sMagicScampID";
summonMap[ESM::MagicEffect::SummonSkeletalMinion] = "sMagicSkeletalMinionID";
summonMap[ESM::MagicEffect::SummonStormAtronach] = "sMagicStormAtronachID";
summonMap[ESM::MagicEffect::SummonWingedTwilight] = "sMagicWingedTwilightID";
summonMap[ESM::MagicEffect::SummonWolf] = "sMagicCreature01ID";
summonMap[ESM::MagicEffect::SummonBear] = "sMagicCreature02ID";
summonMap[ESM::MagicEffect::SummonBonewolf] = "sMagicCreature03ID";
summonMap[ESM::MagicEffect::SummonCreature04] = "sMagicCreature04ID";
summonMap[ESM::MagicEffect::SummonCreature05] = "sMagicCreature05ID";
}
std::map<int, std::string>::const_iterator it = summonMap.find(effectId);
if (it == summonMap.end())
return std::string();
else
return MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(it->second)->getString();
}
} }

@ -67,6 +67,8 @@ namespace MWMechanics
/// @return Was the effect a tickable effect with a magnitude? /// @return Was the effect a tickable effect with a magnitude?
bool effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const MWMechanics::EffectKey& effectKey, float magnitude); bool effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const MWMechanics::EffectKey& effectKey, float magnitude);
std::string getSummonedCreature(int effectId);
class CastSpell class CastSpell
{ {
private: private:

@ -40,32 +40,7 @@ namespace MWMechanics
void UpdateSummonedCreatures::process() void UpdateSummonedCreatures::process()
{ {
static std::map<int, std::string> summonMap;
if (summonMap.empty())
{
summonMap[ESM::MagicEffect::SummonAncestralGhost] = "sMagicAncestralGhostID";
summonMap[ESM::MagicEffect::SummonBonelord] = "sMagicBonelordID";
summonMap[ESM::MagicEffect::SummonBonewalker] = "sMagicLeastBonewalkerID";
summonMap[ESM::MagicEffect::SummonCenturionSphere] = "sMagicCenturionSphereID";
summonMap[ESM::MagicEffect::SummonClannfear] = "sMagicClannfearID";
summonMap[ESM::MagicEffect::SummonDaedroth] = "sMagicDaedrothID";
summonMap[ESM::MagicEffect::SummonDremora] = "sMagicDremoraID";
summonMap[ESM::MagicEffect::SummonFabricant] = "sMagicFabricantID";
summonMap[ESM::MagicEffect::SummonFlameAtronach] = "sMagicFlameAtronachID";
summonMap[ESM::MagicEffect::SummonFrostAtronach] = "sMagicFrostAtronachID";
summonMap[ESM::MagicEffect::SummonGoldenSaint] = "sMagicGoldenSaintID";
summonMap[ESM::MagicEffect::SummonGreaterBonewalker] = "sMagicGreaterBonewalkerID";
summonMap[ESM::MagicEffect::SummonHunger] = "sMagicHungerID";
summonMap[ESM::MagicEffect::SummonScamp] = "sMagicScampID";
summonMap[ESM::MagicEffect::SummonSkeletalMinion] = "sMagicSkeletalMinionID";
summonMap[ESM::MagicEffect::SummonStormAtronach] = "sMagicStormAtronachID";
summonMap[ESM::MagicEffect::SummonWingedTwilight] = "sMagicWingedTwilightID";
summonMap[ESM::MagicEffect::SummonWolf] = "sMagicCreature01ID";
summonMap[ESM::MagicEffect::SummonBear] = "sMagicCreature02ID";
summonMap[ESM::MagicEffect::SummonBonewolf] = "sMagicCreature03ID";
summonMap[ESM::MagicEffect::SummonCreature04] = "sMagicCreature04ID";
summonMap[ESM::MagicEffect::SummonCreature05] = "sMagicCreature05ID";
}
MWMechanics::CreatureStats& creatureStats = mActor.getClass().getCreatureStats(mActor); MWMechanics::CreatureStats& creatureStats = mActor.getClass().getCreatureStats(mActor);
@ -89,10 +64,7 @@ namespace MWMechanics
bool found = creatureMap.find(std::make_pair(it->first, it->second)) != creatureMap.end(); bool found = creatureMap.find(std::make_pair(it->first, it->second)) != creatureMap.end();
if (!found) if (!found)
{ {
const std::string& creatureGmst = summonMap[it->first]; std::string creatureID = getSummonedCreature(it->first);
std::string creatureID =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(creatureGmst)->getString();
if (!creatureID.empty()) if (!creatureID.empty())
{ {
int creatureActorId = -1; int creatureActorId = -1;

@ -798,6 +798,13 @@ void LocalPlayer::setCell()
MWWorld::Ptr player = world->getPlayerPtr(); MWWorld::Ptr player = world->getPlayerPtr();
ESM::Position pos; ESM::Position pos;
// To avoid crashes, close any container menus this player may be in
if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Container))
{
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);
MWBase::Environment::get().getWindowManager()->setDragDrop(false);
}
world->getPlayer().setTeleported(true); world->getPlayer().setTeleported(true);
int x = cell.mData.mX; int x = cell.mData.mX;
@ -1057,7 +1064,9 @@ void LocalPlayer::sendJournalEntry(const std::string& quest, int index, const MW
journalItem.quest = quest; journalItem.quest = quest;
journalItem.index = index; journalItem.index = index;
journalItem.actorCell = *actor.getCell()->getCell(); if (actor.getCell() != nullptr)
journalItem.actorCell = *actor.getCell()->getCell();
journalItem.actorCellRef.mRefID = actor.getCellRef().getRefId(); journalItem.actorCellRef.mRefID = actor.getCellRef().getRefId();
journalItem.actorCellRef.mRefNum = actor.getCellRef().getRefNum(); journalItem.actorCellRef.mRefNum = actor.getCellRef().getRefNum();

@ -25,6 +25,7 @@
#include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/spellcasting.hpp"
#include <components/openmw-mp/Log.hpp> #include <components/openmw-mp/Log.hpp>
#include <cstdlib> #include <cstdlib>
#include <components/openmw-mp/Version.hpp>
#include "Networking.hpp" #include "Networking.hpp"
#include "LocalPlayer.hpp" #include "LocalPlayer.hpp"
@ -37,6 +38,7 @@ using namespace std;
Main *Main::pMain = 0; Main *Main::pMain = 0;
std::string Main::addr = ""; std::string Main::addr = "";
std::string Main::passw = TES3MP_DEFAULT_PASSW;
std::string loadSettings (Settings::Manager & settings) std::string loadSettings (Settings::Manager & settings)
{ {
@ -85,13 +87,17 @@ Main::~Main()
void Main::optionsDesc(boost::program_options::options_description *desc) void Main::optionsDesc(boost::program_options::options_description *desc)
{ {
namespace bpo = boost::program_options; namespace bpo = boost::program_options;
desc->add_options()("connect", bpo::value<std::string>()->default_value(""), desc->add_options()
"connect to server (e.g. --connect=127.0.0.1:25565)"); ("connect", bpo::value<std::string>()->default_value(""),
"connect to server (e.g. --connect=127.0.0.1:25565)")
("password", bpo::value<std::string>()->default_value(TES3MP_DEFAULT_PASSW),
"сonnect to a secured server. (e.g. --password=AnyPassword");
} }
void Main::configure(const boost::program_options::variables_map &variables) void Main::configure(const boost::program_options::variables_map &variables)
{ {
Main::addr = variables["connect"].as<string>(); Main::addr = variables["connect"].as<string>();
Main::passw = variables["password"].as<string>();
} }
static Settings::CategorySettingValueMap saveUserSettings; static Settings::CategorySettingValueMap saveUserSettings;
@ -130,14 +136,18 @@ bool Main::init(std::vector<std::string> &content)
{ {
pMain->server = mgr.getString("server", "General"); pMain->server = mgr.getString("server", "General");
pMain->port = (unsigned short) mgr.getInt("port", "General"); pMain->port = (unsigned short) mgr.getInt("port", "General");
passw = mgr.getString("password", "General");
if (passw.empty())
passw = TES3MP_DEFAULT_PASSW;
} }
else else
{ {
size_t delim_pos = addr.find(':'); size_t delim_pos = addr.find(':');
pMain->server = addr.substr(0, delim_pos); pMain->server = addr.substr(0, delim_pos);
pMain->port = atoi(addr.substr(delim_pos + 1).c_str()); pMain->port = atoi(addr.substr(delim_pos + 1).c_str());
} }
get().mLocalPlayer->passw = passw;
pMain->mNetworking->connect(pMain->server, pMain->port); pMain->mNetworking->connect(pMain->server, pMain->port);
RestoreMgr(mgr); RestoreMgr(mgr);

@ -34,6 +34,7 @@ namespace mwmp
private: private:
static std::string addr; static std::string addr;
static std::string passw;
Main (const Main&); Main (const Main&);
///< not implemented ///< not implemented
Main& operator= (const Main&); Main& operator= (const Main&);

@ -189,7 +189,6 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
{ {
case ID_HANDSHAKE: case ID_HANDSHAKE:
{ {
getLocalPlayer()->passw = "SuperPassword";
myPacket->Send(getLocalPlayer(), serverAddr); myPacket->Send(getLocalPlayer(), serverAddr);
break; break;
} }
@ -746,7 +745,9 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
if (!ptrCellStore) return; if (!ptrCellStore) return;
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Received ID_CONTAINER"); LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Received ID_CONTAINER about %s",
event->cell.getDescription().c_str());
LOG_APPEND(Log::LOG_VERBOSE, "- action: %i", event->action);
// If we've received a request for information, comply with it // If we've received a request for information, comply with it
if (event->action == mwmp::BaseEvent::REQUEST) if (event->action == mwmp::BaseEvent::REQUEST)
@ -763,7 +764,8 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
if (!ptrCellStore) return; if (!ptrCellStore) return;
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Received ID_OBJECT_PLACE"); LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Received ID_OBJECT_PLACE about %s",
event->cell.getDescription().c_str());
event->placeObjects(ptrCellStore); event->placeObjects(ptrCellStore);
break; break;
@ -774,7 +776,8 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
if (!ptrCellStore) return; if (!ptrCellStore) return;
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Received ID_OBJECT_DELETE"); LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Received ID_OBJECT_DELETE about %s",
event->cell.getDescription().c_str());
event->deleteObjects(ptrCellStore); event->deleteObjects(ptrCellStore);
break; break;
@ -785,7 +788,8 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
if (!ptrCellStore) return; if (!ptrCellStore) return;
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Received ID_OBJECT_LOCK"); LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Received ID_OBJECT_LOCK about %s",
event->cell.getDescription().c_str());
event->lockObjects(ptrCellStore); event->lockObjects(ptrCellStore);
break; break;
@ -796,7 +800,8 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
if (!ptrCellStore) return; if (!ptrCellStore) return;
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Received ID_OBJECT_UNLOCK"); LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Received ID_OBJECT_UNLOCK about %s",
event->cell.getDescription().c_str());
event->unlockObjects(ptrCellStore); event->unlockObjects(ptrCellStore);
break; break;
@ -807,7 +812,8 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
if (!ptrCellStore) return; if (!ptrCellStore) return;
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "%s", "Received ID_OBJECT_SCALE"); LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Received ID_OBJECT_SCALE about %s",
event->cell.getDescription().c_str());
event->scaleObjects(ptrCellStore); event->scaleObjects(ptrCellStore);
break; break;
@ -818,7 +824,8 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
if (!ptrCellStore) return; if (!ptrCellStore) return;
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Received ID_OBJECT_MOVE"); LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Received ID_OBJECT_MOVE about %s",
event->cell.getDescription().c_str());
event->moveObjects(ptrCellStore); event->moveObjects(ptrCellStore);
break; break;
@ -829,7 +836,8 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
if (!ptrCellStore) return; if (!ptrCellStore) return;
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Received ID_OBJECT_ROTATE"); LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Received ID_OBJECT_ROTATE about %s",
event->cell.getDescription().c_str());
event->rotateObjects(ptrCellStore); event->rotateObjects(ptrCellStore);
break; break;
@ -840,7 +848,8 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
if (!ptrCellStore) return; if (!ptrCellStore) return;
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Received ID_OBJECT_ANIM_PLAY"); LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Received ID_OBJECT_ANIM_PLAY about %s",
event->cell.getDescription().c_str());
event->animateObjects(ptrCellStore); event->animateObjects(ptrCellStore);
break; break;
@ -851,7 +860,8 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
if (!ptrCellStore) return; if (!ptrCellStore) return;
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Received ID_DOOR_STATE"); LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Received ID_DOOR_STATE about %s",
event->cell.getDescription().c_str());
event->activateDoors(ptrCellStore); event->activateDoors(ptrCellStore);
break; break;
@ -862,7 +872,8 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
if (!ptrCellStore) return; if (!ptrCellStore) return;
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Received ID_SCRIPT_LOCAL_SHORT"); LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Received ID_SCRIPT_LOCAL_SHORT about %s",
event->cell.getDescription().c_str());
event->setLocalShorts(ptrCellStore); event->setLocalShorts(ptrCellStore);
break; break;
@ -873,7 +884,8 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
if (!ptrCellStore) return; if (!ptrCellStore) return;
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Received ID_SCRIPT_LOCAL_FLOAT"); LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Received ID_SCRIPT_LOCAL_FLOAT about %s",
event->cell.getDescription().c_str());
event->setLocalFloats(ptrCellStore); event->setLocalFloats(ptrCellStore);
break; break;

@ -70,15 +70,20 @@ void mwmp::WorldController::closeContainer(const MWWorld::Ptr &container)
{ {
mwmp::Main::get().getLocalPlayer()->clearCurrentContainer(); mwmp::Main::get().getLocalPlayer()->clearCurrentContainer();
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Container \"%s\" (%d) is closed.", // If the player died while in a container, the container's Ptr could be invalid now
container.getCellRef().getRefId().c_str(), if (!container.isEmpty())
container.getCellRef().getRefNum().mIndex);
MWWorld::ContainerStore &cont = container.getClass().getContainerStore(container);
for (MWWorld::ContainerStoreIterator iter = cont.begin(); iter != cont.end(); iter++)
{ {
LOG_APPEND(Log::LOG_VERBOSE, " - Item. Refid: \"%s\" Count: %d", LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Container \"%s\" (%d) is closed.",
iter->getCellRef().getRefId().c_str(), iter->getRefData().getCount()); container.getCellRef().getRefId().c_str(),
container.getCellRef().getRefNum().mIndex);
MWWorld::ContainerStore &cont = container.getClass().getContainerStore(container);
for (MWWorld::ContainerStoreIterator iter = cont.begin(); iter != cont.end(); iter++)
{
LOG_APPEND(Log::LOG_VERBOSE, " - Item. Refid: \"%s\" Count: %d",
iter->getCellRef().getRefId().c_str(), iter->getRefData().getCount());
}
} }
mwmp::Main::get().getLocalPlayer()->updateInventory(); mwmp::Main::get().getLocalPlayer()->updateInventory();
} }

@ -67,7 +67,6 @@ void WorldEvent::sendContainers(MWWorld::CellStore* cellStore)
containerItem.refId = itemPtr.getCellRef().getRefId(); containerItem.refId = itemPtr.getCellRef().getRefId();
containerItem.count = itemPtr.getRefData().getCount(); containerItem.count = itemPtr.getRefData().getCount();
containerItem.charge = itemPtr.getCellRef().getCharge(); containerItem.charge = itemPtr.getCellRef().getCharge();
containerItem.goldValue = itemPtr.getCellRef().getGoldValue();
worldObject.containerChanges.items.push_back(containerItem); worldObject.containerChanges.items.push_back(containerItem);
} }
@ -86,16 +85,15 @@ void WorldEvent::editContainers(MWWorld::CellStore* cellStore)
{ {
worldObject = objectChanges.objects.at(i); worldObject = objectChanges.objects.at(i);
LOG_APPEND(Log::LOG_WARN, "- cellRef: %s, %i\n- cell: %s", LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i",
worldObject.refId.c_str(), worldObject.refId.c_str(),
worldObject.refNumIndex, worldObject.refNumIndex);
cell.getDescription().c_str());
MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refId, worldObject.refNumIndex); MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refId, worldObject.refNumIndex);
if (ptrFound) if (ptrFound)
{ {
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Found %s, %i", LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Found %s, %i",
ptrFound.getCellRef().getRefId().c_str(), ptrFound.getCellRef().getRefId().c_str(),
ptrFound.getCellRef().getRefNum()); ptrFound.getCellRef().getRefNum());
@ -122,8 +120,6 @@ void WorldEvent::editContainers(MWWorld::CellStore* cellStore)
if (containerItem.charge > -1) if (containerItem.charge > -1)
newPtr.getCellRef().setCharge(containerItem.charge); newPtr.getCellRef().setCharge(containerItem.charge);
newPtr.getCellRef().setGoldValue(containerItem.goldValue);
containerStore.add(newPtr, containerItem.count, ownerPtr, true); containerStore.add(newPtr, containerItem.count, ownerPtr, true);
} }
else if (action == BaseEvent::REMOVE) else if (action == BaseEvent::REMOVE)
@ -135,7 +131,6 @@ void WorldEvent::editContainers(MWWorld::CellStore* cellStore)
if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), containerItem.refId)) if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), containerItem.refId))
{ {
if (iter->getCellRef().getCharge() == containerItem.charge && if (iter->getCellRef().getCharge() == containerItem.charge &&
iter->getCellRef().getGoldValue() == containerItem.goldValue &&
iter->getRefData().getCount() == containerItem.count) iter->getRefData().getCount() == containerItem.count)
{ {
containerStore.remove(*iter, containerItem.actionCount, ownerPtr); containerStore.remove(*iter, containerItem.actionCount, ownerPtr);
@ -169,10 +164,9 @@ void WorldEvent::placeObjects(MWWorld::CellStore* cellStore)
{ {
worldObject = objectChanges.objects.at(i); worldObject = objectChanges.objects.at(i);
LOG_APPEND(Log::LOG_WARN, "- cellRef: %s, %i\n- cell: %s\n- charge: %i\n- count: %i", LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i\n- charge: %i\n- count: %i",
worldObject.refId.c_str(), worldObject.refId.c_str(),
worldObject.refNumIndex, worldObject.refNumIndex,
cell.getDescription().c_str(),
worldObject.charge, worldObject.charge,
worldObject.count); worldObject.count);
@ -206,16 +200,15 @@ void WorldEvent::deleteObjects(MWWorld::CellStore* cellStore)
{ {
worldObject = objectChanges.objects.at(i); worldObject = objectChanges.objects.at(i);
LOG_APPEND(Log::LOG_WARN, "- cellRef: %s, %i\n- cell: %s", LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i",
worldObject.refId.c_str(), worldObject.refId.c_str(),
worldObject.refNumIndex, worldObject.refNumIndex);
cell.getDescription().c_str());
MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refId, worldObject.refNumIndex); MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refId, worldObject.refNumIndex);
if (ptrFound) if (ptrFound)
{ {
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Found %s, %i", LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Found %s, %i",
ptrFound.getCellRef().getRefId().c_str(), ptrFound.getCellRef().getRefId().c_str(),
ptrFound.getCellRef().getRefNum()); ptrFound.getCellRef().getRefNum());
@ -232,16 +225,15 @@ void WorldEvent::lockObjects(MWWorld::CellStore* cellStore)
{ {
worldObject = objectChanges.objects.at(i); worldObject = objectChanges.objects.at(i);
LOG_APPEND(Log::LOG_WARN, "- cellRef: %s, %i\n- cell: %s", LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i",
worldObject.refId.c_str(), worldObject.refId.c_str(),
worldObject.refNumIndex, worldObject.refNumIndex);
cell.getDescription().c_str());
MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refId, worldObject.refNumIndex); MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refId, worldObject.refNumIndex);
if (ptrFound) if (ptrFound)
{ {
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Found %s, %i", LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Found %s, %i",
ptrFound.getCellRef().getRefId().c_str(), ptrFound.getCellRef().getRefId().c_str(),
ptrFound.getCellRef().getRefNum()); ptrFound.getCellRef().getRefNum());
@ -258,16 +250,15 @@ void WorldEvent::unlockObjects(MWWorld::CellStore* cellStore)
{ {
worldObject = objectChanges.objects.at(i); worldObject = objectChanges.objects.at(i);
LOG_APPEND(Log::LOG_WARN, "- cellRef: %s, %i\n- cell: %s", LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i",
worldObject.refId.c_str(), worldObject.refId.c_str(),
worldObject.refNumIndex, worldObject.refNumIndex);
cell.getDescription().c_str());
MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refId, worldObject.refNumIndex); MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refId, worldObject.refNumIndex);
if (ptrFound) if (ptrFound)
{ {
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Found %s, %i", LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Found %s, %i",
ptrFound.getCellRef().getRefId().c_str(), ptrFound.getCellRef().getRefId().c_str(),
ptrFound.getCellRef().getRefNum()); ptrFound.getCellRef().getRefNum());
@ -284,16 +275,15 @@ void WorldEvent::scaleObjects(MWWorld::CellStore* cellStore)
{ {
worldObject = objectChanges.objects.at(i); worldObject = objectChanges.objects.at(i);
LOG_APPEND(Log::LOG_WARN, "- cellRef: %s, %i\n- cell: %s", LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i",
worldObject.refId.c_str(), worldObject.refId.c_str(),
worldObject.refNumIndex, worldObject.refNumIndex);
cell.getDescription().c_str());
MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refId, worldObject.refNumIndex); MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refId, worldObject.refNumIndex);
if (ptrFound) if (ptrFound)
{ {
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Found %s, %i", LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Found %s, %i",
ptrFound.getCellRef().getRefId().c_str(), ptrFound.getCellRef().getRefId().c_str(),
ptrFound.getCellRef().getRefNum()); ptrFound.getCellRef().getRefNum());
@ -310,16 +300,15 @@ void WorldEvent::moveObjects(MWWorld::CellStore* cellStore)
{ {
worldObject = objectChanges.objects.at(i); worldObject = objectChanges.objects.at(i);
LOG_APPEND(Log::LOG_WARN, "- cellRef: %s, %i\n- cell: %s", LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i",
worldObject.refId.c_str(), worldObject.refId.c_str(),
worldObject.refNumIndex, worldObject.refNumIndex);
cell.getDescription().c_str());
MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refId, worldObject.refNumIndex); MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refId, worldObject.refNumIndex);
if (ptrFound) if (ptrFound)
{ {
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Found %s, %i", LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Found %s, %i",
ptrFound.getCellRef().getRefId().c_str(), ptrFound.getCellRef().getRefId().c_str(),
ptrFound.getCellRef().getRefNum()); ptrFound.getCellRef().getRefNum());
@ -337,16 +326,15 @@ void WorldEvent::rotateObjects(MWWorld::CellStore* cellStore)
{ {
worldObject = objectChanges.objects.at(i); worldObject = objectChanges.objects.at(i);
LOG_APPEND(Log::LOG_WARN, "- cellRef: %s, %i\n- cell: %s", LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i",
worldObject.refId.c_str(), worldObject.refId.c_str(),
worldObject.refNumIndex, worldObject.refNumIndex);
cell.getDescription().c_str());
MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refId, worldObject.refNumIndex); MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refId, worldObject.refNumIndex);
if (ptrFound) if (ptrFound)
{ {
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Found %s, %i", LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Found %s, %i",
ptrFound.getCellRef().getRefId().c_str(), ptrFound.getCellRef().getRefId().c_str(),
ptrFound.getCellRef().getRefNum()); ptrFound.getCellRef().getRefNum());
@ -364,16 +352,15 @@ void WorldEvent::animateObjects(MWWorld::CellStore* cellStore)
{ {
worldObject = objectChanges.objects.at(i); worldObject = objectChanges.objects.at(i);
LOG_APPEND(Log::LOG_WARN, "- cellRef: %s, %i\n- cell: %s", LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i",
worldObject.refId.c_str(), worldObject.refId.c_str(),
worldObject.refNumIndex, worldObject.refNumIndex);
cell.getDescription().c_str());
MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refId, worldObject.refNumIndex); MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refId, worldObject.refNumIndex);
if (ptrFound) if (ptrFound)
{ {
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Found %s, %i", LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Found %s, %i",
ptrFound.getCellRef().getRefId().c_str(), ptrFound.getCellRef().getRefId().c_str(),
ptrFound.getCellRef().getRefNum()); ptrFound.getCellRef().getRefNum());
@ -391,16 +378,15 @@ void WorldEvent::activateDoors(MWWorld::CellStore* cellStore)
{ {
worldObject = objectChanges.objects.at(i); worldObject = objectChanges.objects.at(i);
LOG_APPEND(Log::LOG_WARN, "- cellRef: %s, %i\n- cell: %s", LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i",
worldObject.refId.c_str(), worldObject.refId.c_str(),
worldObject.refNumIndex, worldObject.refNumIndex);
cell.getDescription().c_str());
MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refId, worldObject.refNumIndex); MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refId, worldObject.refNumIndex);
if (ptrFound) if (ptrFound)
{ {
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Found %s, %i", LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Found %s, %i",
ptrFound.getCellRef().getRefId().c_str(), ptrFound.getCellRef().getRefId().c_str(),
ptrFound.getCellRef().getRefNum()); ptrFound.getCellRef().getRefNum());
@ -418,7 +404,7 @@ void WorldEvent::playMusic()
{ {
worldObject = objectChanges.objects.at(i); worldObject = objectChanges.objects.at(i);
LOG_APPEND(Log::LOG_WARN, "- filename: %s", LOG_APPEND(Log::LOG_VERBOSE, "- filename: %s",
worldObject.filename.c_str()); worldObject.filename.c_str());
MWBase::Environment::get().getSoundManager()->streamMusic(worldObject.filename); MWBase::Environment::get().getSoundManager()->streamMusic(worldObject.filename);
@ -433,7 +419,7 @@ void WorldEvent::playVideo()
{ {
worldObject = objectChanges.objects.at(i); worldObject = objectChanges.objects.at(i);
LOG_APPEND(Log::LOG_WARN, "- filename: %s\n- allowSkipping: %s", LOG_APPEND(Log::LOG_VERBOSE, "- filename: %s\n- allowSkipping: %s",
worldObject.filename.c_str(), worldObject.filename.c_str(),
worldObject.allowSkipping ? "true" : "false"); worldObject.allowSkipping ? "true" : "false");
@ -449,10 +435,9 @@ void WorldEvent::setLocalShorts(MWWorld::CellStore* cellStore)
{ {
worldObject = objectChanges.objects.at(i); worldObject = objectChanges.objects.at(i);
LOG_APPEND(Log::LOG_WARN, "- cellRef: %s, %i\n- cell: %s\n- index: %i\n- shortVal: %i", LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i\n- index: %i\n- shortVal: %i",
worldObject.refId.c_str(), worldObject.refId.c_str(),
worldObject.refNumIndex, worldObject.refNumIndex,
cell.getDescription().c_str(),
worldObject.index, worldObject.index,
worldObject.shortVal); worldObject.shortVal);
@ -460,7 +445,7 @@ void WorldEvent::setLocalShorts(MWWorld::CellStore* cellStore)
if (ptrFound) if (ptrFound)
{ {
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Found %s, %i", LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Found %s, %i",
ptrFound.getCellRef().getRefId().c_str(), ptrFound.getCellRef().getRefId().c_str(),
ptrFound.getCellRef().getRefNum()); ptrFound.getCellRef().getRefNum());
@ -477,10 +462,9 @@ void WorldEvent::setLocalFloats(MWWorld::CellStore* cellStore)
{ {
worldObject = objectChanges.objects.at(i); worldObject = objectChanges.objects.at(i);
LOG_APPEND(Log::LOG_WARN, "- cellRef: %s, %i\n- cell: %s\n- index: %i\n- floatVal: %f", LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i\n- index: %i\n- floatVal: %f",
worldObject.refId.c_str(), worldObject.refId.c_str(),
worldObject.refNumIndex, worldObject.refNumIndex,
cell.getDescription().c_str(),
worldObject.index, worldObject.index,
worldObject.floatVal); worldObject.floatVal);
@ -488,7 +472,7 @@ void WorldEvent::setLocalFloats(MWWorld::CellStore* cellStore)
if (ptrFound) if (ptrFound)
{ {
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Found %s, %i", LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Found %s, %i",
ptrFound.getCellRef().getRefId().c_str(), ptrFound.getCellRef().getRefId().c_str(),
ptrFound.getCellRef().getRefNum()); ptrFound.getCellRef().getRefNum());
@ -505,7 +489,7 @@ void WorldEvent::setMemberShorts()
{ {
worldObject = objectChanges.objects.at(i); worldObject = objectChanges.objects.at(i);
LOG_APPEND(Log::LOG_WARN, "- cellRef: %s\n- index: %i\n- shortVal: %i\n", LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s\n- index: %i\n- shortVal: %i\n",
worldObject.refId.c_str(), worldObject.refId.c_str(),
worldObject.index, worldObject.index,
worldObject.shortVal); worldObject.shortVal);
@ -515,7 +499,7 @@ void WorldEvent::setMemberShorts()
if (!ptrFound.isEmpty()) if (!ptrFound.isEmpty())
{ {
LOG_MESSAGE_SIMPLE(Log::LOG_WARN, "Found %s, %i", LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Found %s, %i",
ptrFound.getCellRef().getRefId().c_str(), ptrFound.getCellRef().getRefId().c_str(),
ptrFound.getCellRef().getRefNum()); ptrFound.getCellRef().getRefNum());
@ -537,7 +521,7 @@ void WorldEvent::setGlobalShorts()
{ {
worldObject = objectChanges.objects.at(i); worldObject = objectChanges.objects.at(i);
LOG_APPEND(Log::LOG_WARN, "- varName: %s\n- shortVal: %i", LOG_APPEND(Log::LOG_VERBOSE, "- varName: %s\n- shortVal: %i",
worldObject.varName.c_str(), worldObject.varName.c_str(),
worldObject.shortVal); worldObject.shortVal);

@ -531,7 +531,7 @@ namespace MWPhysics
mShape = new btHeightfieldTerrainShape( mShape = new btHeightfieldTerrainShape(
sqrtVerts, sqrtVerts, heights, 1, sqrtVerts, sqrtVerts, heights, 1,
minh, maxh, 2, minh, maxh, 2,
PHY_FLOAT, true PHY_FLOAT, false
); );
mShape->setUseDiamondSubdivision(true); mShape->setUseDiamondSubdivision(true);
mShape->setLocalScaling(btVector3(triSize, triSize, 1)); mShape->setLocalScaling(btVector3(triSize, triSize, 1));

@ -15,6 +15,7 @@
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwworld/cellstore.hpp" #include "../mwworld/cellstore.hpp"
#include "../mwworld/actionteleport.hpp"
#include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/actorutil.hpp"
@ -46,10 +47,9 @@ namespace MWScript
ESM::Position pos; ESM::Position pos;
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
world->getPlayer().setTeleported(true);
if (world->findExteriorPosition(cell, pos)) if (world->findExteriorPosition(cell, pos))
{ {
world->changeToExteriorCell(pos, true); MWWorld::ActionTeleport("", pos, false).execute(world->getPlayerPtr());
world->fixPosition(world->getPlayerPtr()); world->fixPosition(world->getPlayerPtr());
} }
else else
@ -57,7 +57,7 @@ namespace MWScript
// Change to interior even if findInteriorPosition() // Change to interior even if findInteriorPosition()
// yields false. In this case position will be zero-point. // yields false. In this case position will be zero-point.
world->findInteriorPosition(cell, pos); world->findInteriorPosition(cell, pos);
world->changeToInteriorCell(cell, pos, true); MWWorld::ActionTeleport(cell, pos, false).execute(world->getPlayerPtr());
} }
} }
}; };
@ -76,13 +76,13 @@ namespace MWScript
ESM::Position pos; ESM::Position pos;
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
world->getPlayer().setTeleported(true);
world->indexToPosition (x, y, pos.pos[0], pos.pos[1], true); world->indexToPosition (x, y, pos.pos[0], pos.pos[1], true);
pos.pos[2] = 0; pos.pos[2] = 0;
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
world->changeToExteriorCell (pos, true); MWWorld::ActionTeleport("", pos, false).execute(world->getPlayerPtr());
world->fixPosition(world->getPlayerPtr()); world->fixPosition(world->getPlayerPtr());
} }
}; };

@ -21,10 +21,10 @@
namespace MWScript namespace MWScript
{ {
ScriptManager::ScriptManager (const MWWorld::ESMStore& store, bool verbose, ScriptManager::ScriptManager (const MWWorld::ESMStore& store,
Compiler::Context& compilerContext, int warningsMode, Compiler::Context& compilerContext, int warningsMode,
const std::vector<std::string>& scriptBlacklist) const std::vector<std::string>& scriptBlacklist)
: mErrorHandler (std::cerr), mStore (store), mVerbose (verbose), : mErrorHandler (std::cerr), mStore (store),
mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext), mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext),
mOpcodesInstalled (false), mGlobalScripts (store) mOpcodesInstalled (false), mGlobalScripts (store)
{ {
@ -44,8 +44,7 @@ namespace MWScript
if (const ESM::Script *script = mStore.get<ESM::Script>().find (name)) if (const ESM::Script *script = mStore.get<ESM::Script>().find (name))
{ {
if (mVerbose) mErrorHandler.setContext(name);
std::cout << "compiling script: " << name << std::endl;
bool Success = true; bool Success = true;
try try
@ -74,8 +73,6 @@ namespace MWScript
{ {
std::cerr std::cerr
<< "compiling failed: " << name << std::endl; << "compiling failed: " << name << std::endl;
if (mVerbose)
std::cerr << script->mScriptText << std::endl << std::endl;
} }
if (Success) if (Success)
@ -172,13 +169,10 @@ namespace MWScript
if (const ESM::Script *script = mStore.get<ESM::Script>().search (name2)) if (const ESM::Script *script = mStore.get<ESM::Script>().search (name2))
{ {
if (mVerbose)
std::cout
<< "scanning script for local variable declarations: " << name2
<< std::endl;
Compiler::Locals locals; Compiler::Locals locals;
mErrorHandler.setContext(name2 + "[local variables]");
std::istringstream stream (script->mScriptText); std::istringstream stream (script->mScriptText);
Compiler::QuickFileParser parser (mErrorHandler, mCompilerContext, locals); Compiler::QuickFileParser parser (mErrorHandler, mCompilerContext, locals);
Compiler::Scanner scanner (mErrorHandler, stream, mCompilerContext.getExtensions()); Compiler::Scanner scanner (mErrorHandler, stream, mCompilerContext.getExtensions());

@ -36,7 +36,6 @@ namespace MWScript
{ {
Compiler::StreamErrorHandler mErrorHandler; Compiler::StreamErrorHandler mErrorHandler;
const MWWorld::ESMStore& mStore; const MWWorld::ESMStore& mStore;
bool mVerbose;
Compiler::Context& mCompilerContext; Compiler::Context& mCompilerContext;
Compiler::FileParser mParser; Compiler::FileParser mParser;
Interpreter::Interpreter mInterpreter; Interpreter::Interpreter mInterpreter;
@ -52,7 +51,7 @@ namespace MWScript
public: public:
ScriptManager (const MWWorld::ESMStore& store, bool verbose, ScriptManager (const MWWorld::ESMStore& store,
Compiler::Context& compilerContext, int warningsMode, Compiler::Context& compilerContext, int warningsMode,
const std::vector<std::string>& scriptBlacklist); const std::vector<std::string>& scriptBlacklist);

@ -449,7 +449,8 @@ namespace MWScript
// Added by tes3mp // Added by tes3mp
// //
// LocalPlayer has gained a spell, so send a packet with it // LocalPlayer has gained a spell, so send a packet with it
mwmp::Main::get().getLocalPlayer()->sendSpellAddition(id); if (ptr == MWMechanics::getPlayer())
mwmp::Main::get().getLocalPlayer()->sendSpellAddition(id);
} }
}; };
@ -478,7 +479,8 @@ namespace MWScript
// Added by tes3mp // Added by tes3mp
// //
// LocalPlayer has lost a spell, so send a packet with it // LocalPlayer has lost a spell, so send a packet with it
mwmp::Main::get().getLocalPlayer()->sendSpellRemoval(id); if (ptr == MWMechanics::getPlayer())
mwmp::Main::get().getLocalPlayer()->sendSpellRemoval(id);
} }
}; };

@ -4,6 +4,8 @@
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "player.hpp" #include "player.hpp"
@ -34,6 +36,7 @@ namespace MWWorld
void ActionTeleport::teleport(const Ptr &actor) void ActionTeleport::teleport(const Ptr &actor)
{ {
MWBase::World* world = MWBase::Environment::get().getWorld(); MWBase::World* world = MWBase::Environment::get().getWorld();
actor.getClass().getCreatureStats(actor).land();
if(actor == world->getPlayerPtr()) if(actor == world->getPlayerPtr())
{ {
world->getPlayer().setTeleported(true); world->getPlayer().setTeleported(true);

@ -650,7 +650,8 @@ namespace MWWorld
loadRef (ref, deleted, refNumToID); loadRef (ref, deleted, refNumToID);
} }
setLastRefNumIndex(refNumToID.rbegin()->first.mIndex); if(refNumToID.size() != 0)
setLastRefNumIndex(refNumToID.rbegin()->first.mIndex);
updateMergedRefs(); updateMergedRefs();
} }

@ -304,6 +304,11 @@ namespace MWWorld
return ""; return "";
} }
bool Class::useAnim() const
{
return false;
}
void Class::getModelsToPreload(const Ptr &ptr, std::vector<std::string> &models) const void Class::getModelsToPreload(const Ptr &ptr, std::vector<std::string> &models) const
{ {
std::string model = getModel(ptr); std::string model = getModel(ptr);

@ -271,6 +271,9 @@ namespace MWWorld
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
virtual bool useAnim() const;
///< Whether or not to use animated variant of model (default false)
virtual void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const; virtual void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const;
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation: list getModel(). ///< Get a list of models to preload that this object may use (directly or indirectly). default implementation: list getModel().

@ -297,9 +297,15 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor)
{ {
if (old.getTypeName() == typeid(ESM::Armor).name()) if (old.getTypeName() == typeid(ESM::Armor).name())
{ {
if (old.getClass().getEffectiveArmorRating(old, actor) >= test.getClass().getEffectiveArmorRating(test, actor)) if (old.get<ESM::Armor>()->mBase->mData.mType < test.get<ESM::Armor>()->mBase->mData.mType)
// old armor had better armor rating
continue; continue;
if (old.get<ESM::Armor>()->mBase->mData.mType == test.get<ESM::Armor>()->mBase->mData.mType)
{
if (old.getClass().getEffectiveArmorRating(old, actor) >= test.getClass().getEffectiveArmorRating(test, actor))
// old armor had better armor rating
continue;
}
} }
// suitable armor should replace already equipped clothing // suitable armor should replace already equipped clothing
} }

@ -56,15 +56,23 @@ namespace
void addObject(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics, void addObject(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics,
MWRender::RenderingManager& rendering) MWRender::RenderingManager& rendering)
{ {
std::string model = Misc::ResourceHelpers::correctActorModelPath(ptr.getClass().getModel(ptr), rendering.getResourceSystem()->getVFS()); bool useAnim = ptr.getClass().useAnim();
std::string model = ptr.getClass().getModel(ptr);
if (useAnim)
model = Misc::ResourceHelpers::correctActorModelPath(model, rendering.getResourceSystem()->getVFS());
std::string id = ptr.getCellRef().getRefId(); std::string id = ptr.getCellRef().getRefId();
if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker")
model = ""; // marker objects that have a hardcoded function in the game logic, should be hidden from the player model = ""; // marker objects that have a hardcoded function in the game logic, should be hidden from the player
ptr.getClass().insertObjectRendering(ptr, model, rendering); ptr.getClass().insertObjectRendering(ptr, model, rendering);
setNodeRotation(ptr, rendering, false); setNodeRotation(ptr, rendering, false);
ptr.getClass().insertObject (ptr, model, physics); ptr.getClass().insertObject (ptr, model, physics);
if (useAnim)
MWBase::Environment::get().getMechanicsManager()->add(ptr);
if (ptr.getClass().isActor()) if (ptr.getClass().isActor())
rendering.addWaterRippleEmitter(ptr); rendering.addWaterRippleEmitter(ptr);
} }
@ -303,7 +311,11 @@ namespace MWWorld
// ... then references. This is important for adjustPosition to work correctly. // ... then references. This is important for adjustPosition to work correctly.
/// \todo rescale depending on the state of a new GMST /// \todo rescale depending on the state of a new GMST
insertCell (*cell, true, loadingListener);
// Minor change done by tes3mp:
// Instead of always rescaling objects as in the original code, never rescale them
insertCell(*cell, false, loadingListener);
mRendering.addCell(cell); mRendering.addCell(cell);
bool waterEnabled = cell->getCell()->hasWater() || cell->isExterior(); bool waterEnabled = cell->getCell()->hasWater() || cell->isExterior();
@ -703,10 +715,14 @@ namespace MWWorld
Resource::SceneManager* mSceneManager; Resource::SceneManager* mSceneManager;
}; };
void Scene::preload(const std::string &mesh) void Scene::preload(const std::string &mesh, bool useAnim)
{ {
if (!mRendering.getResourceSystem()->getSceneManager()->checkLoaded(mesh, mRendering.getReferenceTime())) std::string mesh_ = mesh;
mRendering.getWorkQueue()->addWorkItem(new PreloadMeshItem(mesh, mRendering.getResourceSystem()->getSceneManager())); if (useAnim)
mesh_ = Misc::ResourceHelpers::correctActorModelPath(mesh_, mRendering.getResourceSystem()->getVFS());
if (!mRendering.getResourceSystem()->getSceneManager()->checkLoaded(mesh_, mRendering.getReferenceTime()))
mRendering.getWorkQueue()->addWorkItem(new PreloadMeshItem(mesh_, mRendering.getResourceSystem()->getSceneManager()));
} }
void Scene::preloadCells(float dt) void Scene::preloadCells(float dt)

@ -133,7 +133,7 @@ namespace MWWorld
Ptr searchPtrViaActorId (int actorId); Ptr searchPtrViaActorId (int actorId);
void preload(const std::string& mesh); void preload(const std::string& mesh, bool useAnim=false);
}; };
} }

@ -3481,10 +3481,16 @@ namespace MWWorld
{ {
if (obj.empty()) if (obj.empty())
return; return;
MWWorld::ManualRef ref(store, obj); try
std::string model = ref.getPtr().getClass().getModel(ref.getPtr()); {
if (!model.empty()) MWWorld::ManualRef ref(store, obj);
scene->preload(model); std::string model = ref.getPtr().getClass().getModel(ref.getPtr());
if (!model.empty())
scene->preload(model, ref.getPtr().getClass().useAnim());
}
catch(std::exception& e)
{
}
} }
void World::preloadEffects(const ESM::EffectList *effectList) void World::preloadEffects(const ESM::EffectList *effectList)
@ -3493,6 +3499,12 @@ namespace MWWorld
{ {
const ESM::MagicEffect *effect = mStore.get<ESM::MagicEffect>().find(it->mEffectID); const ESM::MagicEffect *effect = mStore.get<ESM::MagicEffect>().find(it->mEffectID);
if (MWMechanics::isSummoningEffect(it->mEffectID))
{
preload(mWorldScene, mStore, "VFX_Summon_Start");
preload(mWorldScene, mStore, MWMechanics::getSummonedCreature(it->mEffectID));
}
preload(mWorldScene, mStore, effect->mCasting); preload(mWorldScene, mStore, effect->mCasting);
preload(mWorldScene, mStore, effect->mHit); preload(mWorldScene, mStore, effect->mHit);

@ -14,6 +14,9 @@ namespace Compiler
else else
mStream << "warning "; mStream << "warning ";
if (!mContext.empty())
mStream << mContext << " ";
mStream mStream
<< "line " << loc.mLine+1 << ", column " << loc.mColumn+1 << "line " << loc.mLine+1 << ", column " << loc.mColumn+1
<< " (" << loc.mLiteral << ")" << std::endl << " (" << loc.mLiteral << ")" << std::endl
@ -34,5 +37,10 @@ namespace Compiler
<< " " << message << std::endl; << " " << message << std::endl;
} }
void StreamErrorHandler::setContext(const std::string &context)
{
mContext = context;
}
StreamErrorHandler::StreamErrorHandler (std::ostream& ErrorStream) : mStream (ErrorStream) {} StreamErrorHandler::StreamErrorHandler (std::ostream& ErrorStream) : mStream (ErrorStream) {}
} }

@ -13,6 +13,8 @@ namespace Compiler
{ {
std::ostream& mStream; std::ostream& mStream;
std::string mContext;
// not implemented // not implemented
StreamErrorHandler (const StreamErrorHandler&); StreamErrorHandler (const StreamErrorHandler&);
@ -26,6 +28,8 @@ namespace Compiler
public: public:
void setContext(const std::string& context);
// constructors // constructors
StreamErrorHandler (std::ostream& ErrorStream); StreamErrorHandler (std::ostream& ErrorStream);

@ -12,13 +12,12 @@ namespace mwmp
std::string refId; std::string refId;
int count; int count;
int charge; int charge;
int goldValue;
int actionCount; int actionCount;
inline bool operator==(const ContainerItem& rhs) inline bool operator==(const ContainerItem& rhs)
{ {
return refId == rhs.refId && count == rhs.count && charge == rhs.charge && goldValue && rhs.goldValue; return refId == rhs.refId && count == rhs.count && charge == rhs.charge;
} }
}; };

@ -60,7 +60,6 @@ void PacketContainer::Packet(RakNet::BitStream *bs, BaseEvent *event, bool send)
RW(containerItem.refId, send); RW(containerItem.refId, send);
RW(containerItem.count, send); RW(containerItem.count, send);
RW(containerItem.charge, send); RW(containerItem.charge, send);
RW(containerItem.goldValue, send);
RW(containerItem.actionCount, send); RW(containerItem.actionCount, send);
if (!send) if (!send)

@ -5,7 +5,9 @@
#ifndef OPENMW_VERSION_HPP #ifndef OPENMW_VERSION_HPP
#define OPENMW_VERSION_HPP #define OPENMW_VERSION_HPP
#define TES3MP_VERSION "0.4.2" #define TES3MP_VERSION "0.5.0"
#define TES3MP_PROTO_VERSION 5 #define TES3MP_PROTO_VERSION 6
#define TES3MP_DEFAULT_PASSW "SuperPassword"
#endif //OPENMW_VERSION_HPP #endif //OPENMW_VERSION_HPP

@ -374,7 +374,7 @@ namespace SceneUtil
bool sortLights (const LightManager::LightSourceViewBound* left, const LightManager::LightSourceViewBound* right) bool sortLights (const LightManager::LightSourceViewBound* left, const LightManager::LightSourceViewBound* right)
{ {
return left->mViewBound.center().length2() - left->mViewBound.radius2()/4.f < right->mViewBound.center().length2() - right->mViewBound.radius2()/4.f; return left->mViewBound.center().length2() - left->mViewBound.radius2()*81 < right->mViewBound.center().length2() - right->mViewBound.radius2()*81;
} }
void LightListCallback::operator()(osg::Node *node, osg::NodeVisitor *nv) void LightListCallback::operator()(osg::Node *node, osg::NodeVisitor *nv)

@ -3,6 +3,7 @@
#include <cassert> #include <cassert>
#include <stdexcept> #include <stdexcept>
#include <iostream> #include <iostream>
#include <cstdlib>
#include <SDL_mouse.h> #include <SDL_mouse.h>
#include <SDL_endian.h> #include <SDL_endian.h>
@ -81,6 +82,8 @@ namespace
osg::ref_ptr<osg::Image> decompress (osg::ref_ptr<osg::Image> source, float rotDegrees) osg::ref_ptr<osg::Image> decompress (osg::ref_ptr<osg::Image> source, float rotDegrees)
{ {
// TODO: use software decompression once S3TC patent expires
int width = source->s(); int width = source->s();
int height = source->t(); int height = source->t();
@ -130,9 +133,13 @@ namespace
osg::ref_ptr<osg::Geometry> geom; osg::ref_ptr<osg::Geometry> geom;
#if defined(__APPLE__) #if defined(__APPLE__)
// Extra flip needed on Intel graphics OS X systems due to a driver bug // Extra flip needed on OS X systems due to a driver bug
const char* envval = getenv("OPENMW_CURSOR_WORKAROUND");
bool workaround = !envval || envval == std::string("1");
std::string vendorString = (const char*)glGetString(GL_VENDOR); std::string vendorString = (const char*)glGetString(GL_VENDOR);
if (vendorString.find("Intel") != std::string::npos) if (!envval)
workaround = vendorString.find("Intel") != std::string::npos || vendorString.find("ATI") != std::string::npos || vendorString.find("AMD") != std::string::npos;
if (workaround)
geom = osg::createTexturedQuadGeometry(osg::Vec3(-1,1,0), osg::Vec3(2,0,0), osg::Vec3(0,-2,0)); geom = osg::createTexturedQuadGeometry(osg::Vec3(-1,1,0), osg::Vec3(2,0,0), osg::Vec3(0,-2,0));
else else
#endif #endif

@ -1,6 +1,7 @@
[General] [General]
server = mp.tes3mp.com server = mp.tes3mp.com
port = 25565 port = 25565
password =
# 0 - Verbose (spam), 1 - Info, 2 - Warnings, 3 - Errors, 4 - Only fatal errors # 0 - Verbose (spam), 1 - Info, 2 - Warnings, 3 - Errors, 4 - Only fatal errors
loglevel = 0 loglevel = 0

@ -5,6 +5,7 @@ players = 64
hostname = My TES3MP server hostname = My TES3MP server
# 0 - Verbose (spam), 1 - Info, 2 - Warnings, 3 - Errors, 4 - Only fatal errors # 0 - Verbose (spam), 1 - Info, 2 - Warnings, 3 - Errors, 4 - Only fatal errors
loglevel = 1 loglevel = 1
password =
[Plugins] [Plugins]
#home = ~/local/openmw/tes3mp #home = ~/local/openmw/tes3mp

@ -1,13 +1,34 @@
0.5.0
-----
* Server browser
* Synchronization of containers
* Reworked world packets allowing for the saving and loading of world state, including container state
* Bandwidth optimization by forwarding the most frequent player packets only to other players in the same loaded cells
0.4.1
-----
* Packet for saving and loading spellbooks
0.4.0
-----
* Synchronization of spells
* Packet for saving and loading inventories
* Being in a menu no longer prevents you from sending packets about your client
* Fixes to freezes and problems caused by players moving around in cells from expansions/plugins that are not loaded by others
0.3.0 0.3.0
----- -----
* Fixed client freezes related to players logging in at the same time * Synchronization of world object removal, placement, scaling, locking and unlocking
* Fixed server crashes related to sending information about invalid players
* Synchronization of world object removal, placement and scaling
* Synchronization of local, global and member script variables for specific scripts * Synchronization of local, global and member script variables for specific scripts
* Synchronization for the setdelete, placeat, setscale, lock and unlock console commands * Synchronization for the setdelete, placeat, setscale, lock and unlock console commands
* Player markers on minimap * Player markers on minimap
* Death reasons in chat * Death reasons in chat
* Fixes to client freezes related to players logging in at the same time
* Fixes to server crashes related to sending information about invalid players
0.2.0 0.2.0
----- -----
@ -19,9 +40,9 @@
------ ------
* Synchronization of attributes and skills * Synchronization of attributes and skills
* Fixed memory leaks related to player initialization on Windows servers * Fixes to memory leaks related to player initialization on Windows servers
* Fixed various graphical glitches * Fixes to various graphical glitches
* Disabled main menu buttons for starting, saving and loading games * Main menu buttons for starting, saving and loading games are now disabled
0.0.1a 0.0.1a
------ ------
@ -36,6 +57,7 @@
0.0.1 0.0.1
----- -----
* Initial networking and packet architecture
* Synchronization of player character generation * Synchronization of player character generation
* Synchronization of player position * Synchronization of player position
* Synchronization of player attack states (unarmed, armed with a weapon, using a spell) * Synchronization of player attack states (unarmed, armed with a weapon, using a spell)

@ -4,20 +4,20 @@ tes3mp Credits
Programmers Programmers
----------- -----------
Stanislav Zhukov (Koncord) - The main loafer and Project Leader Stanislav Zhukov (Koncord) - Overall architecture, networking & scripting systems, player sync, server browser
David Cernat - The person pulling the strings in the shadows David Cernat - World sync & state saving/loading, player state saving/loading, general bug fixes
Script developers Script developers
----------------- -----------------
Grim Kriegor Grim Kriegor - Teleportation commands, various early script fixes
Testers Testers
------- -------
Volk Milit (Ja'Virr-Dar) - Team Manager, Debian Linux Volk Milit (Ja'Virr-Dar) - Team manager, Debian Linux
Shnatsel - Debian Linux Shnatsel - Debian Linux
Goodevil - Mint and Xubuntu Linux Goodevil - Mint and Xubuntu Linux
@ -25,8 +25,8 @@ Testers
Public Relations and Translations Public Relations and Translations
--------------------------------- ---------------------------------
Volk Milit (Ja'Virr-Dar) - Public relations & News Writer Volk Milit (Ja'Virr-Dar) - Public relations & news writer
Shnatsel - Translator & News Writer Shnatsel - Translator & news writer
Art Art

Loading…
Cancel
Save