Merge pull request #1 from TES3MP/master

tes3mp changes
This commit is contained in:
Battlerax 2017-02-22 14:54:33 -06:00 committed by GitHub
commit ba4b77f2c3
69 changed files with 866 additions and 227 deletions

View file

@ -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).

View file

@ -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();
}

View file

@ -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();

View file

@ -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);
}

View file

@ -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
};
};

View file

@ -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>

View file

@ -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));

View file

@ -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

223
apps/openmw-mp/Cell.cpp Normal file
View file

@ -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();
}

76
apps/openmw-mp/Cell.hpp Normal file
View file

@ -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

View file

@ -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();
}

View file

@ -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);

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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)

View file

@ -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;

View file

@ -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);
}

View file

@ -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,

View file

@ -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"))
{

View file

@ -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);

View file

@ -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);

View file

@ -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>());

View file

@ -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>();

View file

@ -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)
};
}

View file

@ -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

View file

@ -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;

View file

@ -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
{

View file

@ -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;
};
}

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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.

View file

@ -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());

View file

@ -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();

View file

@ -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);

View file

@ -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();
}
}

View file

@ -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:

View file

@ -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;

View file

@ -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();

View file

@ -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();

View file

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

View file

@ -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;

View file

@ -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();
}

View file

@ -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);

View file

@ -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));

View file

@ -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());
}
};

View file

@ -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());

View file

@ -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);

View file

@ -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);
}
};

View file

@ -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);

View file

@ -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();
}

View file

@ -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);

View file

@ -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().

View file

@ -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
}

View file

@ -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)

View file

@ -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);
};
}

View file

@ -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);

View file

@ -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) {}
}

View file

@ -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);

View file

@ -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;
}
};

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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