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)
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)
* 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)
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
--------------
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.
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.
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](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
---------------
@ -36,4 +38,4 @@ Getting Started
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;
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))
return qApp->quit();
}

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

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

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

@ -1757,6 +1757,41 @@ namespace CSMWorld
return true;
}
};
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>
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 BodyPartTypeColumn<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,
ESM::BodyPart::BPF_NotPlayable, ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, true));

@ -35,9 +35,10 @@ if(BUILD_WITH_PAWN)
endif(BUILD_WITH_PAWN)
option(BUILD_WITH_LUA "Enable Terra/Lua language" ON)
option(FORCE_LUA "Use Lua instead Terra" OFF)
if(BUILD_WITH_LUA)
#set(Terra_ROOT ${CMAKE_SOURCE_DIR}/external/terra/)
if(WIN32)
if(WIN32 OR FORCE_LUA)
find_package(Lua51 REQUIRED)
MESSAGE(STATUS "Found LUA_LIBRARY: ${LUA_LIBRARY}")
MESSAGE(STATUS "Found LUA_INCLUDE_DIR: ${LUA_INCLUDE_DIR}")
@ -69,6 +70,7 @@ set(SERVER
Networking.cpp
Utils.cpp
MasterClient.cpp
Cell.cpp
Script/Script.cpp Script/ScriptFunction.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 "MasterClient.hpp"
#include <components/openmw-mp/Log.hpp>
#include <components/openmw-mp/Version.hpp>
#include "Networking.hpp"
using namespace std;
@ -69,7 +70,9 @@ MasterClient::Send(std::string hostname, std::string modname, unsigned maxPlayer
sstr << "\"hostname\": \"" << hostname.c_str() << "\", ";
sstr << "\"modname\": \"" << modname.c_str() << "\", ";
sstr << "\"players\": " << players << ", ";
sstr << "\"max_players\": " << maxPlayers;
sstr << "\"max_players\": " << maxPlayers << ", ";
sstr << "\"version\": \"" << TES3MP_VERSION << "\", ";
sstr << "\"passw\": " << (mwmp::Networking::get().isPassworded() ? "true" : "false");
sstr << "}";
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"
" the server. Trying low rate...");
if((timeout - step_rate) >= step_rate)
if ((timeout - step_rate) >= step_rate)
SetUpdateRate(timeout - step_rate);
update = false;
}
@ -177,10 +180,10 @@ void MasterClient::Start()
void MasterClient::Stop()
{
if(!sRun)
if (!sRun)
return;
sRun = false;
if(thrQuery.joinable())
if (thrQuery.joinable())
thrQuery.join();
}

@ -15,6 +15,8 @@
#include "Networking.hpp"
#include "MasterClient.hpp"
#include "Cell.hpp"
#include <components/openmw-mp/Version.hpp>
using namespace mwmp;
using namespace std;
@ -29,6 +31,8 @@ Networking::Networking(RakNet::RakPeerInterface *peer)
this->peer = peer;
players = Players::getPlayers();
CellController::create();
playerController = new PlayerPacketController(peer);
worldController = new WorldPacketController(peer);
@ -40,17 +44,31 @@ Networking::Networking(RakNet::RakPeerInterface *peer)
exitCode = 0;
Script::Call<Script::CallbackIdentity("OnServerInit")>();
serverPassword = TES3MP_DEFAULT_PASSW;
}
Networking::~Networking()
{
Script::Call<Script::CallbackIdentity("OnServerExit")>(false);
CellController::destroy();
sThis = 0;
delete playerController;
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)
{
Player *player = Players::getPlayer(packet->guid);
@ -59,7 +77,6 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
if (packet->data[0] == ID_HANDSHAKE)
{
string passw = "SuperPassword";
myPacket->Read(player);
@ -72,7 +89,7 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
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)",
player->getId(),
@ -145,7 +162,10 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
if (!player->creatureStats.mDead)
{
myPacket->Read(player);
myPacket->Send(player, true); //send to other clients
//myPacket->Send(player, true); //send to other clients
player->sendToLoaded(myPacket);
}
break;
@ -162,7 +182,27 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
LOG_APPEND(Log::LOG_INFO, "- Moved to %s",
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
Script::Call<Script::CallbackIdentity("OnPlayerCellChange")>(player->getId());
}
else
@ -180,6 +220,8 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
myPacket->Read(player);
CellController::get()->update(player);
Script::Call<Script::CallbackIdentity("OnPlayerCellState")>(player->getId());
break;
@ -190,7 +232,9 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
if (!player->creatureStats.mDead)
{
myPacket->Read(player);
myPacket->Send(player, true);
//myPacket->Send(player, true);
player->sendToLoaded(myPacket);
Script::Call<Script::CallbackIdentity("OnPlayerAttributesChange")>(player->getId());
}
@ -203,7 +247,8 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
if (!player->creatureStats.mDead)
{
myPacket->Read(player);
myPacket->Send(player, true);
//myPacket->Send(player, true);
player->sendToLoaded(myPacket);
Script::Call<Script::CallbackIdentity("OnPlayerSkillsChange")>(player->getId());
}
@ -216,7 +261,7 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
if (!player->creatureStats.mDead)
{
myPacket->Read(player);
myPacket->Send(player, true);
//myPacket->Send(player, true);
Script::Call<Script::CallbackIdentity("OnPlayerLevelChange")>(player->getId());
}
@ -228,7 +273,9 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
DEBUG_PRINTF("ID_PLAYER_EQUIPMENT\n");
myPacket->Read(player);
myPacket->Send(player, true);
//myPacket->Send(player, true);
player->sendToLoaded(myPacket);
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);
}
break;
@ -301,7 +349,10 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
{
DEBUG_PRINTF("ID_PLAYER_DYNAMICSTATS\n");
myPacket->Read(player);
myPacket->Send(player, true);
//myPacket->Send(player, true);
player->sendToLoaded(myPacket);
break;
}
@ -352,7 +403,10 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
{
DEBUG_PRINTF("ID_PLAYER_DRAWSTATE\n");
myPacket->Read(player);
myPacket->Send(player, true);
//myPacket->Send(player, true);
player->sendToLoaded(myPacket);
break;
}
@ -558,7 +612,16 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
player->npc.mName.c_str());
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")>(
player->getId(),
@ -817,7 +880,7 @@ int Networking::mainLoop()
RakNet::BitStream bs;
bs.Write((unsigned char) ID_MASTER_QUERY);
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(0); // plugins
peer->Send(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false);

@ -40,11 +40,14 @@ namespace mwmp
MasterClient *getMasterClient();
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 Networking *getPtr();
private:
std::string serverPassword;
static Networking *sThis;
RakNet::RakPeerInterface *peer;
RakNet::BitStream bsOut;

@ -15,6 +15,8 @@ void Players::deletePlayer(RakNet::RakNetGUID guid)
if (players[guid] != 0)
{
CellController::get()->deletePlayer(players[guid]);
LOG_APPEND(Log::LOG_INFO, "- Emptying slot %i",
players[guid]->getId());
@ -52,7 +54,7 @@ void Players::newPlayer(RakNet::RakNetGUID guid)
Player *Players::getPlayer(RakNet::RakNetGUID guid)
{
if(players.count(guid) == 0)
if (players.count(guid) == 0)
return nullptr;
return players[guid];
}
@ -135,3 +137,44 @@ std::chrono::steady_clock::time_point Player::getLastAttackerTime()
{
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/Base/BasePlayer.hpp>
#include <components/openmw-mp/Packets/Player/PlayerPacket.hpp>
#include "Cell.hpp"
struct Player;
typedef std::map<RakNet::RakNetGUID, Player*> TPlayers;
@ -38,6 +40,7 @@ private:
class Player : public mwmp::BasePlayer
{
friend class Cell;
unsigned short id;
public:
@ -67,12 +70,18 @@ public:
virtual ~Player();
CellController::TContainer *getCells();
void sendToLoaded(mwmp::PlayerPacket *myPacket);
void forEachLoaded(std::function<void(Player *pl, Player *other)> func);
public:
mwmp::InventoryChanges inventoryChangesBuffer;
mwmp::SpellbookChanges spellbookChangesBuffer;
mwmp::JournalChanges journalChangesBuffer;
private:
CellController::TContainer cells;
bool handshakeState;
int loadState;
unsigned short lastAttacker;

@ -132,12 +132,6 @@ int WorldFunctions::GetContainerItemCharge(unsigned int objectIndex, unsigned in
.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
{
return mwmp::Networking::getPtr()->getLastEvent()->objectChanges.objects.at(objectIndex)

@ -26,7 +26,6 @@
{"GetContainerItemRefId", WorldFunctions::GetContainerItemRefId},\
{"GetContainerItemCount", WorldFunctions::GetContainerItemCount},\
{"GetContainerItemCharge", WorldFunctions::GetContainerItemCharge},\
{"GetContainerItemGoldValue", WorldFunctions::GetContainerItemGoldValue},\
{"GetContainerItemActionCount", WorldFunctions::GetContainerItemActionCount},\
\
{"SetBaseEventCell", WorldFunctions::SetBaseEventCell},\
@ -90,7 +89,6 @@ public:
static const char *GetContainerItemRefId(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 GetContainerItemGoldValue(unsigned int objectIndex, unsigned int itemIndex) noexcept;
static int GetContainerItemActionCount(unsigned int objectIndex, unsigned int itemIndex) 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);
}
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 void SetModname(const char* name) noexcept;
static void SetHostname(const char* name) noexcept;
static void SetServerPassword(const char *passw) noexcept;
static constexpr ScriptFunctionData functions[]{
{"CreateTimer", ScriptFunctions::CreateTimer},
@ -88,6 +89,7 @@ public:
{"GetAvgPing", ScriptFunctions::GetAvgPing},
{"SetModname", ScriptFunctions::SetModname},
{"SetHostname", ScriptFunctions::SetHostname},
{"SetServerPassword", ScriptFunctions::SetServerPassword},
POSITIONAPI,
CELLAPI,

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

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

@ -84,7 +84,6 @@ namespace OMW
osg::ref_ptr<osgViewer::ScreenCaptureHandler> mScreenCaptureHandler;
std::string mCellName;
std::vector<std::string> mContentFiles;
bool mVerboseScripts;
bool mSkipMenu;
bool mUseSound;
bool mCompileAll;
@ -158,9 +157,6 @@ namespace OMW
*/
void addContentFile(const std::string& file);
/// Enable or disable verbose script output
void setScriptsVerbosity(bool scriptsVerbosity);
/// Disable or enable all sounds
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)
->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)
->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
engine.setCompileAll(variables["script-all"].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.setStartupScript (variables["script-run"].as<Files::EscapeHashString>().toStdString());
engine.setWarningsMode (variables["script-warn"].as<int>());

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

@ -39,6 +39,9 @@ namespace MWClass
static void registerSelf();
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())
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

@ -26,6 +26,8 @@ namespace MWClass
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 osg::Vec3f getRotationVector(const MWWorld::Ptr& ptr) const;

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

@ -70,6 +70,8 @@ namespace MWClass
virtual void restock (const MWWorld::Ptr &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().getMechanicsManager()->add(ptr);
bool Door::useAnim() const
{
return true;
}
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 bool useAnim() 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);
/// 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::SoundManager::Play_TypeSfx,
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

@ -16,6 +16,8 @@ namespace MWClass
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;
///< \return name (the one that is to be presented to the user; not the internal one);
/// can return an empty string.

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

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

@ -116,7 +116,6 @@ namespace MWGui
containerItem.refId =itemPtr.getCellRef().getRefId();
containerItem.count = itemPtr.getRefData().getCount();
containerItem.charge = itemPtr.getCellRef().getCharge();
containerItem.goldValue = itemPtr.getCellRef().getGoldValue();
containerItem.actionCount = count;
worldObject.containerChanges.items.push_back(containerItem);
@ -176,9 +175,8 @@ namespace MWGui
// Make sure we get the drag and drop count, not the count of the original item
containerItem.count = mDragAndDrop->mDraggedCount;
containerItem.charge = itemPtr.getCellRef().getCharge();
containerItem.goldValue = itemPtr.getCellRef().getGoldValue();
worldObject.containerChanges.items.push_back(containerItem);
event->addObject(worldObject);

@ -1189,4 +1189,40 @@ namespace MWMechanics
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?
bool effectTick(CreatureStats& creatureStats, const MWWorld::Ptr& actor, const MWMechanics::EffectKey& effectKey, float magnitude);
std::string getSummonedCreature(int effectId);
class CastSpell
{
private:

@ -40,32 +40,7 @@ namespace MWMechanics
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);
@ -89,10 +64,7 @@ namespace MWMechanics
bool found = creatureMap.find(std::make_pair(it->first, it->second)) != creatureMap.end();
if (!found)
{
const std::string& creatureGmst = summonMap[it->first];
std::string creatureID =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(creatureGmst)->getString();
std::string creatureID = getSummonedCreature(it->first);
if (!creatureID.empty())
{
int creatureActorId = -1;

@ -798,6 +798,13 @@ void LocalPlayer::setCell()
MWWorld::Ptr player = world->getPlayerPtr();
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);
int x = cell.mData.mX;
@ -1057,7 +1064,9 @@ void LocalPlayer::sendJournalEntry(const std::string& quest, int index, const MW
journalItem.quest = quest;
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.mRefNum = actor.getCellRef().getRefNum();

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

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

@ -189,7 +189,6 @@ void Networking::processPlayerPacket(RakNet::Packet *packet)
{
case ID_HANDSHAKE:
{
getLocalPlayer()->passw = "SuperPassword";
myPacket->Send(getLocalPlayer(), serverAddr);
break;
}
@ -746,7 +745,9 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
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 (event->action == mwmp::BaseEvent::REQUEST)
@ -763,7 +764,8 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
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);
break;
@ -774,7 +776,8 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
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);
break;
@ -785,7 +788,8 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
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);
break;
@ -796,7 +800,8 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
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);
break;
@ -807,7 +812,8 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
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);
break;
@ -818,7 +824,8 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
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);
break;
@ -829,7 +836,8 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
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);
break;
@ -840,7 +848,8 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
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);
break;
@ -851,7 +860,8 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
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);
break;
@ -862,7 +872,8 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
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);
break;
@ -873,7 +884,8 @@ void Networking::processWorldPacket(RakNet::Packet *packet)
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);
break;

@ -70,15 +70,20 @@ void mwmp::WorldController::closeContainer(const MWWorld::Ptr &container)
{
mwmp::Main::get().getLocalPlayer()->clearCurrentContainer();
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Container \"%s\" (%d) is closed.",
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++)
// If the player died while in a container, the container's Ptr could be invalid now
if (!container.isEmpty())
{
LOG_APPEND(Log::LOG_VERBOSE, " - Item. Refid: \"%s\" Count: %d",
iter->getCellRef().getRefId().c_str(), iter->getRefData().getCount());
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Container \"%s\" (%d) is closed.",
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();
}

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

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

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

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

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

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

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

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

@ -271,6 +271,9 @@ namespace MWWorld
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;
///< 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.getClass().getEffectiveArmorRating(old, actor) >= test.getClass().getEffectiveArmorRating(test, actor))
// old armor had better armor rating
if (old.get<ESM::Armor>()->mBase->mData.mType < test.get<ESM::Armor>()->mBase->mData.mType)
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
}

@ -56,15 +56,23 @@ namespace
void addObject(const MWWorld::Ptr& ptr, MWPhysics::PhysicsSystem& physics,
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();
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
ptr.getClass().insertObjectRendering(ptr, model, rendering);
setNodeRotation(ptr, rendering, false);
ptr.getClass().insertObject (ptr, model, physics);
if (useAnim)
MWBase::Environment::get().getMechanicsManager()->add(ptr);
if (ptr.getClass().isActor())
rendering.addWaterRippleEmitter(ptr);
}
@ -303,7 +311,11 @@ namespace MWWorld
// ... then references. This is important for adjustPosition to work correctly.
/// \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);
bool waterEnabled = cell->getCell()->hasWater() || cell->isExterior();
@ -703,10 +715,14 @@ namespace MWWorld
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()))
mRendering.getWorkQueue()->addWorkItem(new PreloadMeshItem(mesh, mRendering.getResourceSystem()->getSceneManager()));
std::string mesh_ = mesh;
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)

@ -133,7 +133,7 @@ namespace MWWorld
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())
return;
MWWorld::ManualRef ref(store, obj);
std::string model = ref.getPtr().getClass().getModel(ref.getPtr());
if (!model.empty())
scene->preload(model);
try
{
MWWorld::ManualRef ref(store, obj);
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)
@ -3493,6 +3499,12 @@ namespace MWWorld
{
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->mHit);

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

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

@ -12,13 +12,12 @@ namespace mwmp
std::string refId;
int count;
int charge;
int goldValue;
int actionCount;
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.count, send);
RW(containerItem.charge, send);
RW(containerItem.goldValue, send);
RW(containerItem.actionCount, send);
if (!send)

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

@ -374,7 +374,7 @@ namespace SceneUtil
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)

@ -3,6 +3,7 @@
#include <cassert>
#include <stdexcept>
#include <iostream>
#include <cstdlib>
#include <SDL_mouse.h>
#include <SDL_endian.h>
@ -81,6 +82,8 @@ namespace
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 height = source->t();
@ -130,9 +133,13 @@ namespace
osg::ref_ptr<osg::Geometry> geom;
#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);
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));
else
#endif

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

@ -5,6 +5,7 @@ players = 64
hostname = My TES3MP server
# 0 - Verbose (spam), 1 - Info, 2 - Warnings, 3 - Errors, 4 - Only fatal errors
loglevel = 1
password =
[Plugins]
#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
-----
* Fixed client freezes related to players logging in at the same time
* Fixed server crashes related to sending information about invalid players
* Synchronization of world object removal, placement and scaling
* Synchronization of world object removal, placement, scaling, locking and unlocking
* Synchronization of local, global and member script variables for specific scripts
* Synchronization for the setdelete, placeat, setscale, lock and unlock console commands
* Player markers on minimap
* 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
-----
@ -19,9 +40,9 @@
------
* Synchronization of attributes and skills
* Fixed memory leaks related to player initialization on Windows servers
* Fixed various graphical glitches
* Disabled main menu buttons for starting, saving and loading games
* Fixes to memory leaks related to player initialization on Windows servers
* Fixes to various graphical glitches
* Main menu buttons for starting, saving and loading games are now disabled
0.0.1a
------
@ -36,6 +57,7 @@
0.0.1
-----
* Initial networking and packet architecture
* Synchronization of player character generation
* Synchronization of player position
* Synchronization of player attack states (unarmed, armed with a weapon, using a spell)

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

Loading…
Cancel
Save