mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-19 12:40:49 +00:00
Create MWWorld::SafePtr
This commit is contained in:
parent
cf0ba158f6
commit
0b385d5db9
@ -198,7 +198,7 @@ namespace MWLua
|
||||
}
|
||||
|
||||
LocalScripts::LocalScripts(LuaUtil::LuaState* lua, const LObject& obj)
|
||||
: LuaUtil::ScriptsContainer(lua, "L" + idToString(obj.id()))
|
||||
: LuaUtil::ScriptsContainer(lua, "L" + obj.id().toString())
|
||||
, mData(obj)
|
||||
{
|
||||
this->addPackage("openmw.self", sol::make_object(lua->sol(), &mData));
|
||||
|
@ -187,11 +187,12 @@ namespace MWLua
|
||||
for (LocalEvent& e : localEvents)
|
||||
{
|
||||
LObject obj(e.mDest);
|
||||
LocalScripts* scripts = obj.isValid() ? obj.ptr().getRefData().getLuaScripts() : nullptr;
|
||||
const MWWorld::Ptr& ptr = obj.ptrOrNull();
|
||||
LocalScripts* scripts = ptr.isEmpty() ? nullptr : ptr.getRefData().getLuaScripts();
|
||||
if (scripts)
|
||||
scripts->receiveEvent(e.mEventName, e.mEventData);
|
||||
else
|
||||
Log(Debug::Debug) << "Ignored event " << e.mEventName << " to L" << idToString(e.mDest)
|
||||
Log(Debug::Debug) << "Ignored event " << e.mEventName << " to L" << e.mDest.toString()
|
||||
<< ". Object not found or has no attached scripts";
|
||||
}
|
||||
|
||||
@ -204,14 +205,15 @@ namespace MWLua
|
||||
for (const LocalEngineEvent& e : mLocalEngineEvents)
|
||||
{
|
||||
LObject obj(e.mDest);
|
||||
if (!obj.isValid())
|
||||
const MWWorld::Ptr& ptr = obj.ptrOrNull();
|
||||
if (ptr.isEmpty())
|
||||
{
|
||||
if (luaDebug)
|
||||
Log(Debug::Verbose) << "Can not call engine handlers: object" << idToString(e.mDest)
|
||||
Log(Debug::Verbose) << "Can not call engine handlers: object" << e.mDest.toString()
|
||||
<< " is not found";
|
||||
continue;
|
||||
}
|
||||
LocalScripts* scripts = obj.ptr().getRefData().getLuaScripts();
|
||||
LocalScripts* scripts = ptr.getRefData().getLuaScripts();
|
||||
if (scripts)
|
||||
scripts->receiveEngineEvent(e.mEvent);
|
||||
}
|
||||
@ -238,17 +240,18 @@ namespace MWLua
|
||||
for (ObjectId id : mObjectAddedEvents)
|
||||
{
|
||||
GObject obj(id);
|
||||
if (obj.isValid())
|
||||
const MWWorld::Ptr& ptr = obj.ptrOrNull();
|
||||
if (!ptr.isEmpty())
|
||||
{
|
||||
mGlobalScripts.objectActive(obj);
|
||||
const MWWorld::Class& objClass = obj.ptr().getClass();
|
||||
const MWWorld::Class& objClass = ptr.getClass();
|
||||
if (objClass.isActor())
|
||||
mGlobalScripts.actorActive(obj);
|
||||
if (mWorldView.isItem(obj.ptr()))
|
||||
if (mWorldView.isItem(ptr))
|
||||
mGlobalScripts.itemActive(obj);
|
||||
}
|
||||
else if (luaDebug)
|
||||
Log(Debug::Verbose) << "Could not resolve a Lua object added event: object" << idToString(id)
|
||||
Log(Debug::Verbose) << "Could not resolve a Lua object added event: object" << id.toString()
|
||||
<< " is already removed";
|
||||
}
|
||||
mObjectAddedEvents.clear();
|
||||
@ -675,7 +678,7 @@ namespace MWLua
|
||||
selectedScripts = selectedPtr.getRefData().getLuaScripts();
|
||||
if (selectedScripts)
|
||||
selectedScripts->collectStats(selectedStats);
|
||||
out << "Profiled object (selected in the in-game console): " << ptrToString(selectedPtr) << "\n";
|
||||
out << "Profiled object (selected in the in-game console): " << selectedPtr.toString() << "\n";
|
||||
}
|
||||
else
|
||||
out << "No selected object. Use the in-game console to select an object for detailed profile.\n";
|
||||
|
@ -1,59 +0,0 @@
|
||||
#include "object.hpp"
|
||||
|
||||
#include "types/types.hpp"
|
||||
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
std::string idToString(const ObjectId& id)
|
||||
{
|
||||
return std::to_string(id.mIndex) + "_" + std::to_string(id.mContentFile);
|
||||
}
|
||||
|
||||
bool isMarker(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
return Misc::ResourceHelpers::isHiddenMarker(ptr.getCellRef().getRefId());
|
||||
}
|
||||
|
||||
std::string ptrToString(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
std::string res = "object";
|
||||
if (ptr.getRefData().isDeleted())
|
||||
res = "deleted object";
|
||||
res.append(idToString(getId(ptr)));
|
||||
res.append(" (");
|
||||
res.append(getLuaObjectTypeName(ptr));
|
||||
res.append(", ");
|
||||
res.append(ptr.getCellRef().getRefId().getRefIdString());
|
||||
res.append(")");
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string Object::toString() const
|
||||
{
|
||||
if (isValid())
|
||||
return ptrToString(ptr());
|
||||
else
|
||||
return "object" + idToString(mId) + " (not found)";
|
||||
}
|
||||
|
||||
bool Object::isValid() const
|
||||
{
|
||||
MWWorld::WorldModel& w = *MWBase::Environment::get().getWorldModel();
|
||||
if (mLastUpdate < w.getPtrIndexUpdateCounter())
|
||||
{
|
||||
mPtr = w.getPtr(mId);
|
||||
mLastUpdate = w.getPtrIndexUpdateCounter();
|
||||
}
|
||||
return !mPtr.isEmpty();
|
||||
}
|
||||
|
||||
const MWWorld::Ptr& Object::ptr() const
|
||||
{
|
||||
if (!isValid())
|
||||
throw std::runtime_error("Object is not available: " + idToString(mId));
|
||||
return mPtr;
|
||||
}
|
||||
}
|
@ -21,39 +21,24 @@ namespace MWLua
|
||||
{
|
||||
return ptr.getCellRef().getRefNum();
|
||||
}
|
||||
std::string idToString(const ObjectId& id);
|
||||
std::string ptrToString(const MWWorld::Ptr& ptr);
|
||||
bool isMarker(const MWWorld::Ptr& ptr);
|
||||
|
||||
// Lua scripts can't use MWWorld::Ptr directly, because lifetime of a script can be longer than lifetime of Ptr.
|
||||
// `GObject` and `LObject` are intended to be passed to Lua as a userdata.
|
||||
// It automatically updates the underlying Ptr when needed.
|
||||
class Object
|
||||
class Object : public MWWorld::SafePtr
|
||||
{
|
||||
public:
|
||||
Object(ObjectId id)
|
||||
: mId(id)
|
||||
{
|
||||
}
|
||||
virtual ~Object() {}
|
||||
ObjectId id() const { return mId; }
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
// Updates and returns the underlying Ptr. Throws an exception if object is not available.
|
||||
const MWWorld::Ptr& ptr() const;
|
||||
|
||||
// Returns `true` if calling `ptr()` is safe.
|
||||
bool isValid() const;
|
||||
|
||||
virtual sol::object getObject(lua_State* lua, ObjectId id) const = 0; // returns LObject or GOBject
|
||||
using SafePtr::SafePtr;
|
||||
virtual sol::object getObject(lua_State* lua, ObjectId id) const = 0; // returns LObject or GObject
|
||||
virtual sol::object getCell(lua_State* lua, MWWorld::CellStore* store) const = 0; // returns LCell or GCell
|
||||
|
||||
protected:
|
||||
const ObjectId mId;
|
||||
|
||||
mutable MWWorld::Ptr mPtr;
|
||||
mutable size_t mLastUpdate = 0;
|
||||
const MWWorld::Ptr& ptr() const
|
||||
{
|
||||
const MWWorld::Ptr& res = ptrOrNull();
|
||||
if (res.isEmpty())
|
||||
throw std::runtime_error("Object is not available: " + id().toString());
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
// Used only in local scripts
|
||||
@ -100,7 +85,6 @@ namespace MWLua
|
||||
{
|
||||
Obj mObj;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // MWLUA_OBJECT_H
|
||||
|
@ -116,10 +116,10 @@ namespace MWLua
|
||||
{
|
||||
MWWorld::Ptr object = MWBase::Environment::get().getWorldModel()->getPtr(mObject);
|
||||
if (object.isEmpty())
|
||||
throw std::runtime_error(std::string("Object not found: " + idToString(mObject)));
|
||||
throw std::runtime_error(std::string("Object not found: " + mObject.toString()));
|
||||
MWWorld::Ptr actor = MWBase::Environment::get().getWorldModel()->getPtr(mActor);
|
||||
if (actor.isEmpty())
|
||||
throw std::runtime_error(std::string("Actor not found: " + idToString(mActor)));
|
||||
throw std::runtime_error(std::string("Actor not found: " + mActor.toString()));
|
||||
|
||||
if (object.getRefData().activate())
|
||||
{
|
||||
@ -131,8 +131,8 @@ namespace MWLua
|
||||
|
||||
std::string toString() const override
|
||||
{
|
||||
return std::string("ActivateAction object=") + idToString(mObject) + std::string(" actor=")
|
||||
+ idToString(mActor);
|
||||
return std::string("ActivateAction object=") + mObject.toString() + std::string(" actor=")
|
||||
+ mActor.toString();
|
||||
}
|
||||
|
||||
private:
|
||||
@ -165,7 +165,7 @@ namespace MWLua
|
||||
template <class ObjectT>
|
||||
void addBasicBindings(sol::usertype<ObjectT>& objectT, const Context& context)
|
||||
{
|
||||
objectT["isValid"] = [](const ObjectT& o) { return o.isValid(); };
|
||||
objectT["isValid"] = [](const ObjectT& o) { return !o.ptrOrNull().isEmpty(); };
|
||||
objectT["recordId"] = sol::readonly_property(
|
||||
[](const ObjectT& o) -> std::string { return o.ptr().getCellRef().getRefId().getRefIdString(); });
|
||||
objectT["cell"] = sol::readonly_property([](const ObjectT& o) -> sol::optional<Cell<ObjectT>> {
|
||||
@ -197,7 +197,7 @@ namespace MWLua
|
||||
if (esmRecordType != ESM::REC_CREA && esmRecordType != ESM::REC_NPC_)
|
||||
throw std::runtime_error(
|
||||
"The argument of `activateBy` must be an actor who activates the object. Got: "
|
||||
+ ptrToString(actor.ptr()));
|
||||
+ actor.toString());
|
||||
context.mLuaManager->addAction(std::make_unique<ActivateAction>(context.mLua, o.id(), actor.id()));
|
||||
};
|
||||
|
||||
@ -266,7 +266,7 @@ namespace MWLua
|
||||
MWWorld::Ptr ptr = object.ptr();
|
||||
LocalScripts* localScripts = ptr.getRefData().getLuaScripts();
|
||||
if (!localScripts || !localScripts->hasScript(*scriptId))
|
||||
throw std::runtime_error("There is no script " + std::string(path) + " on " + ptrToString(ptr));
|
||||
throw std::runtime_error("There is no script " + std::string(path) + " on " + ptr.toString());
|
||||
if (localScripts->getAutoStartConf().count(*scriptId) > 0)
|
||||
throw std::runtime_error("Autostarted script can not be removed: " + std::string(path));
|
||||
localScripts->removeScript(*scriptId);
|
||||
|
@ -51,7 +51,7 @@ namespace MWLua
|
||||
|| itemPtr.getContainerStore() != static_cast<const MWWorld::ContainerStore*>(&store))
|
||||
{
|
||||
Log(Debug::Warning)
|
||||
<< "Object" << idToString(std::get<ObjectId>(item)) << " is not in inventory";
|
||||
<< "Object" << std::get<ObjectId>(item).toString() << " is not in inventory";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -77,7 +77,7 @@ namespace MWLua
|
||||
allowedSlots.begin(), allowedSlots.end(), [&](int s) { return !usedSlots[s]; });
|
||||
if (firstAllowed == allowedSlots.end())
|
||||
{
|
||||
Log(Debug::Warning) << "No suitable slot for " << ptrToString(itemPtr);
|
||||
Log(Debug::Warning) << "No suitable slot for " << itemPtr.toString();
|
||||
return false;
|
||||
}
|
||||
slot = *firstAllowed;
|
||||
@ -261,7 +261,7 @@ namespace MWLua
|
||||
if (!obj.ptr().getClass().hasInventoryStore(obj.ptr()))
|
||||
{
|
||||
if (!equipment.empty())
|
||||
throw std::runtime_error(ptrToString(obj.ptr()) + " has no equipment slots");
|
||||
throw std::runtime_error(obj.toString() + " has no equipment slots");
|
||||
return;
|
||||
}
|
||||
SetEquipmentAction::Equipment eqp;
|
||||
|
@ -95,7 +95,7 @@ namespace MWLua
|
||||
std::string msg = "Requires type '";
|
||||
msg.append(getLuaObjectTypeName(recordType));
|
||||
msg.append("', but applied to ");
|
||||
msg.append(ptrToString(ptr));
|
||||
msg.append(ptr.toString());
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
return ptr;
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include <components/esm3/esmwriter.hpp>
|
||||
#include <components/esm3/loadcell.hpp>
|
||||
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include "../mwclass/container.hpp"
|
||||
@ -39,7 +41,7 @@ namespace MWLua
|
||||
{
|
||||
// It is important to check `isMarker` first.
|
||||
// For example "prisonmarker" has class "Door" despite that it is only an invisible marker.
|
||||
if (isMarker(ptr))
|
||||
if (Misc::ResourceHelpers::isHiddenMarker(ptr.getCellRef().getRefId()))
|
||||
return nullptr;
|
||||
const MWWorld::Class& cls = ptr.getClass();
|
||||
if (cls.isActivator())
|
||||
|
42
apps/openmw/mwworld/ptr.cpp
Normal file
42
apps/openmw/mwworld/ptr.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
#include "ptr.hpp"
|
||||
|
||||
#include "apps/openmw/mwbase/environment.hpp"
|
||||
|
||||
#include "worldmodel.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
|
||||
std::string Ptr::toString() const
|
||||
{
|
||||
std::string res = "object";
|
||||
if (getRefData().isDeleted())
|
||||
res = "deleted object";
|
||||
res.append(getCellRef().getRefNum().toString());
|
||||
res.append(" (");
|
||||
res.append(getTypeDescription());
|
||||
res.append(", ");
|
||||
res.append(getCellRef().getRefId().getRefIdString());
|
||||
res.append(")");
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string SafePtr::toString() const
|
||||
{
|
||||
update();
|
||||
if (mPtr.isEmpty())
|
||||
return "object" + mId.toString() + " (not found)";
|
||||
else
|
||||
return mPtr.toString();
|
||||
}
|
||||
|
||||
void SafePtr::update() const
|
||||
{
|
||||
WorldModel& w = *MWBase::Environment::get().getWorldModel();
|
||||
if (mLastUpdate < w.getPtrIndexUpdateCounter())
|
||||
{
|
||||
mPtr = w.getPtr(mId);
|
||||
mLastUpdate = w.getPtrIndexUpdateCounter();
|
||||
}
|
||||
}
|
||||
}
|
@ -128,6 +128,8 @@ namespace MWWorld
|
||||
: PtrBase(liveCellRef, cell, nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
std::string toString() const;
|
||||
};
|
||||
|
||||
/// @note The difference between Ptr and ConstPtr is that the second one adds const to the underlying pointers.
|
||||
@ -145,6 +147,41 @@ namespace MWWorld
|
||||
}
|
||||
};
|
||||
|
||||
// SafePtr holds Ptr and automatically updates it via WorldModel if the Ptr becomes invalid.
|
||||
// Uses ESM::RefNum as an unique id. Can not be used for Ptrs without RefNum.
|
||||
// Note: WorldModel automatically assignes RefNum to all registered Ptrs.
|
||||
class SafePtr
|
||||
{
|
||||
public:
|
||||
using Id = ESM::RefNum;
|
||||
|
||||
explicit SafePtr(Id id)
|
||||
: mId(id)
|
||||
{
|
||||
}
|
||||
explicit SafePtr(const Ptr& ptr)
|
||||
: SafePtr(ptr.getCellRef().getRefNum())
|
||||
{
|
||||
}
|
||||
virtual ~SafePtr() = default;
|
||||
Id id() const { return mId; }
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
const Ptr& ptrOrNull() const
|
||||
{
|
||||
update();
|
||||
return mPtr;
|
||||
}
|
||||
|
||||
private:
|
||||
const Id mId;
|
||||
|
||||
mutable Ptr mPtr;
|
||||
mutable size_t mLastUpdate = 0;
|
||||
|
||||
void update() const;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -175,6 +175,11 @@ namespace ESM
|
||||
}
|
||||
}
|
||||
|
||||
std::string RefNum::toString() const
|
||||
{
|
||||
return std::to_string(mIndex) + "_" + std::to_string(mContentFile);
|
||||
}
|
||||
|
||||
void CellRef::load(ESMReader& esm, bool& isDeleted, bool wideRefNum)
|
||||
{
|
||||
loadId(esm, wideRefNum);
|
||||
|
@ -27,6 +27,8 @@ namespace ESM
|
||||
inline bool hasContentFile() const { return mContentFile >= 0; }
|
||||
|
||||
inline bool isSet() const { return mIndex != 0 || mContentFile != -1; }
|
||||
|
||||
std::string toString() const;
|
||||
};
|
||||
|
||||
/* Cell reference. This represents ONE object (of many) inside the
|
||||
|
Loading…
x
Reference in New Issue
Block a user